DEV Community

Cover image for Why deps.ts and mod.ts is BAD in Deno
WJH
WJH

Posted on

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

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.

Top comments (16)

Collapse
 
zorbyte profile image
zorbyte

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)

Collapse
 
tobiassn profile image
Tobias SN

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.

Collapse
 
wongjiahau profile image
WJH

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?

Collapse
 
tobiassn profile image
Tobias SN

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

Thread Thread
 
wongjiahau profile image
WJH

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.

Thread Thread
 
tobiassn profile image
Tobias SN

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.

Collapse
 
oganexon profile image
oganexon • Edited

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.

Collapse
 
artis3n profile image
Ari Kalfus

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

Collapse
 
wongjiahau profile image
WJH

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

Collapse
 
oganexon profile image
oganexon

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

Thread Thread
 
wongjiahau profile image
WJH

True, I might have taken that way too far lol

Collapse
 
ievolved profile image
Shawn Bullock • Edited

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.

Collapse
 
wongjiahau profile image
WJH

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.

Collapse
 
artis3n profile image
Ari Kalfus

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.

Collapse
 
wongjiahau profile image
WJH

Will the example provided in this repository helps? github.com/wongjiahau/deno-mod-ben...

Collapse
 
artis3n profile image
Ari Kalfus

Yup! Thanks