NPM - An intervention
Posted 8 hours, 7 minutes ago by Felix Geisendörfer
Update: Isaac commented and explained why fuzzy version specifiers are here to stay. I'll be ok with it and will adapt my workflow accordingly.
NPM is the official node.js package manager. Unlike many package managers that came before, it is actually incredibly awesome, and has helped to create one of the most vibrant communities in the history of open source.
However, today I want to talk about a few aspects of npm that concern me. In particular I want to talk about stuff where I feel that NPM is making bad things easy, and good things hard.
NPM module versions are broken
Today, I tried to contribute to the forever module. The company I am helping had to patch their version of it because of a hard-to-reproduce bug in production and asked me to help submitting their fix upstream. Being the scientific type, I set out to write a test case against the forever version their patch is based on:
$ npm install forever@0.7.2
Fantastic, NPM lets me specify which version of forever I want to install. Now lets verify the installed version works:
$ ./node_modules/forever/bin/forever
node.js:134
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: undefined is not a function
at CALL_NON_FUNCTION_AS_CONSTRUCTOR (native)
at Object. (/Users/Felix/Desktop/foo/node_modules/forever/lib/forever.js:43:23)
...
Oh no, what happened? Mind you, except for an unrelated patch, this version of forever is running perfectly fine in production.
Well, as it turns out, you have been lied to. There is no such thing as forever v0.7.2. At least not a single one. It depends on an implicit and unchangable second parameter: time.
Why is that? Well, it is because forever v0.7.2 depends on this:
And as it turns out, nconf has released newer versions matching this selector, featuring a different API.
You are doing it wrong
"Hah!", you might say. "That's why you should check your node_modules into git!".
I am sorry, but that is not helpful. While this will allow me to pin down the node modules used by my app exactly, it does not help me here. What I want to do is to reproduce this bug in a standalone copy of forever v0.7.2, then check if it exists in the latest version, and if so submit the test case and fix for it upstream.
However, I can't. Not without manually resolving all forever dependencies the way NPM resolved them when v0.7.2 was released. (The fact that forever is a bit of a spaceship when it comes to dependencies does not help either).
Discouraging Open Source
Speaking about Mikeal's article. I felt that something was wrong about checking your node_modules into git when reading it, but it is only now that I can point out what:
In the article, Mikeal argues that module authors should not try to exactly reference their dependency versions, so this way users would get more frequent updates of those dependencies and help test them.
However, he says doing so for your app is a good thing.
I disagree. To me, this approach discourages open source for two reasons:
a) Bug reports:
I currently maintain 44 NPM modules. It is very hard to keep up with that.
If you are asking me to support multiple versions of all my dependencies, I will have to stop helping people with bug reports for my modules.
When somebody reports a bug for a given version of my module, I want to know exactly what version he used. Figuring out when he installed my module to rule out dependency issues for every bug report is not an option for me.
b) Contributions
Ask yourself what is easier. Adding a quick patch to a node module you already track include in the git repo of your app, --or-- creating a fork of it, fixing the problem in the fork, pushing that fork on GitHub, changing your package.json to point to your fork, and submitting a pull request.
I know people cannot be forced to contribute back, nor should they be. But as things stand right now, checking in all node_modules of an app into git is the only sane option, as the version numbers in your package.json are essentially meaningless.
This means that contributing back to open source is made difficult by default, while keeping your patches to yourself is made easy. I would like this to be the other way arround.
Conclusion
I propose to gradually drop all support for fuzzy version specifiers from NPM.
To me, fuzzy version specifiers are entirely evil. They make things more complex. They force me to manually snapshot the packages I depend on for my apps. They prevent me from supporting and contributing to open source.
So rather than throwing more complexity at this problem, lets just remove this feature alltogether.
If you agree, please re-tweet this article or leave a comment.
--fg

