Why deps.ts and mod.ts is BAD in Deno
WJH

WJH @wongjiahau

Joined:
Apr 30, 2018

Why deps.ts and mod.ts is BAD in Deno

Publish Date: Jun 9 '20
49 16

If you read the official Deno manual, you'll notice that the author advices you to contain all dependecies into one file.

Similarly, if you're creating a library, the de-facto standard is to export all modules in a single mod.ts file.

Somehow, this convention is suppose to make the development experiences better.

But instead, let me tell you why this convention is doing the opposite, which is making the development experience worse by increasing compile time (around 200% based on my simple benchmark).

If you want to look at benchmarks, check it out at this repository.

Imagine we have a deps.ts:

export {A} from 'https://deno.land/x/a/mod.ts'
export {B} from 'https://deno.land/x/b/mod.ts'
Enter fullscreen mode Exit fullscreen mode

And a main.ts file:

import {A} from './deps.ts'
Enter fullscreen mode Exit fullscreen mode

By running deno run main.ts, Deno will actually also resolve dependencies of B and typecheck thems even if main.ts don't really need them, imagine B also imports from its own deps.ts, and so on and so forth.

Ultimately, most of the compile time is wasted on compiling unused imports.

Moreover, if you try to bundle it by doing deno bundle main.ts, you'll notice that the bundle output also contain B and its dependencies because Deno do not support tree shaking.

So how can this problem be solved? Easy, just use direct URL imports! By doing so, you will have improved compile time and improved bundle size.

// main.ts
import {A} from 'https://deno.land/x/a/mod.ts'
Enter fullscreen mode Exit fullscreen mode

These are all the problems we have when we are using npm.

You can find Ryan Dhal, the creator of Deno talking about this problem here.
Do you remember that when you tries to import a Lodash function into your code and suddenly the bundle size got so huge?

So how the Lodash community solves this? They do it by publishing a package for each function.

Therefore if we want to avoid:

  • unnecessary long compile time
  • unnecessary huge bundle size
  • Deno packages collapsing into a black hole.

The whole Deno community needs to stop placing all dependencies/export into one deps.ts/mod.ts file.

If we continue to use deps.ts and mod.ts, we will hinder the development experience of Deno developers by wasting their precious time waiting to compile unused dependencies.

Here's what I propose for managing dependancies, so instead of putting every dependencies in single deps.ts like this

// deps.ts
export {A} from 'https://deno.land/x/a/mod.ts'
export {B} from 'https://deno.land/x/b/mod.ts'
Enter fullscreen mode Exit fullscreen mode

Export each of them as separate files under the deps folder.

// deps/a.ts
export {A} from 'https://deno.land/x/a/mod.ts'
Enter fullscreen mode Exit fullscreen mode
// deps/b.ts
export {B} from 'https://deno.land/x/b/mod.ts'
Enter fullscreen mode Exit fullscreen mode

Ok, without mod.ts how do we allow library users to discover API easily?
Simple, instead of placing all export into a single mod.ts, split them into separate files under the mod folder.

For example, instead of having:

// mod.ts
export {A} from './a.ts'
export {B} from './b.ts'
Enter fullscreen mode Exit fullscreen mode

Do:

// mod/a.ts
export {A} from '../src/a.ts'
Enter fullscreen mode Exit fullscreen mode
// mod/b.ts
export {B} from '../src/b.ts'
Enter fullscreen mode Exit fullscreen mode

Last but not least, if the whole Deno community ditch this deps.ts/mod.ts practice, Deno by itself might not even need to implement tree-shaking and skip-unused-import features (which is tremendously difficult to implement).

It's like how social distancing prevented the spread of Corona virus instead of relying on vaccine.

Please if you think this article make sense, and for the greater good of Deno's future, please share it with your fellow Deno developers.

Comments 16 total

  • Tobias SN
    Tobias SNJun 9, 2020

    I mean, if you have a dependency, I think there’s a pretty high chance you’re gonna use it somewhere, so it’ll be compiled anyway.

    • WJH
      WJHJun 9, 2020

      So are you saying if I import A from X, I'm going to use B, C, D, E, F, G, H anyway? If that is true, why would the lodash community wanted modularize lodash packages?

      • Tobias SN
        Tobias SNJun 9, 2020

        No, I’m saying if you have B, C, D, E, F, G and H in there you’re gonna use them anyway.

        • WJH
          WJHJun 11, 2020

          I don't disagree with that, but you have to know that this problem applies to every level of dependencies, so even if you're gonna use everything module, you cannot make sure that the author of the library that you're importing are going to utilize every functions that imported from the mod.ts of another library.
          Eventually this creates a snowball effect, little by little, all those unused imports will take up a significant portion if not most of the compile time.

          • Tobias SN
            Tobias SNJun 11, 2020

            I’m talking about modules, not functions. You are right that you can’t be sure that a dependency will use every function of all its dependency, but in most cases it won’t matter, as all the functions will be compiled no matter how many you use.

  • oganexon
    oganexonJun 9, 2020

    At first I wasn't convinced
    But the idea of separating the dependencies while keeping the possibility to update them without doing it for each file is well thought-out

    This pattern should be proposed to more people, in the end it doesn't bring any disadvantages.

    EDIT: this is very time consuming, not recommended.

    • Ari Kalfus
      Ari KalfusJun 10, 2020

      Anyone else wondering what happened between the end of this comment and the edit. What did this person see.

    • WJH
      WJHJun 11, 2020

      Hi @oganexon , thanks for the reply. But I have a question for you, isn't longer compile time time-consuming too?

      • oganexon
        oganexonJun 11, 2020

        I meant to divide each dep in it's own file.
        My module only does one thing and the cli has its own dep in the file so I already divided the deps
        So overall I support your idea to provide a mod folder but not a deps folder

        • WJH
          WJHJun 11, 2020

          True, I might have taken that way too far lol

  • Ari Kalfus
    Ari KalfusJun 9, 2020

    You mention some compile time benchmarks you ran. I'd be interested if you'd add some examples to your article to highlight your point.

  • Shawn Bullock
    Shawn BullockJun 9, 2020

    Sometimes it might make sense. If your project is a bunch of functions that may or may not be used, this totally makes sense. But if you're project is something like Oak, then a fair amount of it will be wanted, and maybe other parts will be added on, and those dependencies can be isolated if needed.

    Or if your project is an enterprise app, like mine, then you most likely want most, if not all, of the dependencies.

    Compile time isn't a big deal (to me). It's a one time thing for each new launch. And such a decision imposes architecture which may or may not be what I'm looking for.

    The compromise I've made in my public Deno libraries is to allow each major feature to be independently loaded, but also have a mod.ts that loads them all if someone so chooses. I tend to choose it.

    • WJH
      WJHJun 11, 2020

      Compile time isn't a big deal

      It's might be true for backend, but for frontend development, a fast feedback loop is very crucial, especially most of frontend developer's time is spent on styling and layout rather than logic.

  • zorbyte
    zorbyteJun 12, 2020

    The removal of deps.ts and mod.ts works well with a module alias file. I use module aliases for my dependencies and for folders in my projects (e.g. if I had a utils folder with a logger in it I would configure an alias to use @utils/logger.ts)

Add comment