Semver explained - why is there a caret (^) in my package.json?
So you're installing new packages and get your package.json updated as a side effect. Taking a closer look, you notice that there's something in front of the version numbers.
You're guessing this is some sort of way to widen the range of accepted versions. You'd like to be clear on this one since it leaves you with that unpleasant feeling of uncertainty. You wouldn't like to add a dependency that might break the build for your whole team.
Introducing Semantic Versioning
The prefix character (^) has to do with a version numbering scheme called Semantic Versioning or semver. Semantic Versioning dictates what kind of changes cause the version number to be incremented. Semver uses three-part version number like 3.9.2 and calls these three numbers from left to right as the major, minor and patch numbers.
3 | . | 9 | . | 2 |
---|---|---|---|---|
major | minor | patch |
The basic contract for the module maintainer making changes is
- backward-incompatible change increments the major number
- new functionality that is backward compatible increments the minor number
- simple bug fix to existing functionality increments the patch number
For any dependency, the release 1.0.0 is considered the first stable release, and the semver contract does not apply to releases before it.
Let's take lodash version 3.9.2 as a starting point. Major backward incompatible change to, for example, how
_.filter()
works, would make the next release 4.0.0. A new optional argument to_.map
would make the next release 3.10.0. Fixing a bug that wasn't handling certain corner-case in_.find()
would make the next release 3.9.3.
Not every module follows Semantic Versioning. A module might use a three-part version number, but increment it as they like. The safest way is to check the module documentation.
Giving npm permission to install newer version
When executing npm install
on a fresh checkout without existing node_modules, npm downloads and installs a version that satisfies package.json for each dependency. Instead of specifying the exact version to be installed in package.json, npm allows you to widen the range of accepted versions. You can allow a newer patch level version with tilde (~) and newer minor or patch level version with caret (^).
Symbol | Dependency | Versions | Changes |
---|---|---|---|
caret (^) | ^3.9.2 | 3.*.* |
|
tilde (~) | ~3.9.2 | 3.9.* |
|
Depending on too old version
When deciding whether to allow patch or minor level newer versions, it is important to note that old versions usually don't receive patches. The most common branch that gets a bug fix is the latest stable branch, and it is not that often that they are backported for older versions.
Let's say you are using version 1.3.4 of a library with a tilde (~) as your dependency. The library is well maintained and advances quickly. Today the latest version is actually 1.5.1. Then a mission-critical bug is discovered that affects every version to date. The bug gets proper treatment and is soon fixed in version 1.5.2. Now it is likely that there will be no tailored bug fix patch release 1.3.5 for your version, and you end up not getting that bug fix.
Updating modules with npm
When executing npm install
on a fresh checkout without existing node_modules, everything works nicely. The package.json file is evaluated, and satisfying versions are installed for each dependency. Things change when you already have node_modules
populated. Running npm install
will not re-check if there's an even newer version available than you already have installed.
There is a dedicated command npm update
for checking and installing newer versions satisfying semver pattern in package.json
.
Or, if you want to update a certain package to the latest version ignoring semantic versioning range, you can use install with the @latest tag.
Reproducible builds
When you use version number ranges instead of exact versions, there is one important consequence. You can't reproduce the build exactly as it went at a later time. The versions that get installed depend on the time you run npm install
. Tomorrow it may install different dependencies than it installs today. This is unsatisfactory for situations where you depend on reproducibility. This is the case for production deployments and public releases.
To make things reproducible, you need to replace version number ranges with exact versions. Npm provides a tool called npm shrinkwrap
for doing this.
Configuring npm defaults
The default behavior in npm is to use caret (^) when updating package.json. This default behavior can be configured with
You can also use --save-exact
flag to inform npm that exact match is desired instead of the default save prefix. You can make this latter option default with npm config set save-exact true
.
Related articles
Semantic Versioning Cheatsheet
Learn the difference between caret (^) and tilde (~) in package.json.