The three numbers
Semantic Versioning 2.0.0 requires a normal version to have the form X.Y.Z, three non-negative integers without leading zeros: major, minor and patch. Once a version is released, its contents must not be changed; any fix ships as a new version. The meaning comes from what changed in the public API:
- Patch (1.4.2): only backward compatible bug fixes.
- Minor (1.5.0): new backward compatible features, or a feature marked as deprecated. The patch resets to 0.
- Major (2.0.0): any change that breaks backward compatibility. Minor and patch reset to 0.
So from 1.4.2, a bug fix gives 1.4.3, a new option gives 1.5.0, and removing a function gives 2.0.0. The specification requires software that uses it to declare a public API, in the code or only in documentation, because compatibility is measured against it. The specification says 1.0.0 is the version that defines that API.
Picking the next version
Look at everything merged since the last release and let the biggest change decide. Starting from 3.7.2:
- Two bug fixes and a faster query, nothing new in the API: 3.7.3.
- The same fixes plus a new optional parameter: 3.8.0. The fixes ride along; the patch resets.
- An old function marked as deprecated but still working: also a minor release, 3.8.0.
- That deprecated function finally removed: 4.0.0, even if it is the only change.
A big release can go through pre-releases first, for example 4.0.0-alpha.1, 4.0.0-beta.1, 4.0.0-rc.1 and then 4.0.0. Each of those sorts below 4.0.0 and above 3.8.0.
What 0.y.z means
Rule 4 is short: major version zero is for initial development, and anything may change at any time. A 0.y.z package makes no compatibility promise at all. The semver.org FAQ suggests starting at 0.1.0 and raising the minor version with each release. It also says that software already used in production, or with users who depend on its API, should probably be at 1.0.0 already. npm's own page on semantic versioning simply advises a new package to start with 1.0.0.
Pre-release and build metadata
A pre-release is marked by a hyphen and dot-separated identifiers after the patch number: 1.0.0-alpha, 1.0.0-beta.2, 1.0.0-rc.1, 1.0.0-0.3.7. Identifiers may contain only ASCII letters, digits and hyphens, and numeric ones must not have leading zeros. A pre-release has lower precedence than the release it leads up to and may not be stable.
Build metadata follows a plus sign: 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85. It records how the build was made, and the specification says it must be ignored when deciding precedence. Two versions that differ only in build metadata rank the same.
To check a version string, semver.org suggests this regular expression, the variant with numbered groups that works in JavaScript. Paste it into the Regex Tester with a few versions to see the five groups: major, minor, patch, pre-release and build.
^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ How versions sort
Rule 11 compares major, minor and patch as numbers, left to right. If all three are equal, a pre-release ranks below the plain version. Two pre-releases are compared identifier by identifier: digits-only identifiers numerically, others in ASCII order, a numeric identifier always below a non-numeric one, and if all earlier identifiers match, the longer list wins. Sorting the shuffled list 1.0.0, 1.10.0, 1.0.0-rc.1, 1.0.0-beta.11, ... by these rules gives, from lowest to highest:
1.0.0-alpha1.0.0-alpha.11.0.0-alpha.beta1.0.0-beta1.0.0-beta.21.0.0-beta.111.0.0-rc.11.0.01.0.0+20130313144700(same precedence as the one above, because build metadata is ignored)1.2.01.10.02.0.0
Plain text sorting gets several of these wrong, because it compares character by character. The table shows which version is higher by each method:
| Versions | SemVer higher | Text sort higher |
|---|---|---|
| 1.2.0 vs 1.10.0 | 1.10.0 | 1.2.0 |
| 1.0.0-beta.2 vs 1.0.0-beta.11 | 1.0.0-beta.11 | 1.0.0-beta.2 |
| 1.0.0-alpha vs 1.0.0-alpha.1 | 1.0.0-alpha.1 | 1.0.0-alpha.1 |
| 1.0.0-rc.1 vs 1.0.0 | 1.0.0 | 1.0.0-rc.1 |
| 1.0.0-1 vs 1.0.0-alpha | 1.0.0-alpha | 1.0.0-alpha |
| 1.0.0+build.9 vs 1.0.0+build.10 | Equal | 1.0.0+build.9 |
The last row shows build metadata in action: semver calls the two versions equal, while a text sort still picks one. When two changelogs or lock files disagree about which version is newer, Text Compare shows the changed lines side by side.
npm ranges: ^ and ~
A package.json dependency usually names a range, not a version. npm's documentation calls ~version "approximately equivalent to version" and ^version "compatible with version", and points to the node-semver library for the details. Its README defines them:
- Tilde allows patch-level changes if a minor version is given, and minor-level changes if not.
- Caret allows changes that do not modify the left-most non-zero number of major, minor and patch.
npm's save-prefix setting defaults to ^, so npm install saves a package found at 1.2.3 as ^1.2.3. Here is what each range accepts if the registry holds these versions: 0.0.3, 0.0.4, 0.2.3, 0.2.9, 0.3.0, 1.2.2, 1.2.3, 1.2.9, 1.3.0, 1.9.9, 2.0.0, 2.3.4, 2.4.0.
| Range | Same as | Allows | Matches |
|---|---|---|---|
| ^1.2.3 | >=1.2.3 <2.0.0-0 | Minor and patch updates | 1.2.3 to 1.9.9 (4) |
| ~1.2.3 | >=1.2.3 <1.3.0-0 | Patch updates only | 1.2.3 to 1.2.9 (2) |
| ^0.2.3 | >=0.2.3 <0.3.0-0 | Patch only, because major is 0 | 0.2.3 to 0.2.9 (2) |
| ^0.0.3 | >=0.0.3 <0.0.4-0 | That one version | 0.0.3 |
| 1.2.x or ~1.2 | >=1.2.0 <1.3.0-0 | Any 1.2 patch | 1.2.2 to 1.2.9 (3) |
| 1.x or ^1.0.0 | >=1.0.0 <2.0.0-0 | Any 1.y.z | 1.2.2 to 1.9.9 (5) |
| 1.2.3 - 2.3.4 | >=1.2.3 <=2.3.4 | Both ends included | 1.2.3 to 2.3.4 (6) |
| 1.2.3 | 1.2.3 | Exactly that version | 1.2.3 |
The trailing -0 in the README's expansions is the lowest possible pre-release of the upper bound: a numeric identifier 0 sorts before any other, so <2.0.0-0 shuts out 2.0.0 and every 2.0.0 pre-release too.
Pre-releases get one more rule. A range only matches a pre-release if one of its comparators has the same major, minor and patch with a pre-release tag of its own. The README's example: >1.2.3-alpha.3 matches 1.2.3-alpha.7 but not 3.4.5-alpha.9, even though the second sorts higher. That keeps ^1.2.3 from pulling in a 1.5.0-beta nobody asked for.
In practice
- Version numbers are a promise, not a check. Nothing stops a maintainer from shipping a breaking change in a minor release. With a package-lock.json, npm's documentation says
npm installuses the exact locked versions as long as they still satisfy the ranges in package.json, andnpm ciinstalls the lock file without changing either file. - Treat 0.y.z as unstable. The caret already narrows to patch updates there, but the spec allows breaking changes in any 0.y.z release.
- Tag releases with the plain number.
v1.2.3is a convention for git tags; the semantic version is1.2.3. The npm library strips a leading v, but other tools may not. - Keep version data valid. package.json is JSON; the JSON Formatter catches a stray comma before npm does, and the guide to JSON syntax errors lists the other usual mistakes.
Frequently asked questions
What do the three numbers in 2.4.1 mean?
Major 2, minor 4, patch 1. Semantic Versioning 2.0.0 says the patch goes up for backward compatible bug fixes, the minor for backward compatible new features, and the major for changes that break the public API. When one number goes up, the ones to its right reset to 0.
What is the difference between ^ and ~ in package.json?
The caret allows every update that keeps the left-most non-zero number, so ^1.2.3 accepts anything from 1.2.3 up to but not including 2.0.0. The tilde allows patch updates when a minor version is given, so ~1.2.3 accepts 1.2.3 up to but not including 1.3.0. npm writes ^ by default when it saves a dependency.
Why does ^0.2.3 not accept 0.3.0?
Because the caret protects the left-most non-zero number, and in 0.2.3 that is the 2. The rule follows the spec: in major version zero anything may change at any time, so a jump from 0.2 to 0.3 is treated as possibly breaking. For ^0.0.3 the left-most non-zero number is the patch, so only 0.0.3 itself matches.
Is v1.2.3 a valid semantic version?
No. The semver.org FAQ says the semantic version is 1.2.3 and the v is only a common prefix, for example in git tags. The npm semver library strips a leading v or = when it parses a version, and its README says that habit is kept only for compatibility with SemVer 1.0.0.
Is 1.0.0-rc.1 newer than 1.0.0?
No, it is older. A pre-release has lower precedence than the normal version with the same three numbers, so every alpha, beta and rc of 1.0.0 sorts before 1.0.0 itself. Build metadata after a plus sign is ignored entirely when comparing.
Sources
- Semantic Versioning 2.0.0, semver.org (rules 2 to 11: X.Y.Z with non-negative integers and no leading zeros, released versions must not change, 0.y.z for initial development, 1.0.0 defines the public API, when to increment and reset, pre-release and build metadata syntax, the precedence rules and their example ordering; the FAQ on 0.1.0, on when to release 1.0.0, on the v prefix and the suggested regular expression)
- npm, node-semver README (tilde, caret, X-range and hyphen range expansions such as ^1.2.3 to >=1.2.3 <2.0.0-0; the pre-release tag rule with >1.2.3-alpha.3; a leading v or = is stripped)
- npm Docs: About semantic versioning (the release types table starting with 1.0.0, and 1.0.x, ~1.0.4, 1.x, ^1.0.4 and * for patch, minor and major updates)
- npm Docs: package.json dependencies (the list of range forms: exact version, comparison operators, ~ approximately equivalent, ^ compatible with, 1.2.x, *, hyphen ranges and ||)
- npm Docs: npm install (with a lock file, the locked versions are used if they satisfy the package.json ranges; npm ci keeps both files in sync)
- npm Docs: config, save-prefix (default ^, applied to versions saved with --save or --save-dev)