caduh

Semantic Versioning (SemVer) Explained in 3 Minutes

3 min read

MAJOR.MINOR.PATCH — what each digit means, how pre‑releases work, and the practical rules you need for safe dependency upgrades.

TL;DR

  • Versions are MAJOR.MINOR.PATCH (1.2.3).
    • MAJOR: breaking API changes.
    • MINOR: new, backward‑compatible features.
    • PATCH: backward‑compatible bug fixes.
  • Pre‑releases add -alpha.1, -beta.2, -rc.1 and sort before the final release. Build metadata +meta is ignored for precedence.
  • For libraries: don’t break on MINOR/PATCH. For apps: be stricter than ranges; rely on lockfiles and tested upgrades.
  • 0.x is unstable: treat MINOR as potentially breaking. Reach 1.0.0 once your API is dependable.

1) What counts as “the API”?

Anything your users depend on: exported functions/classes, CLI flags/exit codes, config keys, environment variables, HTTP endpoints and payloads, file formats, side effects. If changing it can break existing consumers, it’s a breaking changeMAJOR.


2) Pre‑release & build metadata (the small print)

1.4.0-alpha.2   <  1.4.0-beta.1  <  1.4.0-rc.1  <  1.4.0
1.4.0+build.7   == 1.4.0          # +meta doesn’t affect ordering

Use pre‑releases for testing with early adopters. Don’t promise stability until the final .0 is out.


3) Version ranges you’ll actually see

| Notation | Means (roughly) | Good for | |---|---|---| | ^1.2.3 | >=1.2.3 <2.0.0 | Auto‑get compatible MINOR/PATCH | | ~1.2.3 | >=1.2.3 <1.3.0 | Only PATCHes (and tiny MINOR in some ecosystems) | | 1.2.x | >=1.2.0 <1.3.0 | Lock to a MINOR line | | >=1.2.3 <2 | Explicit range | Precise policies | | * / latest | anything | Don’t do this in prod |

0.x special case (npm‑style):

  • ^0.2.3>=0.2.3 <0.3.0 (caret stops at next MINOR).
  • ^0.0.3>=0.0.3 <0.0.4 (caret stops at next PATCH).

Libraries should prefer narrow ranges and test matrixes; apps should pin via lockfile and update intentionally.


4) Maintainers: how to bump correctly

  • PATCH: bug fix, perf, doc typo, build tweaks, internal refactors with no API change.
  • MINOR: new feature, new endpoint/field (additive), deprecations without removal.
  • MAJOR: removals, behavior changes, signature changes, renamed fields, dropped platforms. Provide a migration guide and deprecate first when possible.

Release hygiene

  • Automate release notes from commits (e.g., Conventional Commits).
  • Run contract tests and schema diff checks to catch accidental breaks.
  • If you accidentally break on MINOR/PATCH, own it: publish a revert PATCH or hot MAJOR + notice.

5) Consumers: upgrade without fear

  • Treat MAJOR bumps as migration work; read changelogs.
  • Allow MINOR/PATCH auto‑updates only with CI that runs tests.
  • Use lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml, Cargo.lock, Pipfile.lock) to keep fleets reproducible.
  • In APIs, version URLs or headers (/v2, Accept: application/vnd.example.v2+json).

6) Quick examples

Library adds optional param (back‑compat):

- greet(name: string): string
+ greet(name: string, opts?: { excited?: boolean }): string
# → MINOR

API removes field or changes type:

- { "price": "9.99" }  // string
+ { "price": 9.99 }    // number
# → MAJOR (clients may break)

Bug fix only:

- off-by-one in pagination
+ correct lastPage calculation
# → PATCH

7) Common pitfalls & fixes

| Pitfall | Why it burns | Fix | |---|---|---| | Shipping breaking changes as MINOR/PATCH | Downstream outages | Bump MAJOR, add deprecations & codemods | | Relying on latest tags | Non‑reproducible builds | Pin and use lockfiles | | “Private” APIs leaking | Users depend on internals | Mark internal APIs; use @internal/underscores; enforce lint | | 0.x forever | No stability guarantees | Cut 1.0.0 when API settles | | Hidden schema breaks | JSON shape shifts silently | Contract tests, schema diff in CI |


8) Quick checklist

  • [ ] Bump MAJOR for breaking, MINOR for additive, PATCH for fixes.
  • [ ] Use pre‑releases for testing; remove the tag for GA.
  • [ ] Maintainers: publish changelogs and migration notes.
  • [ ] Consumers: rely on lockfiles and CI to validate updates.
  • [ ] Version public APIs explicitly (/v2, media types).

One‑minute adoption plan

  1. Decide what your API is and write it down (exports, endpoints, formats).
  2. Automate release notes and semantic bumping from commits/tests.
  3. Start publishing pre‑releases for risky changes; gather feedback.
  4. Pin dependencies with a lockfile; run CI on update PRs.
  5. Tag a stable 1.0.0 once you’re confident in your contract.