September 03, 2020
3 min read
In the world of Node.js, you’re gonna be using the npm cli tool. You’re also going to find lots of blogs telling you how to do things but I constantly find one piece of info lacking and surprisingly, this is about installing node modules in a project. Well, I guess there are two things that are related and one sort of makes the other moot.
One thing I always try to get people to do is pin versions of their dependencies in
package.json. What pinning does is
makes anyone installing the dependencies always have the same version. By default, npm adds the
~ prefix to the
versions so if you were to
npm i @mitchellsimoens/versionator it will use
^1.0.5 as the version. This means, if
1.0.6 or 1.1.0 is released, someone could have a different version that has been untested and may have features not
present in older versions. This can add confusion but even worse, this can add risk (even for
~ prefix is a little better
but the same issues can still occur only it locks down the possibilities to patch versions not minor versions.
So what’s the big deal? In today’s world, we have to admit we freely install node modules from anyone. This means we put a lot of faith in people and while I would hope people have great intentions, some don’t and even great intentions aren’t perfect. Also, not everyone follows semver strictly so a minor version maybe should have been a major.
You’re best to protect yourself as best as you can by pinning versions. I even change the default to always pin via npm’s config:
npm config set save-exact=true
I’ll be honest though, this is a bit of a false sense of security as it’s not 100% but it’s at least some form of prevention. Someone I work with found this blog post that does a great job at some pros/cons of version pinning so I definitely think that is worth a read.
The Right Command
The other thing sort of makes version pinning moot. Say you clone a repo that contains a node project, what do you do
first? I bet you run
npm install (or
npm i). You just used the wrong command. Instead, and hopefully everyone checks
package-lock.json into VCS, you should get into the habit of using
npm ci. What this command does is installs
what is in
package-lock.json. If you used
npm install, npm will go and try to find the latest version of your
dependencies (and their dependencies) that match the version and version prefixes come into play then. Npm will then
package-lock.json with these updated versions and you just introduced risk.
In fact, I ran a quick poll of some colleagues and 10 out of 11 (the poll got pushed way off screen with all the
npm install and some didn’t even know about
npm ci. The one person is someone I work with a lot so
wasn’t a surprise there. This is also why I will still pin versions to get at least some sort of protection from those
npm ci when they should.
If you are adding a dependency, then use
npm install since that will just add the dependency(ies) and not upgrade all
existing dependencies. If you are wanting to install all dependencies then use
npm ci and protect yourself.
Written by Mitchell Simoens who is a long time nerd developing software and building computers and gadgets. Anything expressed on this website are Mitchell Simoens's alone and do not represent his employer.