DEV Community

Cover image for How to upgrade Node.js and dependencies. Results.
Roman Voloboev
Roman Voloboev

Posted on • Updated on

How to upgrade Node.js and dependencies. Results.

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.

How-to

Check your dependencies

There is a lot to upgrade in a mature javascript application. You should be smart about what is going to be upgraded and what is not.

Remove unused dependencies

First of all, remove all unused dependencies. You can use a package like depcheck or you could do it manually.

Update dependencies for your Node.js version

If you are going to upgrade packages incompatible with a new Node.js version only, it is ideal case.

  1. In package.json, Change node version in engines sections. It will stop installation with wrong Node.js version.
  2. Update Node.js version any appropriate way. I use nvm: nvm install 12.14.0 and nvm alias default 12.14.0. You can reinstall global packages with --reinstall-packages-from=<old-node-version>. Read more about nvm
  3. Try to install dependencies.
  4. 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 6.26.0 instead of 7.7.0, because the latter has conflicts with other dependencies.

Update vulnerable dependencies

Use npm audit or yarn audit to find vulnerable packages. It is strongly recommended.

Update dependencies to the latest version

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.

Make some strategic choices

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.

Give it time

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.

Comparing the performance of Node.js 8 and Node.js 12

All charts are provided by Newrelic.
Let's start from weekly service level agreement (SLA) report.

Weekly SLA

Snugg Pro Weekly SLA

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 will be more charts with metrics next.You may want to read more about Garbage Collection in Node.js here or extended version here.

GC (Garbage collector) pause time

Before:

GC pause time v8

After:

GC pause time v12

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.

GC pause frequency

Before:

GC pause frequency v8

After:

GC pause frequency v12

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.

Memory usage

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.

Before:

Memory usage v8

After:

Memory usage v12

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.

Maximum CPU time per tick

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.

Conclusion

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.
Bye, folks.

PS: Many thanks to Benjamin Mailian – Snugg Pro Co-Founder / Head of Product for help with this article.

Discussion (0)