¿Odias los anuncios? Ir Sin publicidad Hoy

Semantic Versioning What the Numbers in Your package.json Actually Mean

Actualizado en

Every package.json has version strings, but most developers just trust the caret without knowing what it allows. This guide explains the MAJOR.MINOR.PATCH contract and exactly which updates ^, ~, >=, and * will accept.

Semantic Versioning: What the Numbers in Your package.json Actually Mean 1
ANUNCIO · ¿ELIMINAR?

Every JavaScript project has a package.json. Most developers have typed npm install some-library hundreds of times without giving the version string a second thought. But ask someone what ^1.2.3 actually allows — as in, which versions npm will happily pull in — and you’ll often get a shrug.

This isn’t a trivial gap. A misunderstood version range is how a routine npm install on a fresh machine suddenly breaks a CI pipeline that worked fine for months. Understanding the contract behind those numbers is one of those low-effort, high-payoff things that separates developers who debug version issues in minutes from those who spend hours on them.

The MAJOR.MINOR.PATCH Contract

Semantic versioning (semver) is a three-number format: MAJOR.MINOR.PATCH. Each position carries a specific promise about what changed:

  • MAJOR — breaking changes. Upgrading across a major version may require you to update your code.
  • MINOR — new features, backwards-compatible. Your existing code should keep working.
  • PATCH — bug fixes only. Safe to upgrade; no API changes.

That’s the contract packages publish under. Jumping from 2.3.1 a 2.4.0 should add new features without breaking existing behavior. Jumping to 3.0.0 is your warning shot: read the changelog before upgrading.

In practice, maintainers sometimes slip up and sneak a breaking change into a minor release. Semver doesn’t enforce itself — it’s a convention. But it gives you a framework to reason about risk, and it’s what all the range operators are built on.

What Counts as a Breaking Change?

A breaking change is anything that forces consumers to update their code:

  • Removing or renaming an exported function, class, or constant
  • Changing a function’s signature — removing parameters, adding required ones, or changing return types
  • Changing observable behavior in a way that correct calling code now behaves wrong
  • Changing a configuration file format in an incompatible way

Adding a new optional parameter? That’s MINOR. Adding a new export? MINOR. Fixing a bug that someone was accidentally relying on as a feature? That’s a judgment call, but conventionally PATCH. When in doubt, bump MINOR and document it clearly.

Version Range Operators in package.json

Open any package.json and you’ll see version strings like "^4.17.21" o "~1.0.4". These aren’t exact pins — they’re ranges that tell npm or yarn which versions are acceptable when resolving your dependency tree.

Caret ^ — Compatible with Version

The caret is the default operator when you run npm install. It means: “accept any version that doesn’t change the leftmost non-zero digit.” In practice, for stable packages, that means same-major, any minor or patch:

^1.2.3  →  >=1.2.3 <2.0.0   (any 1.x.x at or above 1.2.3)
^0.2.3  →  >=0.2.3 <0.3.0   (zero-major: pins to minor)
^0.0.3  →  >=0.0.3 <0.0.4   (zero-zero: pins to exact patch)

The zero-major behavior is intentional. Packages at 0.x.x signal "unstable API" — any minor version might break things. The caret respects that signal and becomes much more conservative.

Tilde ~ — Patch-Level Updates Only

The tilde is more conservative. It accepts new patches but won't touch the minor version:

~1.2.3  →  >=1.2.3 <1.3.0   (any 1.2.x at or above 1.2.3)
~1.2    →  >=1.2.0 <1.3.0
~1      →  >=1.0.0 <2.0.0   (when only major given — same as ^1)

Reach for ~ when you want bug fixes but you're not confident the library respects semver for minor bumps, or when your code is tightly coupled to a specific API surface and a new feature release historically comes with subtle behavior changes.

Comparison Operators: >=, <=, >

You can write arbitrary ranges using comparison operators. A space between two constraints means AND:

>=1.2.0           # at least 1.2.0, no upper bound
>=1.2.0 <2.0.0   # same as ^1.2.0 (explicit AND)
>1.2.0 <=1.5.0   # between these values, exclusive/inclusive

These show up most often in peerDependencies, where a library says something like "react": ">=16.8.0 <19.0.0" to declare which host versions it's compatible with.

Wildcards: * and x

The wildcard forms are mostly for documentation readability; npm treats them as equivalent to caret/tilde with a zero base:

  • * o "" — any version. Do not use this in production.
  • 1.x o 1.x.x — any 1.x.x (same as ^1.0.0)
  • 1.2.x — any 1.2.x (same as ~1.2.0)

Pre-Release Versions

Pre-release versions look like 1.0.0-alpha.1, 2.0.0-beta.3, o 3.1.0-rc.1. They're considered lower than the release with the same numbers — 1.0.0-alpha.1 < 1.0.0.

Critically, range operators don't automatically include pre-releases. A ^1.0.0 range will not pull in 1.1.0-beta.1, even though it technically satisfies >=1.0.0 <2.0.0. You have to explicitly opt in:

npm install some-library@next
npm install some-library@2.0.0-beta.3

This is intentional safety. You'd rarely want CI to silently pick up an -alpha build of a dependency because it happens to satisfy a version range. If you're testing pre-releases, do it deliberately.

Lockfiles Are Not Optional

When npm resolves your ^1.2.3 range, it picks the highest compatible version available at that moment. Run npm install today and you get 1.5.0. Run it six months later and you might get 1.9.2. Same package.json, different dependency tree, potentially different behavior.

That's what lockfiles solve. package-lock.json (npm) and yarn.lock (yarn) record the exact version installed for every dependency — direct and transitive. When someone else clones your repo and runs npm ci, they get an identical tree.

Commit your lockfile. Always. Not committing it means:

  • Different developers may run different dependency versions without knowing it
  • Your CI environment can silently drift from your local environment
  • A transitive dependency update can change production behavior with no visible change in your git diff

The main exception: published libraries (not applications) conventionally leave lockfiles uncommitted so that consumers can resolve their own dependency tree. If you're building an app, there's no good reason to leave the lockfile out of source control.

"latest" in package.json Is Always a Mistake

Occasionally you'll see this in a package.json:

"dependencies": {
  "some-package": "latest"
}

Don't do this. "latest" maps to whatever is currently tagged latest on npm — it changes whenever the maintainer publishes a new release. A fresh npm install on any new machine could pull a completely different major version from what you tested against.

It might work fine for weeks, then silently break when the package ships a new major. Worse, it makes package.json useless as a reproducible spec — you can't reason about what version you're running without manually checking npm. Pin to a real version and let the caret handle safe updates within that range.

Check Whether a Version Satisfies a Range

If you're not sure whether a version matches a given range — especially with zero-major packages or unusual compound expressions — the Calculadora de versiones Semver y verificador de rangos on iotools.cloud gives you an immediate answer. Enter the range (^1.2.3, ~0.5.0, >=2.0.0 <3.0.0) and the candidate version, and it tells you whether the constraint is satisfied.

This is useful when reviewing dependency upgrade PRs, debugging why npm install resolved to an unexpected version, or double-checking a peerDependencies range before publishing a library.

Guía rápida

OperatorEjemploResolves to
^^1.2.3>=1.2.3 <2.0.0
~~1.2.3>=1.2.3 <1.3.0
>=>=1.2.01.2.0 or higher, no cap
**Any version (avoid)
x1.2.xAny 1.2.x patch
exact1.2.3Exactly 1.2.3
¿Quieres eliminar publicidad? Adiós publicidad hoy

Instalar extensiones

Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas

añadir Extensión de Chrome añadir Extensión de borde añadir Extensión de Firefox añadir Extensión de Opera

¡El marcador ha llegado!

Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!

ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?

Noticias Aspectos técnicos clave

Involucrarse

Ayúdanos a seguir brindando valiosas herramientas gratuitas

Invítame a un café
ANUNCIO · ¿ELIMINAR?