How to upgrade to npm packages with breaking changes?

Sanyam Aggarwal
5 min readOct 7, 2021

--

A whole lot of features rolled out in ES6 and this caused a lot of npm packages to release new versions that either had breaking changes or added async support rather than using callbacks, and the upgrade to these made absolute sense when everybody started converting their code using nasty callbacks to the nicer and cleaner async syntax. Say you have already managed to upgrade your package anyhow to not lose any new functionality the new versions had to offer. The world of Javascript is a developing one and another such major release of features can once again trigger breaking changes. In this article, we will discuss, how to structure the usage of npm packages in your project/codebase so that you can upgrade to newer versions with the least possible development and testing effort.

Do you have an npm package that has been widely used in your project that you were long ignoring to update but now has a major stable release with all the security and bug fixes and the async version that you now have to upgrade to? But the package is so extensively used that updating it all at once will be a nightmare, not only for the developers but also for the testing team. To top it all, if your package is used directly in code haywire without having any dedicated module/file for it, then you are looking at pretty large changes in your codebase.

Photo by smithore

The right way to handle such a mess is always an iterative approach, a few changes at a time.

Assume that some package foo is being used in the project which has to be updated from the version 1.4.0 to 2.1.3.

// ES6 import syntax
// import foo from 'foo';
const foo = require('foo');

foo will correspond to one version of the package only in this case 1.4.0. But you begin to wonder that to update iteratively, some part of your codebase has to use the existing installed version of the package and the other new one, but how it is even possible to use two versions of the same package at a time? You know you can’t use the two versions of the same package by the above import/require statement.

Photo by Juan Rumimpunu

How to use two versions of a package in your code?

Installing a specific version of a package

npm install <name>@<version>

For instance, this command installs the exact version 2.1.3 the package foo

npm install foo@2.1.3

This updates the foo package to version 2.1.3 and you still cannot have co-existing old and newer versions of the package.

Installing a package under a custom name (alias)

npm install <alias>@npm:<name>

For instance, this command installs the package foo under the custom name foobar

npm install foobar@npm:foo

Now combine both of these to install a specific version of a package under a custom name—

npm install <alias>@npm:<name>@<version>

npm i foo-2.1.3@npm:foo@2.1.3

This installs the version 2.1.3 of foo under a new name i.e.foo-2.1.3, so that you can use it independently without affecting the original package used.

const foo_213 = require('foo-2.1.3');

Now the package required/imported as foo in the code everywhere uses the already installed version 1.4.0 of the package foo, and any new piece of code can use foo-2.1.3 to use the newer version - 2.1.3 of the package.

Handling Breaking Changes

Ideally, there should be a helper module/file for using npm packages to avoid changes in the whole codebase in the event of breaking changes in newer releases.

The helper file for each package will contain functions (wrappers) that will call the underlying API of the package. Any new functionality introduced in the newer version will map to a new function in this module, and any breaking changes can be handled easily by changing just this file without having to inflict changes in the whole codebase.

For any new usages in the code where usage of this package is required, you should instead require this newly created helper module/file and try to iteratively shift your old code to use this module.

If you are considering the installation of a new package for a project, it is best to use it from dedicated helper modules to avoid such instances.

Photo by Sigmund

PRO TIPS:

Save exact versions of packages

Javascript developers tend to simply run this command to install a new package.

npm i foo --save

Be aware that this adds the dependency of foo in package.json as -

{  "name": "baz-bar",   ....  "dependencies": {   ....   foo: "^2.1.3",
...
}

The carat (^) means that the package will update to minor updates or patches, if available. The next time you have to do an npm install for setting up the environment for your project, there is a high chance that some package gets automatically updated. Any updates to npm packages should be tested and to ensure developers have the same local environment, the installation of the exact version of a package is highly preferred.

npm i foo --save-exact
# OR
npm i foo@2.1.3 --save-exact

Verify version before installation

the npm description page for mongodb

Before installing or updating to the latest version blindly, always check the versions tab on the npm description page to check popular versions, the ones with the highest downloads. It is not always the case that the latest version is popular, and can have many bugs.

Commit package-lock.json

Developers often ignore to commit changes in package-lock.json or have package-lock.json git ignored. This is NOT recommended.

npm ci

Run npm ci 🚀 instead of npm install 🐢 when setting up your project in a new environment. Thank me later.

https://www.npmjs.com/

I have tried to share the best practices I have discovered during my experience of handling updates to packages with breaking changes in newer versions of npm packages in large Node.js projects. Let me know in the comments, I would like to know your experiences as well.

Acknowledgements

I express my gratitude to my mentor and guide — Dilraj Singh, with whose help a strategy was orchestrated to iteratively upgrade to the latest stable version of MongoDB Server and an update to npm package mongodb was a part of required codebase changes.

--

--

Sanyam Aggarwal
Sanyam Aggarwal

Written by Sanyam Aggarwal

sanyamaggarwal.com Software Engineer. Car Enthusiast. Living to Learn, Learning to Live.

Responses (2)