NPM Versions — Explained

Jacob Barr
AT&T Israel Tech Blog
4 min readJun 29, 2020

This article is aimed for both junior developers who want to understand how the npm versioning system works and veteran developers who never did have the time to delve into it. I’ll try to sum up the crucial parts that are most important and every developer should be familiar with, especially those of us who work as part of a team.

Semantic versioning convention

In order to understand npm versioning syntax, we first have to understand semantic versioning conventions.

  • Patch — A patch is an update which is meant to fix existing bugs and it’s backward compatible (meaning you can update without having to worry about your current usage of the library being broken).
  • Minor —A minor update represents an update which introduces new features and it’s also backward compatible.
  • Major —A major update is very different from the previous kind of updates since it is not backward compatible (meaning, upgrading to a new major version introduces breaking changes and might cause your previous version code to break). A major update represents a change which is massive and could change the whole mechanism of the library and how it works.

Meaning of prefix symbols

  • Caret (^) — a caret is the default prefix you get from npm after installing a new package. It gives you the highest minor version available with its highest patch version.
  • Tilde (~) — a tilde prefix will only promote patch versions, meaning that you’ll get the highest patch version for your current minor.

Examples:

  • ^1.2.3 possible result could be 1.3.2
  • ~1.2.3 possible result could be 1.2.4

There are other symbols that can let you pick your version range more specifically. For additional info, check: https://semver.npmjs.com/

A word about self-responsibility — In theory, everything sounds perfectly simple, though in real life, it could happen that a library patch or minor update won’t be backward compatible and will cause previous version code to break. As developers who publish libraries for public use, we must follow the convention and make sure our patch and minor updates are backward compatible.

The lock file

I couldn’t complete this article without describing the lock file and how important it is for teamwork consistency.

The lock file (package-lock.json)

You will be able to identify the lockfile by its name and structure (see the image above). The name will be package-lock (or yarn.lock if you’re using yarn)

When working as a team, it’s important to have the same dependencies as your other teammates; imagine a bug that happens only when you run the code and doesn’t reproduce for any of your fellow developers.

This could happen when you’re using different dependency versions.

Imagine you’ve got the following version for a specific dependency: ^1.2.3

When the dependency was first added, 1.2.3 was its latest version, so that’s the version you’ve got installed. Next week, a new developer joins your team, clones the repository and installs the same dependencies — but by then, the package was updated and now its latest version is 1.3.1.

The new developer will get 1.3.1 due to the caret symbol and will have a different dependency than the first developer who installed the dependency a week ago (who still got version 1.2.3).

Lock file to the rescue!

If you’ve got a lockfile, before npm installs your dependencies, it checks the versions on the lockfile and installs them. So, in the case above, if the new developer has the same lockfile as the first developer, then he’d have the same version. Which leads me to my next point — the lockfile should be pushed to git if you want to conserve consistency between all the teammates working on the same project.

NPM Update

In order to update your dependencies to their max version (according to the range specified), you’ll need to run a command called “npm update”. This command respects semantic versioning and will also update the lockfile with the new installed version so that all developers in the team will share the same updated dependencies.

Dependency Pinning

Another way of solving the problem of inconsistency is dependency pinning. Meaning stating a very specific version and not a range of versions (this is done by simply removing any carets, tildes or any other prefix symbols. e.g- writing just 1.2.3 will always stay 1.2.3). This way we can always be sure all the developers have the same version.

Why we should NOT use dependency pinning?

Because we want updates for bug fixes and additional features which aren’t introducing breaking changes. Some of these updates could be really important even if you’re not aware of them (consider security fixes).

In this article I went over some very important aspects which I believe every developer should be familiar with. I truly hope I shed some light upon those concepts which most of us just don’t have the time to delve into.

Thanks for reading :)

--

--