What is semantic versioning?

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

If you are working with Node.js and npm packages it's likely that have heard the term "semver" or semantic versioning. In this article we'll discuss what it is and how it is used.

Version management

Managing large software projects and their dependencies can pose a real challenge for developers. Each dependency can manage versions using different methods and techniques and standarising version numbers as well as when and how to increase the version numbers is certainly something that is required.

In light of the the semantic versioning standard was born and npm (the package manager for Node.js) implements this standard.

In systems with many dependencies, releasing new package versions can quickly become a nightmare. If the dependency specifications are too tight, you are in danger of version lock (the inability to upgrade a package without having to release new versions of every dependent package). If dependencies are specified too loosely, you will inevitably be bitten by version promiscuity (assuming compatibility with more future versions than is reasonable). Dependency hell is where you are when version lock and/or version promiscuity prevent you from easily and safely moving your project forward.

Semantic versioning in Node.js (npm)

Semantic versioning requires to have a major, a minor and a patch version number available. Therefore a package with version 1.3.5 means the following: major.minor.patch135

How to interpret version numbers

Increasing the major version signifies incompatible changes to the package, increasing the minor version number signifies additional functionality in a backward-compatible manner and last but not least increasing the patch version number signifies that there are backward-compatible bug fixes.

Therefore if a bug fix or a minor change gets added to our imaginary package, the last number should be increased as it's a patch release: 1.3.6.

Adding, new functionality to the package (that are non-breaking changes/features) would mean that it is considered to be a minor release and therefore the middle number should be incremented: 1.4.5.

And last but not least, changes that break backward-compatibility are considered to be a major release therefore the first number should be incremented to signify this: 2.3.5.

npm and package version numbers

When installing packages with npm for Node.js package versions can also be specified and we have a few options - we can specify ranges as well as exact version numbers:

  • package@2.0.0 - install the package with exact version number 2.0.0
  • package >= 1.2.7 - install package with version 1.2.7 or 1.2.8 or even 2.5.5 but never 1.2.6 or smaller
  • package ~1.2.3 - install patch level changes (1.2.3, 1.2.4 but not 1.3.x, nor 2.x)
  • package ^1.2.3 - install minor updates (1.2.3, 1.2.4, 1.5.6 but not 2.x)

npm tagged versions

When working with npm we may come across situations when the version number is specified with the @ symbol: package@latest or package@next.

These are tagged versions and tags are a supplement to semantic versioning for organisaing various packages. For example the latest tag can, at all times, contain the latest stable version of a package. next on the other hand can contain the latest beta version of a package.