NodeJSSemantic Versioning (SemVer)

Semantic Versioning

Every version on npm is a semver string: MAJOR.MINOR.PATCH. The genius of the scheme is that the number itself communicates a promise about compatibility — so tools (and humans) can reason about which upgrades are safe. The range operators ^ and ~ then encode how much of that promise you are willing to trust automatically.

The three numbers

Position

Bumped when…

Promise

MAJOR (4.x.x)

You make a breaking change

Existing code may break — read the changelog

MINOR (x.19.x)

You add a backward-compatible feature

Safe to upgrade; new things added

PATCH (x.x.2)

You make a backward-compatible bug fix

Safe to upgrade; behavior only fixed

The contract
Semver is a *social contract*, not enforced by code. A well-behaved package bumps MAJOR for anything that could break callers, MINOR for additions, PATCH for fixes. When everyone honors it, you can safely auto-accept patches and minors while pinning majors. The range operators below let you express exactly that policy.
Range operators

Range

Means

Matches

Excludes

^1.2.3

Compatible — no MAJOR bump

1.2.3 → <2.0.0

2.0.0

~1.2.3

Approximately — no MINOR bump

1.2.3 → <1.3.0

1.3.0

1.2.3

Exact — this and only this

1.2.3

everything else

>=1.2.3

This or anything newer

1.2.3 → ∞

< 1.2.3

1.x / 1.*

Any version with MAJOR 1

1.0.0 → <2.0.0

2.0.0

*

Any version at all

everything

nothing

^ (caret) is npm's defaultnpm install express writes ^4.19.2. It means "let me get bug fixes and new features automatically, but never a breaking major."

Caret vs tilde, visually

Which updates each range allows

Text
Installed: 1.2.3      Available: 1.2.4  1.3.0  2.0.0

^1.2.3   ►  accepts 1.2.4 ✓   1.3.0 ✓   2.0.0 ✗   (locks MAJOR)
~1.2.3   ►  accepts 1.2.4 ✓   1.3.0 ✗   2.0.0 ✗   (locks MAJOR.MINOR)
 1.2.3   ►  accepts nothing — exact pin
The 0.x special case
For `0.x` versions, semver treats every release as potentially breaking, so `^` behaves like `~`: `^0.2.3` allows `0.2.x` but **not** `0.3.0`. The reasoning: pre-1.0 packages have unstable APIs, so a MINOR bump in `0.x` is allowed to break things. Don't assume `^0.5.0` will pick up `0.6.0` — it won't.
Ranges vs the lockfile

A range in package.json says what you allow; the lockfile records what you got. They work together:

Text
package.json:        "express": "^4.19.2"   ← the policy (allow 4.x ≥ 4.19.2)
package-lock.json:    express 4.19.2          ← the fact (exactly this, today)

Until you run npm update (or bump the range), the lock keeps you on 4.19.2 even after 4.20.0 ships. The range only matters when the lock is regenerated. See Understanding package-lock.json.

Pre-release and build tags

Versions can carry a pre-release suffix after a hyphen. These sort before the matching stable release and are excluded from normal ranges unless you opt in:

Text
2.0.0-alpha.1   <   2.0.0-beta.2   <   2.0.0-rc.1   <   2.0.0

# ^2.0.0 does NOT match 2.0.0-alpha.1 — you must ask explicitly:
npm install pkg@2.0.0-beta.2
dist-tags are named pointers
Beyond numbers, npm has *dist-tags* — human names pointing at a version. `latest` is the default `npm install` target; packages may also publish `next`, `beta`, or `canary`. `npm install react@next` grabs whatever `next` currently points to. Publish under a tag with `npm publish --tag beta` (see [Publishing Packages](/nodejs/publishing-packages)).
Testing a range

Bash
npx semver 4.20.0 -r "^4.19.2"     # prints 4.20.0 → it satisfies the range
npx semver 5.0.0  -r "^4.19.2"     # prints nothing → it does not
Practical policy
  • Default to ^ for most dependencies — automatic fixes and features, no surprise majors.

  • Use ~ when you want only patch fixes (conservative; common for fragile or fast-moving packages).

  • Use an exact pin for tools where reproducibility trumps everything (or rely on the lockfile, which pins anyway).

  • Treat a MAJOR bump as a task: read the migration guide, run tests — never auto-merge it.

Next
Versions and dependencies handled — now automate your workflow with the `scripts` field: [npm Scripts](/nodejs/npm-scripts).