NPM is a nice package-manager indeed, but many developers are not aware of some aspects of NPM that make it an uncontrolled beast.

The NPM semantic versioning system is nice, but it is completely up to developers of NPM modules to honor the versioning. So in theory, a patch/hotfix could contain breaking changes.

So you are using version ~1.0.2 or >1.0.2 of a package in your package.json file in Node.js, and then an NPM developer releases version 1.0.3, which should be a non-breaking fix, but guess what? it broke your code! How? the NPM developer wasn't careful enough and wasn't using versions properly.

One more problem with NPM is that you often don't have a clue about submodules and sub-dependencies, so NPM will often grow and update sub-modules at it's will, which reminds me of a powerful uncontrolled kraken growing limbs over time.

alt

The main problem with this Kraken is that you build and release it to production, and you get an uncontrolled beast, since any module or sub-module could change whenever you install the product. It is a disaster waiting to happen.

"This does mean that when you "npm install" a package with dependencies, there's no guarantee that you'll get the same set of code now that you would have gotten an hour ago, or that you would get if you were to run it again an hour later. You may get a bunch of bug fixes now that weren't available an hour ago." - Nodejs.org blog

Yeah, you might get bug fixes, but you may also get a bunch of bugs, so we are screwed... but there is a way out!

Fighting the kraken

Obviously we need to control NPM, and make it ready for production. deterministic builds are a good thing, and many people try to keep the kraken at bay with one of the following two approaches:

  • Commiting the node_modules folder into source control
  • Using fixed major versions in package.json instead of using more loose semantic versioning (e.g. minor or patch) in package.json.

Commiting the node_modules folder works very well, since you will not run npm install in your environment, but you will get a different set of problems (e.g. GIT/Mercurial problems, cumbersome dev. experience etc.)

Using fixed major versions will keep the required module version under control, but since you don't have control over the sub-modules, you can still have many issues.

Shit happens in the open-source - Myself

Shrinkwrap the kraken

One of the best solutions to the NPM dependency management problem is NPM itself, and to be exact the npm shrinkwrap command.

So let's shrinkwrap the Kraken.

img

"This command locks down the versions of a package's dependencies so that you can control exactly which versions of each dependency will be used when your package is installed. The package.json file is still required if you want to use npm install." - npmjs.org

Let's say you had a package.json that had the following dependencies:

{
  "name": "A",
  "version": "0.1.0",
  "dependencies": {
    "B": "<0.1.0"
  }
}

When you run npm shrinkwrap it will create a npm-shrinkwrap.json file to lock-down the dependencies of modules existing in the local node_modules folder (and sub-modules too). The file looks like:

{
  "name": "A",
  "version": "1.1.0",
  "dependencies": {
    "B": {
      "version": "1.0.1",
      "from": "B@^1.0.0",
      "resolved": "https://registry.npmjs.org/B/-/B-1.0.1.tgz",
      "dependencies": {
        "C": {
          "version": "1.0.1",
          "from": "org/C#v1.0.1",
          "resolved": "git://github.com/org/C.git#5c380ae319fc4efe9e7f2d9c78b0faa588fd99b4"
        }
      }
    }
  }
}

Yes, the shrinkwrapped beast is ugly, but at least it is under control, and you got yourself a nice present: deterministic NPM builds.

When "npm install" installs a package with a npm-shrinkwrap.json file in the package root, the shrinkwrap file (rather than package.json files) completely drives the installation of that package and all of its dependencies (recursively).

To update the shrinkwrapped thingy you can do it with the usual NPM commands (npm outdated -> npm install -> npm shrinkwrap)

However, Shrinkwrap is not a silver-bullet, and it has some limitations too, but at least we have one weapon against the kraken.

For more reading check out:

https://docs.npmjs.com/cli/shrinkwrap
https://nodejs.org/en/blog/npm/managing-node-js-dependencies-with-shrinkwrap