npm major package upgrades with backward patch support

A short checklist for publishing new major versions of your packages in the npm registry including support for patching older versions.

As your NodeJS packages evolve, there will come the point when you will have to make non-backward compatible changes. According to semantic versioning you should then increase the major version number, e.g. from 1.x.x to 2.x.x.

Simple package publishing without major changes

So far, most probably all you did to release a new version of your package was to increase the minor (second) or patch (third) version number in your package.json and simply run npm publish (including --access public for scoped packages) to push the new version to the npm registry. Your “versions” tab in the registry will look quite similar to this with one tag called latest.

npm-versions

Now, for advancing to the next major version 2.0.0, there is a bit more to do and think of. Especially, if you want to continue patch support for the 1.x version. You should consider this good style, because your package is actively in use with other projects and not everbody is able to implement the breaking changes immediately.

Checklist for publishing a new major package version

Here are the tasks you should consider when publishing a new major version of a package…

Branching the old version source code

Typically you will be developing your new major version in a separate branch, let’s say version-2. Meanwhile master is holding the code of the currently published package version.

$ git branch
  master
* version-2

To be able to provide further patch support for version 1.x, you need to retain a copy of the code. For that, create a branch version-1 out of master and push it to the repository. Ideally, you should do this before merging the new version 2.x code to master.

$ git checkout master
$ git checkout -b version-1
$ git push -u origin version-1
$ git branch
* master
  version-1
  version-2

Now you have the 1.x sources in a separate branch for patching the old version if needed.

Creating a dist-tag for the old package version

Additionally to the packages version number, npm has so called tags associated with a package. By default, there’s only one tag: latest. Simply call npm dist-tag to list all current tags.

$ npm dist-tag
latest: 1.0.7

Whenever you do a npm publish without specifying any tag, the new version will be published under the latest tag. Everytime any user does a npm install or visits the npmjs.com site of your package, he’ll get the last published version for the tag latest.

Now what does this mean? When you need to patch an older version, let’s say v1.0.7 to v1.0.8, after you have already published a new major version v2.0.0, you cannot simply do an npm publish for the v1.x patch. Doing so would set v1.0.8 as latest which is not intended!

Luckily, the solution is quite simple. With npm dist-tag you can easily create a new tag, let’s say stable-v1, for the v1.x branch having the current version 1.0.7. This new tag is then used when you need to publish a patch for the older v1.0.7 version.

$ npm dist-tag add my-package@1.0.7 stable-v1
$ npm dist-tag
latest: 1.0.7
stable-v1: 1.0.7

Having that setup in place, you are ready for potential updates of the old package version without affecting the latest version. See updating an older package version for more details.

Copying and updating the external documentation

As with any new version of a package you should update the documentation in README.md according to the changes you made. Additionally you will probably have an own website linked via the homepage property in your package.json.

I recommend not to create a completely new site for the new major version of the package and change the homepage property to that. The reason for that is mainly related to search engines…

  • You may want to retain your current rankings/reputation for the new major version
  • You may want search engines to only index exactly one website for the package, which should contain the documentation of the latest version

This is achieved by…

  • Creating a copy of the existing package’s website which then has a new URL
  • Optionally excluding this new URL from being indexed by search engines, e.g. via disallow in robots.txt or a noindex meta-tag
  • Updating the original website to the new major package version
  • Optionally creating a link to the old documentation on the original website, so that users of the old package version have a chance to find it

Merging the new version source code

After saving the old versions code in a separate branch, it’s time to merge the new major version to master.

$ git checkout master
$ git merge version-2

Publishing the new major version to the npm registry

Make sure that you are in the master branch and the version number in package.json is correctly set to the new major version. Then push the new version to the npm registry.

$ npm publish

Or for scoped packages…

$ npm publish --access public

Heading back to the “versions” tab of your packages site you should see version 2.0.0 released under the latest tag and the recently created tag for the older 1.x version, like so…

npm-versions-with-tags

Great! You have successfully released the new major version of your package ensuring maintenance for the previous version.

Optional final actions

Although you’re done, I recommend the following optional actions for a for a consistent and qualitative development process.

Creating a tag & release in Git

It’s a good practice to tag published versions in your Git repo and create a release for them. To create the tag, do the following in the master branch.

$ git tag -a v2.0.0 -m "Release 2.0.0"
$ git push origin --tags

For creating a release in your Git repo you could either use a command line tool like GitHub CLI or simply switch over to your repos GitHub site. There, it takes only a few clicks:

Releases (right pane) >> Draft a new release >> Select v2.0.0 tag from branch master and enter a title & description >> Publish release.

github-create-release-3

After that you’ll see the latest release version 2.0.0 in the right pane of your repos site.

github-release-latest

Deleting the new versions development branch

After merging the version-2 development branch back to master, you can get rid of it – both, local and remote.

$ git push -d origin version-2
$ git branch -d version-2

Updating an older package version

Let’s assume you need to patch v1.0.7 to v1.0.8 but v2.0.0 is already released. If you followed the checklist steps mentioned before, your package version & tag situation will look like this.

$ npm dist-tag
latest: 2.0.0
stable-v1: 1.0.7

To update the old version v1.0.7, simply do the needed changes in the formerly created branch version-1. Then increase the version number there to v1.0.8 and publish the new 1.x version directly from this branch using the tag stable-v1.

$ git checkout version-1

# Implement changes & test, set version to 1.0.8, commit to branch

$ npm publish --tag stable-v1

This release will not effect the latest tag, meaning that v2.0.0 is still considered to be the most current version of your package when somebody installs it or visits the packages site. Don’t forget to add --access public for scoped packages.

$ npm dist-tag
latest: 2.0.0
stable-v1: 1.0.8

Happy coding 🙂

Useful links