---
topic: dev-practices
author: Crashtech Editorial
published: Nov 14, 2026 · read: 3 min
---

Semantic Versioning in Practice: What MAJOR.MINOR.PATCH Actually Promises

A version number is a contract about breakage. When to bump each part, how 0.x, pre-release tags and build metadata work, and where SemVer falls short.

– –

A version number is not decoration. It is a sentence to everyone who depends on you: “upgrading is safe”, “upgrading adds things”, or “upgrading may break you”. Semantic versioning is the shared grammar for that sentence.

The three numbers

MAJOR . MINOR . PATCH
  2   .   4   .   1
PartRaise it whenConsumer reaction
PATCHYou fix a bug without changing the public APIUpgrade freely
MINORYou add functionality, backward compatibleUpgrade, optionally use the new feature
MAJORYou make an incompatible change to the public APIRead the notes, plan the migration

The rule that surprises people: when a higher part goes up, every lower part resets to zero. After 1.4.7, a breaking change is 2.0.0.

A release timeline

  1. 0.1.0: initial development

    Everything may change at any time. Nobody should depend on stability yet.

  2. 1.0.0: the first stable release

    You are declaring a public API and promising to evolve it by the rules below. Reaching 1.0.0 is a decision, not a date.

  3. 1.0.1: a bug fix

    Same behaviour, defect corrected. Patch.

  4. 1.1.0: a new backward-compatible feature

    A new endpoint, option or function. Existing callers are untouched. Minor.

  5. 2.0.0: an incompatible change

    A removed field, a changed return type, a renamed function. Existing callers can break. Major.

Pre-releases and build metadata

A hyphen introduces a pre-release, a plus sign introduces build metadata.

1.0.0-alpha < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0
1.0.0+20130313144700     (build metadata: ignored when ordering)

A pre-release sorts before the release it precedes, so release candidates are never mistaken for the final version. Build metadata records provenance, such as a build timestamp or commit, and two versions that differ only there are considered equal in precedence.

What counts as the public API?

SemVer only means something once you have declared the public surface: HTTP endpoints, exported functions, CLI flags, config keys, database migrations that consumers rely on. Changing anything outside that surface is not a breaking change, however much it hurts someone who relied on it anyway. Write the surface down.

Advertisement

Classifying a change honestly

Most arguments about version bumps are arguments about classification.

ChangeBumpWhy
Fix a crash in an existing functionPATCHBehaviour now matches the documentation
Add an optional parameter with a defaultMINORExisting calls still work
Add a new response fieldMINORTolerant clients ignore it, but strict ones may break: document it
Make a previously optional parameter requiredMAJORExisting calls now fail
Remove or rename a public functionMAJORExisting callers break
Tighten validation so previously accepted input is rejectedMAJORBehaviour contract changed
Deprecate something but keep it workingMINORStill compatible, but announce removal

Do

Deprecate in a MINOR release with a clear removal version, keep a changelog that names every breaking change, and run an API diff in CI so a breaking change cannot ship as a patch by accident.

Don't

Ship a breaking fix as a patch because “it was a bug anyway”. Someone’s build depended on the old behaviour, and the version number just told them it was safe.

Where SemVer falls short

  • It trusts the author’s judgment about what is breaking. There is no enforcement in the spec.
  • Large projects bump MAJOR so rarely, or so often, that the number stops carrying signal. Some projects prefer calendar versioning, which tells you age instead of risk.
  • It says nothing about how big a MAJOR change is. 2.0.0 might rename one function or rewrite the architecture.
  • Dependency ranges (^1.2.0, ~1.2.0) rely entirely on authors getting it right, which is why lockfiles exist.

Takeaway

Use SemVer as a promise you can keep: declare your public API, classify every change against it, reset lower numbers on a bump, and let a changelog and an automated diff keep you honest. The number is cheap to write and expensive to get wrong, because someone else’s build is trusting it.

Advertisement

Frequently asked questions

What do MAJOR, MINOR and PATCH mean in semantic versioning?

MAJOR increases for incompatible API changes, MINOR for backward-compatible new functionality, and PATCH for backward-compatible bug fixes. When a higher part increases, the lower parts reset to zero, so 1.4.7 followed by a breaking change becomes 2.0.0, not 2.4.7.

What does a version below 1.0.0 mean?

Versions starting with 0 are for initial development, where anything may change at any time and the public API should not be considered stable. Releasing 1.0.0 is the declaration that you have a public API and will follow the rules for changing it.

How are pre-release and build metadata versions ordered?

A pre-release such as 1.0.0-rc.1 sorts lower than 1.0.0, and pre-release identifiers compare in order. Build metadata after a plus sign, as in 1.0.0+20130313, is ignored when determining precedence, so two versions differing only in build metadata are equal.

Is SemVer enough to prevent breaking changes?

No. It only works if authors correctly classify their changes, and what counts as breaking is a judgment about the public API. Compatibility tests, API diffing tools and changelogs catch the cases where a maintainer calls a breaking change a patch.

Sources & further reading

/* Comments */