SemVer - Magic version 0
Tags: semver, npm
Published: 2021-04-06 19:16:44

### SemVer

[Semantic Versioning](https://semver.org/) defines a package version format and a set of rules that should be used when the package updates. 

> Under this scheme, version numbers and the way they change convey meaning about the underlying code and what has been modified from one version to the next.

A brief intro to SemVer: you can specify a version of a package using three groups of numbers: `major.minor.patch`. When you create a new version of your package, you should:
1. Increment `major` if you introduced a breaking change.
2. Increment `minor` if you added functionality in a backward-compatible manner.
3. Increment `patch` if you made a backward-compatible bugfix.

This is pretty much it, although in documents you can find more details.

### Version ranges
When you work with package managers which rely on semver (e.g. NPM), you can declare specific versions of dependencies or _version ranges_, which would allow you to update dependencies easier.

For example, you can use the _caret_(`^`) symbol to tell the package manager that all compatible versions are allowed:
```
// package.json
"dependencies": {
  "react": "^16.0.0"
}
``` 
The above means that all versions of `react` up to (but not including) `17.0.0` are allowed. So when a new version of `react` is out, you can run `npm update` and it will download an updated version of it, update your `package.json` and a lock file.


### Magic zero
But there is [an exception](https://semver.org/#spec-item-4) for packages with `major` version equal to _zero_, e.g. `0.1.2`.

> Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

Technically, it is legitimate to introduce breaking changes, updating the `minor` digit of the package version. So, versions `0.1.2` and `0.2.0` won't be compatible.

To prevent potential breaking changes, when you do a minor update of a dependency, NPM treats versions a bit [different](https://github.com/npm/node-semver#caret-ranges-123-025-004):

> Allows changes that do not modify the left-most non-zero element in the [major, minor, patch] tuple. In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for versions 0.X >=0.1.0, and no updates for versions 0.0.X.

So, if you have a dependency `^0.1.2`, `npm update` won't update it to `0.2.0`, because only `patch` updates are allowed. 
I didn't know this subtlety before and when an automatic update didn't work, I thought that it was due to some bug in NPM CLI :) Even worse, I updated the dependencies manually... Even though we had a lot of tests, how on Earth didn't I break anything?!

Anyways, if you depend directly or transitively on various `0.x.y` versions of the same package, you can't do much. But as a package author, if you introduce a backward-compatible update to `0.x.y` version, you'd better update `patch` instead of `minor`. That way the update process would be more streamlined for the package consumers.

### P.S.
Online semver calculator - https://semver.npmjs.com/
Discussion related to pre-releases and magic zero - https://github.com/semver/semver/issues/221