loading...
Cover image for How to release npm packages with confidence

How to release npm packages with confidence

rasmus profile image Rasmus Originally published at bytesafe.dev ・9 min read

This post takes on an important topic that is subject to countless different approaches: Release management. Specifically we will see how this can be handled in the world of JavaScript and npm.

If you have ever been tasked with setting up release management you know it can be difficult. Just designing a theoretical workflow can in itself be a challenge but on top of that we then have to add the practical limitations of the tools available and mix that with a multitude of people and different workflows.

This post will help you set up release management with npm by looking at what other people are doing and by using a few tricks from my own experience. And finally a look at what Bytesafe can offer for this process.

Sounds good? Let's start then!

Disclosure:
I am a member of the team behind the service Bytesafe that offers free, private npm registries and that helps you handle different workflows with ease.

The goals for successful release management

When setting up release management it is easy to get distracted by a lot of different things.

Your new solution might contain parts from an old workflow that you are so used to that you fail to question it. You could also be led astray by what other people in the community are doing, unaware of any problems that they might have to account for which you don't.

Because of this it is always best to start with a goal statement that is technology agnostic. Using a goal statement we can always return to it and benchmark our different solutions.

Below is the list that we are going to use to achieve successful release management, derived from my personal experience handling these issues for various teams and different types of software.

Simple

Release management needs to be simple to make sure we release what we intend to release.

Clear

It also needs to be clear so that we can be sure that we have actually accomplished what we set out to do.

Fast

A point that is sometimes overlooked until put into actual use. The release management needs to be fast to not waste time and tempt people to cut corners.

Reliable

Above all release management needs to be reliable. Otherwise we cannot trust the process and are more likely to make mistakes.

The npm ecosystem

Now that we have our goal statement in place we can start to take a look at the npm ecosystem. There are several aspects of the world of npm that are relevant to our topic.

First of all, npm is special in its fast pace and in its commitment to small open source components. To get the integration right between the myriad of different projects it relies on the semantic versioning scheme. When done right it can be a very powerful tool for large scale cooperation but can be very brittle when versions don't match expectations.

The second aspect is the public npm registry, simple and strict in its philosophy: don't remove what you have published. This is one of the big aspects of release management with npm: we want to get it right the first time around.

The third part is that we get a limited toolbox to work with from the public registry. We got versions and tags, that is it.

What does the World Wide Web have to offer?

It is always a good idea to figure out how others have solved the issues of release management. Hopefully there is already some off-the-shelf solution that meets our requirements. So we first search the web to see how others set up release management within the npm ecosystem.

At the time of writing there is a clear theme to the solutions to be found. The solutions, in essence, take control of the process by formalizing the interaction between the npm client and the public registry.

In practice it means that we for example use a tool like semantic-release. Semantic release will help us structure and automate much of the release process and give us clear rules that we can follow. Semantic release works by structuring the git log in a certain way which is needed for the automation.

This in itself creates another problem: How can we make sure that the git log is formatted correctly? Well in the true spirit of the npm ecosystem there is a multitude of small tools that will help us with this:

  • Commitizen: A tool to create correct commit messages
  • Commitlint: A tool to check that our commit messages are correctly formatted
  • Husky: A tool to generate git hooks for stopping bad commits

Basically those are the solutions I found.

I suspect that there is a lot of custom npm scripts out there doing the heavy lifting for a lot of teams that are not sharing their workflows on the internet.

Beauty of the current solutions

By using semantic release we can improve and advance closer to our goals: We automate large parts of the release management giving us improved stability. We have rules that connect the release changelog and the git log giving us improved overview of the process which makes it more clear.

Another benefit comes from the fact that we handle the process on the client side which makes us in part agnostic to the exact npm registry we are using. It could be the public registry or a private registry: we are fine with either.

Angular style commits: Well-structured commit logs

Angular style commits: Well-structured commit logs

 

Problems with the current solutions

Tools like these are great and has probably improved the release management for countless of organizations.

But when I see tutorials with 5+ different client side tools (however small and "lightweight") I hesitate to go down that route for several reasons:

Added complexity

I have done my fair bit of "yak shaving" and if I can avoid it I would rather not have to troubleshoot my "commit-linter-pre-release-git-hook".

Additional tooling to learn

Herding developers is tiring and time-consuming. A client side solution always entails some new concept that developers have to be introduced to and adhere to. I rather handle the complexity somewhere else and let the developers remain in their happy fairy tale land with rainbows and unicorns.

developers

Developers: Look how happy they are when somebody else takes care of the problem

 

Alienation

Moving the details of release management into the developer tools is great when your team consists only of developers. A modern DevOps inspired team is made up of a wider spectrum of competences. Many of those will have an interest in the release management but will only grasp the developer tools such at git and npm on a very basic level. Burying the release management deep into the client tools can be a great disservice to those people and with that also the entire team.

A different approach

Now that we have seen what others are doing I would like to return to our initial goal statement. Let's get a fresh start and try to get as close as possible to a solution with as little effort (work, complexity, maintenance cost and so on) as possible. Then we can compare it to any current solutions.

Back to our original goal statement

We want to be able to release npm packages in a fashion that is simple, clear, fast and reliable. For that I think that a reliability junkie's best friend: "Promoting Artifacts" might be a good fit.

Promoting artifacts in the npm world

The concept of promotion is simple: instead of building a release version we simply select a well tested development version and make this a production release. Ideally this make no changes to the artifact itself. We could change the name of the artifact, change external metadata or move it to a different location to mark it as a release version. By using promotion instead of rebuilding we generally avoid a lot of common error causes: testing one thing and releasing another, slight differences with the production build chain and the development build chain and so on.

An npm package is a file archive that amongst other things contains a
package.json file which holds information about the package. If we are to "promote" an npm package from a development version to a production version there are two things that need to be done:

  1. We need to change the version in package.json contained in the package archive from the development version to the production version (which unfortunately is a small change to the artifact itself)
  2. We need to republish the artifact with the release version and optionally any new tags

Promote

Promote: Yes 1.0.39-alpha+20200220 you are the chosen one

 

Leveraging a private registry

Having a private registry makes the promotion strategy much easier. We can then just publish development versions during the development and testing iterations. Once we have an artifact that we are happy with, we promote that artifact and can publish it to the public registry.

One side effect of promotion is that we move some ceremony involved to post release. Things like adding tags to the source code repository or updating changelogs. The fact that we do this after we have a working artifact both saves us time and limits "littering" of source tags and version numbers being associated with broken builds and other side effects that we carefully need to rollback in case of failure.

Result

The new approach is clearly different from the one that we found earlier, but how well does this solve our problem?

Simple - Check!

One simple transformation is all we need to create a release version. No need for a multitude of different client side tools.

Clear - Check!

By keeping the release so simple and the resulting artifacts in different registries, we have a workflow that is both easy to verify and approachable by non-technical team members.

Fast - Check!

By skipping a lot of "upfront" cost in having the ceremony before every release and by not rebuilding the artifact, but simply transforming it, we get a very fast release process.

Reliable - Check!

By not rebuilding the artifact we don't expose ourselves to the risk of getting a different artifact.

Using a promotion strategy with Bytesafe

Applying the workflow described above on Bytesafe is easy:

  1. First we create a registry for our development versions
  2. Then we set our package version to a pre-release version. For example 1.0.0-0 if we plan to release a 1.0.0 version.
  3. Then we enable a plugin called "Version auto increment".

By using a numerical pre-release version we can use a convenience plugin in Bytesafe called "Version auto increment". On publish this plugin will calculate a package version by incrementing the highest know version in that registry. The first time 1.0.0-0 is published it will be published as 1.0.0-0, the second time it will be published as 1.0.0-1, the third 1.0.0-2 and so on. No changes have to be made to the package.json in our source repository.

auto increment plugin

Enabling the auto increment plugin

 

Then we just code away, making changes and publishing our work to the development registry.

Publish

We don't have to remember to change the version in our package.json when
publishing to a registry where we have the auto increment plugin enabled.

Unchanged package.json

Versions

The plugin will automatically step versions for us

 

When we feel that we have a worthy production candidate we promote it either by using the web console or the CLI. If we for example select to promote 1.0.0-5 to version 1.0.0 it will rewrite the package.json contained in the package archive from 1.0.0-5 to 1.0.0 and republish it.

Promote

When promoting you can select to publish it to another registry which is a good idea to keep your development packages separate from your production packages.

Production version

Now we have a production ready package that we can distribute to any public registry with confidence!

Thanks for reading! If you think that this workflow can be beneficial to you or you team, just head over to Bytesafe.dev and try it out!

Discussion

markdown guide