read

After a few years of loyalty to QtCreator as C++ IDE, I decided to give a try to Visual Studio Code. Very positively surprised by its simplicity of usage, its built-in features and the quality of extensions, I thought about going one step further and write an extension.

In the Hello World extension example, it is advised to generate the extension skeleton through a Node.js generator:

We have written a Yeoman generator to help get you started. Install Yeoman and the Yeoman VS Code Extension generator and scaffold a new extension:

npm install -g yo generator-code yo code

The thing is, running this command took almost as long as the installation of my entire Linux distribution.

For this small code generator, I had to install no less than 1066 dependencies. So what kind of dependencies are we having here? Let’s have a look. Here are a few of them:

isobject — 6 times

kind-of — 22 times

is-obj — once

unique-string — once

path-exists — 4 times

is-number — 4 times

isarray — 4 times

number-is-nan — once

Curious about the code behind those, I glanced at their github. This is path-exists :

const fs = require ( 'fs' ); module . exports . sync = fp => { try { fs . accessSync ( fp ); return true ; } catch ( err ) { return false ; } };

So this module is really just turning an exception into a boolean.

Let’s look at this is number-is-nan :

module . exports = function ( x ) { return x !== x ; };

Now, this is the unique-string module:

const cryptoRandomString = require ( 'crypto-random-string' ); module . exports = () => cryptoRandomString ( 32 );

… and its dependency crypto-random-string :

const crypto = require ( 'crypto' ); module . exports = len => { if ( ! Number . isFinite ( len )) { throw new TypeError ( 'Expected a finite number' ); } return crypto . randomBytes ( Math . ceil ( len / 2 )). toString ( 'hex' ). slice ( 0 , len ); };

So unique-string is really just forwarding any function call to cryptoRandomString(32) , and three out of four lines of this separate module check that the argument is a finite number — I am by the way slightly surprised the developer didn’t use the module is-finite …!

Among the most famous one-liners, I can’t get along without presenting isarray :

var isArray = Array . isArray || function ( xs ) { return Object . prototype . toString . call ( xs ) === '[object Array]' ; };

… and last, but not least, the lowercase-keys module:

module . exports = object => { const ret = {}; for ( const [ key , value ] of Object . entries ( object )) { ret [ key . toLowerCase ()] = value ; } return ret ; };

Why would you need that? If you don’t know, me neither.



Suspicious engineering

Is this really needed? What does it bring? Why would I add to my project external dependencies for such tiny features? Why would I make my code depend on an external library such as number-is-nan — which means downloading the external module, importing it from my code, and calling its only one-liner function — just to check if a number is NaN , when I can simply use !== , or call the ECMAScript 2015 function isNaN (if I know that my variable is a number)? Same question about path-exists . Same question about isarray and the others.

Adding a dependency to a codebase is not free. There should be a strong reason to do so. Adding a dependency raises many questions: about the maintenance of the library — who? how? —, the quality of its implementation, the existence of documentation, its versioning, the clarity of release notes, and so on.

And trust is one of the key among all these questions. If you trust your library maintainers, if they are professional developers that will push back for invasive features, review before merging patches, fight before breaking your API, quickly fix bugs, etc — if they do all that, then it will be great, and you will be happy. If not… then you had better keep the code on your side.

I am not saying the modules I mentioned above aren’t great. Their implementations are nothing spectacular, but seem reasonable. I question the engineering decision of developers that add dependencies to their systems for questionable features. Nothing is wrong with Node or npm either. It just seems that the direction some Node developers took is debatable, and my problem is that instead of ignoring or questioning such choices, some developers — in this case the Microsoft Visual Studio Code team — blindly follow the dance. I don’t see this as a tangible way to develop softwares. If the very young Visual Studio Code generator already pulls a thousands of these modules, what if we continue to build on the top of it?

To be a bit more constructive: why not simply building bigger libraries? For example, why not a core library around type information, as it seems that many dependencies go into these small modules like is-array , is-buffer , kind-of , etc? The upside would be to get an API rather consistent, maybe more than one maintainer…

And by the way

Eventually, after some long minutes, the installation of my Visual Studio Code extension generator failed in the middle of the installation of the dependencies! I guess this was the end of my adventure here.

As a parting gift, here is the list of dependencies…: