Les pubs vous déplaisent ? Aller Sans pub Auj.

Semantic Versioning The Numbering System Your npm install Depends On

Mis à jour le

Semver's three numbers are a contract. MAJOR breaks, MINOR adds, PATCH fixes — and when your build breaks after npm install, nine times out of ten someone ignored that contract. Here's how the numbering system works, what ^ and ~ actually do in package.json, and why committing your lockfile is non-negotiable.

Semantic Versioning: The Numbering System Your npm install Depends On 1
ANNONCE · Supprimer ?

Your build broke. Worked on Friday. npm install on Monday pulled in react-query@5 and now half your hooks are gone. You’re staring at a stack trace that wasn’t there before, and somewhere a changelog is collecting dust.

This is a semver story. Specifically, it’s your fault.

What the three numbers actually mean

MAJOR.MINOR.PATCH — that’s it. Three slots, three rules:

  • PATCHER (1.2.3 → 1.2.4): Bug fix. Nothing in your code needs to change. You just get less broken behavior.
  • MINOR (1.2.3 → 1.3.0): New feature added, backward-compatible. You don’t have to use it, but it’s there.
  • MAJOR (1.2.3 → 2.0.0): Something broke. A function was renamed, removed, or changed signature. The old API is gone or works differently.

The key word in all three is backward-compatible. MINOR and PATCH are promises: “we didn’t break anything you were already using.” MAJOR is a warning: “we did.”

When a maintainer bumps MAJOR and you didn’t notice because you pinned ^1.0.0 in package.json and the lockfile was stale — that’s on you. The spec worked exactly as designed.

The semver social contract

Semver is a convention, not a law. Packages can claim to follow it and then ship a MINOR with breaking changes. When that happens, it’s bad faith on the maintainer’s part. But when a package properly bumps MAJOR to signal breakage and you pull it in blindly — you’re the one who broke your own build.

This is why changelogs exist. A CHANGELOG.md entry that says “Removed deprecated v1Api — use v2Api instead” is the maintainer holding up their end. Not reading it is you ignoring yours. The changelog is a two-minute read. The debugging session it prevents is not.

^ vs ~ — the actual mechanics

Dans package.json, ^ (caret) and ~ (tilde) define version ranges. They look similar and behave very differently.

Caret (^): Allows anything that doesn’t bump MAJOR. This is npm’s default when you run npm install some-package.

  • ^1.2.3 resolves to >=1.2.3 <2.0.0
  • ^0.2.3 resolves to >=0.2.3 <0.3.0 — special case: 0.x treats MINOR as breaking
  • ^0.0.3 resolves to >=0.0.3 <0.0.40.0.x pins exactly, no wiggle room

Tilde (~): Allows PATCH updates only, within the specified MINOR.

  • ~1.2.3 resolves to >=1.2.3 <1.3.0
  • ~1.2 resolves to >=1.2.0 <1.3.0 — same as ~1.2.0
  • ~1 resolves to >=1.0.0 <2.0.0 — equivalent to ^1.0.0 at this point

Version range examples

GammeWhat it allowsConcrete matches
1.2.3Exactly this versionOnly 1.2.3
^1.2.3Any MINOR/PATCH ≥ 1.2.31.2.4, 1.3.0, 1.99.0 — NOT 2.0.0
^0.2.3PATCH within 0.2.x only0.2.4, 0.2.99 — NOT 0.3.0
~1.2.3PATCH within 1.2.x only1.2.4, 1.2.99 — NOT 1.3.0
~1.2Any patch of 1.2.x1.2.0, 1.2.1, 1.2.99
>=1.2.3 <2.0.0Explicit rangeSame result as ^1.2.3
1.2.xAny patch of 1.21.2.0, 1.2.1, 1.2.99
*Anything at allWhatever npm feels like installing today

Le * range is a “trust me bro” versioning strategy. You’re pinning nothing. If a library ships v9.0.0 with a fully rewritten API, you’ll get it on the next npm install with a clean cache. Use it only in top-level applications that are never depended on by other packages — and even then, only if reproducibility genuinely doesn’t matter to you (it does).

Pre-release identifiers

Before a stable release, maintainers tag versions with a pre-release label:

  • 1.0.0-alpha.1 — early, unstable, API is probably still changing
  • 1.0.0-beta.2 — feature-complete, still being tested, expect some rough edges
  • 1.0.0-rc.1 — release candidate, should be production-ready unless something turns up in final testing

Pre-releases sort below the stable release: 1.0.0-alpha.1 < 1.0.0. And critically, ^1.0.0 will pas install 2.0.0-beta.1 — pre-releases only match if you specify them explicitly in your range. This is the behavior that stops you from accidentally opting into an alpha when you meant to track stable releases.

If you’re consuming a package that only has pre-releases, pin the full version string: "some-package": "1.0.0-beta.2". Don’t use ^ ou ~ with pre-releases unless you know the maintainer treats them carefully — most don’t.

Checking a range before you commit it

Before pinning a version range in package.json, it’s worth confirming what you’re actually agreeing to install. The Semver Version Calculator takes a version range and a list of candidate versions and shows you which ones match — useful when you’re uncertain whether ~2.3 covers a specific version you need, or when you’re reviewing a PR and the range looks off.

The three failure modes

Most semver-related build failures follow one of three patterns:

  1. ^ + MAJOR bump + deleted lockfile: You pinned ^1.0.0, maintainer shipped 2.0.0, the lockfile was deleted or never committed, CI installs 2.0.0. Fix: commit your lockfile. Every project. No exceptions.
  2. * in a library you publish: You’re a library author who used * for a dependency. Every downstream user of your package inherits your wildcard. You’ve made their dependency graph your problem. Fix: use explicit ranges in anything you ship to npm.
  3. Pre-release without a lockfile: A loose range pulled in 1.0.0-alpha.3, the API changed from alpha.1, nothing works. Fix: pin pre-releases explicitly and — say it with me — commit the lockfile.

Read the changelog

When a MAJOR version ships for anything in your dependency tree, spend two minutes on the changelog. The maintainers wrote it so you wouldn’t have to reverse-engineer the breakage from a stack trace at 3am.

If a library ships breaking changes under a MINOR bump with no changelog — that’s bad faith. File an issue. Call it out publicly. But if the MAJOR was clearly there, the migration guide was detailed, and you pulled it in without looking: the tooling did exactly what you told it to. The contract was written in three numbers. You just didn’t read it.

Envie d'une expérience sans pub ? Passez à la version sans pub

Installez nos extensions

Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide

Sur Extension Chrome Sur Extension de bord Sur Extension Firefox Sur Extension de l'opéra

Le Tableau de Bord Est Arrivé !

Tableau de Bord est une façon amusante de suivre vos jeux, toutes les données sont stockées dans votre navigateur. D'autres fonctionnalités arrivent bientôt !

ANNONCE · Supprimer ?
ANNONCE · Supprimer ?
ANNONCE · Supprimer ?

Coin des nouvelles avec points forts techniques

Impliquez-vous

Aidez-nous à continuer à fournir des outils gratuits et précieux

Offre-moi un café
ANNONCE · Supprimer ?