This is a short postmortem regarding an issue we found this morning on a production NodeJS application at SoundHound. The application hasn’t launched yet, so I can’t disclose it, but the lesson we learned is applicable to all NodeJS apps that run on remote servers.
Background
We use Browserify to generate a bundled app.js file consisting of all the modules that we are using on the client. This generally includes React components, Flux Stores/Actions, and other utilities such as UnderscoreJS.
We noticed this morning that one of our production Node Apps was throwing JavaScript errors when rendering React components. Here was the error:
Uncaught TypeError: Property description must be an object: undefined app.js:66355
Normally, a JavaScript error is usually our fault, but it turns out that the line in question was within the .extend()
method within UnderscoreJS. Oddly, the issue was not happening locally or on our staging server. It was only happening on production. A little debugging showed that the production website was using UnderscoreJS@1.8.2
, while the staging website and our local versions were on UnderscoreJS@1.7.0
.
Turns out that our package.json
listed the following:
"underscore": “^1.7.0”
Where the ^
means “Compatible with version”. As we hadn’t run npm install
locally or on the staging server for a while, those two servers had Underscore@1.7.0
. We had recently run npm install
on our production server, and it got Underscore@1.8.2
. Turns out that Underscore@1.8.0
fixed an issue with extend() that broke what we were doing.
Solution
The solution that we decided to go with was to run npm shrinkwrap
locally. This command generates a JSON file based on the contents of your node_modules
directory. If you run npm install
on a project that has npm-shrinkwrap.json
, npm will use the shrink-wrap JSON to drive installation instead of looking at package.json
. This is a good way to make sure that the dependencies on your remote servers are exactly the same as your local dependencies.
If you ever wish to update a local dependency, you can run `npm update <module>
and then run npm shrinkwrap
again to generate a new npm-shrinkwrap.json
.
Once we generated the npm-shrinkwrap.json
, we just removed node_modules
from the production server, ran npm install
, and saw that the production server was now using Underscore@1.7.0
instead of Underscore@1.8.2
, which solved the issue.
More importantly, we ensured that such an issue would not occur again, since our development dependencies will be replicated on our staging and production environments.
Moral of the story
Use npm shrinkwrap
when you are pushing code to remote servers, so you know what your dependencies are.
If you have any tips on how to make NodeJS deployments easier, add them in the comments below!
Why not instead add these changes “$npm config set save=true
$ npm config set save-exact=true, to your .npmrc file?
Good point! I’ll do that.