This is a how-to article reflecting back on our upgrade process from Node.js 8 to Node.js 12 for the Snugg Pro web application. Described upgrade process is fair for any Node.js version.
TLDR: We upgraded from Node.js 8 to Node.js 12 and decreased the average response time of Snugg Pro (a web application) by 40%.
Node.js version 8's end-of-life was at the end of 2019. This was (and still is) a good moment to migrate to the latest version 12 LTS. Here at Snugg Pro we had prepared the migration in the middle of November 2019. We had tested it on staging 3 weeks before upgrading our production servers.
First of all, remove all unused dependencies. You can use a package like depcheck or you could do it manually.
If you are going to upgrade packages incompatible with a new Node.js version only, it is ideal case.
- In package.json, Change node version in engines sections. It will stop installation with wrong Node.js version.
- Update Node.js version any appropriate way. I use nvm:
nvm install 12.14.0and
nvm alias default 12.14.0. You can reinstall global packages with
--reinstall-packages-from=<old-node-version>. Read more about nvm
- Try to install dependencies.
- Fix all errors step-by-step. Decide if you want to upgrade to the latest package version or not on your own. Usually, there are release notes, you get exact version the most suitable and not broken. It is fine to go on with not the freshest version. I upgraded babel to
7.7.0, because the latter has conflicts with other dependencies.
npm audit or
yarn audit to find vulnerable packages. It is strongly recommended.
You may want to take this opportunity to upgrade some packages to the latest major version by the way. This may require some refactoring. For example, the
joi package was moved to
@hapi/joi. This required us to change all import statements for this package but was relatively straight forward. I removed the deprecated
bcrypt-nodejs package in favor of the
bcrypt package. It affects authorization and authentication. The stakes are higher with such an upgrade but security is critical, so it is worth the extra hassle.
Sometimes, you may need to force an unnatural version of application dependencies. This should be done sparingly but it is useful if you want to patch a security issue. For such cases, you should use the
resolutions sections of package.json helps. Read more about the resolutions feature for yarn or for npm.
Once all the dependencies are ready, it is time to deploy your changes to staging. No matter how sure you are or how complete your tests coverage is, you should stage it and forget it for while. The more you can wait and test the Node.js version upgrade on staging, the better your chances of catching unexpected issues. We tested it for 3 weeks, and still missed a minor bug related to error logging in one of our queue workers.
All charts are provided by Newrelic.
Let's start from weekly service level agreement (SLA) report.
The last two columns/weeks reflects changes after upgrade to Node.js 12. It is easy to see all metrics are significantly improved. Apdex reaches 0.95.
There are more spikes on Node.js 8 and some of them take up more than 2 seconds per minute. Node.js 12 takes more milliseconds per minute on average, but there is only one spike of more than 1 second per minute. Node 12 is more balanced by default.
Node 12 makes 2 to 3 times more garbage collection pauses. The idea here is to continue to serve clients by making more frequent but much shorter pauses, instead of stopping everything for 1 second once.
You may already have a sense of the memory usage from above metrics. If Node.js 12 collects garbage more frequently by default, it uses noticeably less memory on average.
Node.js 12 rarely consumes more than 220Mb, but Node.js 8 reaches 400Mb on peaks. Node.js 12 is smarter with memory by default.
If you don't know what is
tick in Node.js, you may read about event loop and ticks here
With Node.js 8, we got pauses upwards of 30 seconds. This was partly due to setting
max-old-space-size to 440Mb for the V8 engine. Node.js would stop serving clients if the old space size reached the preset value. You can read about old space garbage collection here.
Node.js 12 V8 engine settings are balanced better by default. In addition, Node.js 12 brings a fresh version of the V8 engine, and it results in big performance improvements. You can read V8 engine release notes here for more details.
Moreover, Node 12 makes it easier to eliminate
babel on the server, since Node.js 12 supports a lot of ES2016/ES2017/ES2018/ES2019 features out of the box.
At the risk of stating the obvious, upgrading to Node 12 will also ensure that you have access to all the features and security updates that come from running the latest LTS version of Node.js.
This concludes our run through of the Node 8 to Node 12 upgrade.
Thank you for reading.
PS: Many thanks to Benjamin Mailian – Snugg Pro Co-Founder / Head of Product for help with this article.