Semantic versioning (SemVer) is a versioning convention in which a release number is written as MAJOR.MINOR.PATCH, and each part is incremented according to strict rules so the number itself communicates the nature of the changes: incompatible changes, new backward-compatible features, or backward-compatible bug fixes. The goal is to replace arbitrary version numbers with a shared contract, so that a consumer can look at two versions and reason about whether upgrading is safe. SemVer underpins dependency resolution in package managers across nearly every modern ecosystem.
Key takeaways
- A SemVer version is
MAJOR.MINOR.PATCH, for example2.4.1. - Increment MAJOR for breaking changes, MINOR for backward-compatible features, PATCH for backward-compatible fixes.
- A leading
0.y.zmeans unstable initial development with no compatibility promises. - A pre-release tag follows a hyphen (
1.0.0-rc.1); build metadata follows a plus (1.0.0+build.5). - Precedence compares numeric fields first, and a pre-release ranks below its normal release.
- Range operators like caret and tilde let dependents accept compatible upgrades automatically.
What do MAJOR, MINOR, and PATCH mean?
The three mandatory numbers each answer a different question about a release, and they are always non-negative integers without leading zeros.
| Component | Increment when | Effect on consumers |
|---|---|---|
MAJOR |
You make an incompatible API change | May require code changes to upgrade |
MINOR |
You add functionality in a backward-compatible way | Safe to upgrade; new features available |
PATCH |
You make a backward-compatible bug fix | Safe to upgrade; behavior otherwise unchanged |
When you increment a higher-order component, the lower ones reset to zero. Fixing a bug on 1.4.7 yields 1.4.8; adding a feature yields 1.5.0; a breaking change yields 2.0.0.
Why does SemVer depend on a public API?
SemVer’s entire meaning rests on the notion of a declared public API. A change counts as breaking only relative to what you have promised is public. That is why SemVer expects you to document the public surface: the documentation is the boundary that decides whether a given change is a MAJOR, MINOR, or PATCH event.
The practical implication is that internal refactors, private helpers, and undocumented behavior are not part of the contract. If consumers depend on undocumented internals, breaking those is technically allowed under SemVer, though good maintainers still communicate such changes.
What is special about 0.y.z versions?
Any version whose MAJOR component is 0 is in initial development. During this phase the rules are relaxed: anything may change at any time, and the public API should not be considered stable. Consumers who depend on a 0.y.z release should pin it tightly, because even a minor bump can introduce breaking changes.
Reaching 1.0.0 is a deliberate signal that the API is now stable and that the full SemVer guarantees apply from that point forward.
How do pre-release and build metadata work?
SemVer allows two optional suffixes, and they behave very differently.
Pre-release identifiers
A pre-release version appends a hyphen followed by dot-separated identifiers, such as 1.0.0-alpha.1 or 2.0.0-rc.2. Pre-releases denote a version that is not yet ready for general use and that has lower precedence than the corresponding normal release.
Build metadata
Build metadata appends a plus sign and dot-separated identifiers, such as 1.0.0+20130313144700. Build metadata is purely informational and is ignored entirely when determining precedence, so two versions that differ only in build metadata are considered equal for ordering.
1.0.0-alpha pre-release, lowest
1.0.0-alpha.1 later pre-release
1.0.0-beta later still
1.0.0-rc.1 release candidate
1.0.0 final release, highest
1.0.0+build.7 same precedence as 1.0.0
How is version precedence calculated?
To order two versions, compare their fields left to right:
- Compare
MAJOR, thenMINOR, thenPATCHnumerically. - If all three are equal, a version with a pre-release tag has lower precedence than the same version without one.
- When both have pre-release tags, compare the dot-separated identifiers one by one; numeric identifiers compare numerically, and identifiers with letters compare lexically, with a larger set of fields winning when all shared fields are equal.
- Ignore build metadata completely.
This deterministic ordering is what lets package managers decide which of two candidate versions is newer.
How do version ranges use SemVer?
Package managers let dependents accept a range of compatible versions rather than a single pinned one, and these ranges are built on SemVer’s guarantees. Two of the most common operators are the caret and the tilde.
| Range | Allows | Blocks |
|---|---|---|
^1.2.3 |
Updates that keep the left-most non-zero component, so up to but not including 2.0.0 |
A new MAJOR release |
~1.2.3 |
PATCH-level updates within 1.2.x |
A new MINOR or MAJOR release |
1.2.3 |
Exactly that version | Any other version |
Note the caret’s special handling of zero-based versions: because 0.y.z is unstable, a caret on such a version behaves more conservatively, restricting updates so that a change in the left-most non-zero component is treated as breaking.
What are common SemVer mistakes?
- Bumping MINOR for a breaking change. If existing code can break, it is a MAJOR change, full stop.
- Treating a bug fix that changes documented behavior as a PATCH. If behavior consumers relied on changes, it may be breaking.
- Adding features in a PATCH. New functionality belongs in a MINOR release.
- Reusing a published version. Once released, a version’s contents must never change; publish a new number instead.
- Relying on build metadata for ordering. It does not affect precedence, so never encode meaningful sequence there.
Frequently asked questions
What do the three numbers in SemVer mean?
In MAJOR.MINOR.PATCH, MAJOR increments for incompatible API changes, MINOR for backward-compatible new features, and PATCH for backward-compatible bug fixes.
What does a 0.y.z version signify?
It marks initial development where anything may change at any time. The public API is not considered stable, so consumers should not rely on backward compatibility.
Are build metadata and pre-release tags the same?
No. A pre-release tag after a hyphen affects precedence and marks unstable versions, while build metadata after a plus sign is ignored for precedence entirely.
How is version precedence determined?
Compare MAJOR, then MINOR, then PATCH numerically. If those are equal, a version with a pre-release tag ranks lower than the same version without one.
What does the caret range mean in npm?
A caret allows updates that do not change the left-most non-zero version component, so it typically permits new MINOR and PATCH releases but not a new MAJOR.
Should I increment MINOR or PATCH for a bug fix?
Increment PATCH for a backward-compatible bug fix. Reserve MINOR for adding backward-compatible functionality such as a new optional feature.
Does SemVer require the API to be documented?
Yes, effectively. SemVer’s guarantees depend on a declared public API, so a change is breaking or not relative to that documented API surface.