"npm install --save" No Longer Using Tildes

February 20, 2014

A new stable version of Node was released last Wednesday, and with it came the newest version of npm. This update included a lot of big fixes, but the most visible change is that ‘install –save’ now prepends a caret (^) instead of a tilde (~).

What does this mean for you? Well, first you should understand the difference between the two. In the simplest terms, the tilde matches the most recent minor version (the middle number). ~1.2.3 will match all 1.2.x versions but will miss 1.3.0. This has been the default behavior of ‘–save’ since the start, and you are probably already comfortable seeing it in your package.json. The caret, on the other hand, is more relaxed. It will update you to the most recent major version (the first number). ^1.2.3 will match any 1.x.x release including 1.3.0, but will hold off on 2.0.0. npm’s semantic versioning parser clarifies the distinction:

~1.2.3 := >=1.2.3-0 <1.3.0-0 “Reasonably close to 1.2.3”.
^1.2.3 := >=1.2.3-0 <2.0.0-0 “Compatible with 1.2.3”.

― isaacs/node-semver (emphasis added)

The difference between “reasonably close” and “compatible” can be traced back to semantic versioning (SemVer) semantics. From the spec:

Given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards-compatible manner, and
  • PATCH version when you make backwards-compatible bug fixes.

― semver.org

Only major versions are allowed to break compatibility, so a developer should be able to switch between minors and patches with ease. The caret trusts that this promise is kept, but the tilde is a little more pessimistic.

Of course every rule has an exception, and semver’s lies in pre-release versioning:

Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.

― semver.org

Unlike 1.0 and above, 0.x software has only one promise: “Anything can change at any time.” While early developers are now free to rapidly increment versions without constraint, most will still maintain a basic level of sanity. Patches are still commonly reserved for bug fixes & internal changes, while the minor version is used for everything else.

npm’s semver parser relies on this sanity, so the tilde’s behavior for matching “reasonably close” versions can remains unchanged. The caret, however, won’t work with this new rule. To maintaing compatibility, it must be similarly conservative and only match the minor version. So for version zero software, the caret behaves like the tilde. Both ~0.1.2 and ^0.1.2 will match the most recent 0.1 software, but ignore 0.2.x since it could be incompatible.

You probably won’t notice anything immediately, and your package.json file is still fine as it is. But next time you save a dependency you may notice a caret has crept into your package.json.

Eventually you may want to consider switching all dependencies to one or the other, or even just a select few. But this decision is entirely up to you, and should be based on your use case, the packages themselves, and just how much risk you’re willing to take on.