Patrick Pfeifer Software Engineer

How to Build a Website (2/3): Dependency Management with NPM

Jul 19, 2017 7:34:44 PM

The previous post was covering content management. This post is about dependency management. And in in this problem domain, for web projects, an important tool is NPM, the Node Package Manager. It has been shipping alongside Node.js for a long time. Only time will tell how long it stays that way, but as of today, due to the bundling with the Node.js runtime, NPM is still the de-facto standard dependency manager for Javascript projects.

In the recent 5.0 release, an important feature, which has been optionally available before, was “lifted” into a standard feature: Version pinning of the complete project dependency tree. The need for this feature stems from the desire of any software craftsman to have reproducible builds, i.e. to ensure that no unintentional changes are sneaking into the build output, e.g. due to build platform differences or -updates. Dependency tree pinning has been provided by YARN, Facebook’s own re-implementation of NPM, from the beginning. It was one of the major reasons why YARN was built in the first place. Thus, there is some similarity to the way Spring has pushed JEE to embrace POJO service classes and IO.js has pushed Node.js to develop and adopt new features that many people have long been waiting for, but never got. So dependency tree pinning is available in Node.js and it thus makes sense to use it.

  1. Use NPM version 5.0 or later or, in case your development machine’s or build system’s OS does not provide that version yet, issue an “npm shrinkwrap” command whenever you add/modify/remove any dependencies from you project.
  2. Check-in the package lock file – “package-lock.json” for NPM version 5.0 or later and “npm-shrinkwrap.json” otherwise – to the SCM repository and update it whenever you update the “package.json” file.

Now you can be sure that the exact same versions of all dependencies are going to be pulled in, whenever you build the website locally (during development) or directly on the web hosting system (upon deployment). The “npm install” command will automatically make sure that the “pinned” versions of all direct and transitive dependencies are installed.

Because I have been talking about the build process a lot now, I should say that in this little website project, that I am referring to here, NPM is not used as a build tool. I am using it only for dependency management. However, the “dependencies” section of my “project.json” file is nevertheless empty. The project only has “devDependencies”, i.e. build dependencies (or <scope>compile</scope> in maven speak). The build process, to be described in the next post, then generates a deployment artifact that is completely self-contained and executable in any modern browser.

There are, of course, many more details to know about dependency management and NPM. Feel free to comment or drop me a line to let me know of your favorite use case or feature that I have missed.

In the next and final post in this series I will describe the packaging and deployment of the project.