Semantic Versioning in Practice
Introduction¶
Semantic Versioning (MAJOR.MINOR.PATCH) is a promise to consumers of your
package or API about what an upgrade will do to them.
The contract¶
Given a public API:
- PATCH (
1.4.2 → 1.4.3) — backward-compatible bug fix. Consumers upgrade without reading anything. - MINOR (
1.4.2 → 1.5.0) — backward-compatible new functionality. Additive. Safe to upgrade; may want to read the changelog for new features. - MAJOR (
1.4.2 → 2.0.0) — a backward-incompatible change. Consumers must read the migration notes.
"Backward compatible" is defined by your documented public API. Renaming an internal function isn't a breaking change; removing a documented CLI flag is.
Pre-release and metadata¶
1.0.0-alpha
1.0.0-alpha.1
1.0.0-beta.2
1.0.0-rc.1
1.0.0
1.0.0+20260827.sha.5114f85 # build metadata — ignored in precedence
Precedence: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0.
Pre-release versions sort before the release. Build metadata (+...) never
affects ordering.
The 0.x rule¶
Before 1.0.0, anything can break at any time. The convention many ecosystems
adopt: in 0.y.z, a MINOR bump (0.4.x → 0.5.0) may break, a PATCH
(0.4.1 → 0.4.2) shouldn't. Publishing 1.0.0 is a statement that you'll honour
the full contract from now on.
Version ranges¶
npm / semver syntax (others are similar):
| Range | Matches | Notes |
|---|---|---|
1.2.3 |
exactly that | |
^1.2.3 |
>=1.2.3 <2.0.0 |
"compatible" — the default npm install writes |
~1.2.3 |
>=1.2.3 <1.3.0 |
patch-level only |
>=1.2.3 <1.5.0 |
explicit range | |
* / latest |
anything | don't |
^0.2.3 |
>=0.2.3 <0.3.0 |
caret is special-cased for 0.x |
A lockfile (package-lock.json, poetry.lock, Cargo.lock, go.sum)
pins the exact resolved versions so builds are reproducible; the range in the
manifest just bounds what an update may pick.
Automating it¶
Deriving the next version by hand is error-prone. Drive it from Conventional Commits:
feat: add --json output flag -> MINOR
fix: handle empty config file -> PATCH
refactor!: drop Node 16 support -> MAJOR (! or "BREAKING CHANGE:" footer)
docs: fix typo -> no release
Tools: semantic-release, release-please, changesets, commitizen. On
merge to the main branch they compute the bump, update the changelog, tag, and
publish.
# release-please, abridged
- uses: googleapis/release-please-action@v4
with:
release-type: node
Verification and troubleshooting¶
npx semver 1.2.3 -r "^1.0.0" # does it satisfy the range?
npx semver -i minor 1.4.2 # -> 1.5.0
git tag --sort=-v:refname | head
^upgrade broke a build — a dependency shipped a breaking change in a MINOR release (SemVer violation on their part). Pin it (~or exact) and report upstream. This is why lockfiles + tested dependency-update PRs (Renovate/Dependabot) matter.- Two ranges can't be satisfied together —
Aneedslib@^1,Bneedslib@^2. npm may install both copies (nested); other ecosystems error. Update or replace one consumer. - Pre-release not installing — ranges don't match pre-releases unless the
range itself has one (
^1.0.0-0or--include=prerelease). - Automated release didn't fire — commit messages don't follow the
convention, or the only commits were
chore:/docs:(no release by design), or the tool needs afeat/fixsince the last tag. - Version in code out of sync with the git tag — let the release tool be the single source; read the version from package metadata at runtime, don't hardcode it in two places.
Related tools and reading¶
- On-site: Regex Tester.
- Related posts: Git rebase vs merge, Trunk-based development.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in DevOps & CI/CD
OpenTelemetry for Distributed Tracing
A trace follows one request across services. OpenTelemetry is the vendor-neutral way to produce them — here's the model, the Collector, and sampling that won't bankrupt you.
October 24, 2026pre-commit Hooks That Actually Help
pre-commit runs checks before a commit lands, catching formatting, secrets, and syntax errors locally. Here's a config that helps without being annoying.
October 24, 2026Chaos Engineering Basics
Chaos engineering is controlled experiments that inject failure to verify your system tolerates it. Here's the method, safe blast radius, starter experiments, and game days.
October 24, 2026