This post takes on an important topic that is subject to countless different approaches: Release management.
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!
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 |
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: 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:
- 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) - We need to republish the artifact with the release version and optionally any new tags
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.
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:
- First we create a registry for our development versions
- 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.
- Then we enable a plugin called “Version auto increment”.
package.json
in our source repository.
Enabling the auto increment plugin |
Then we just code away, making changes and publishing our work to the development registry.
# Make sure we are using the correct registry
$ npm config get registry
https://test.bytesafe.dev/r/dev
$ npm publish
npm notice
npm notice foobar@1.0.0-0
npm notice === Tarball Contents ===
npm notice 204B package.json
npm notice === Tarball Details ===
npm notice name: foobar
npm notice version: 1.0.0-0
npm notice package size: 237 B
npm notice unpacked size: 204 B
npm notice shasum: 50584a3de2052549aad54e2c6418a97f1415882e
npm notice integrity: sha512-9czoLXYk+KQXj[...]8RI9qBCgYSn5w==
npm notice total files: 1
npm notice
+ foobar@1.0.0-0
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.
cat package.json
{
"name": "foobar",
"version": "1.0.0-0",
...
The plugin will automatically step versions for us |
When we feel that we have a worthy production candidate we push it either by
using the web app 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.
Now we have a production ready package that we can distribute to any public registry with confidence!