PushBackLog

Semantic Versioning (SemVer)

Soft enforcement Complete by PushBackLog team
Topic: delivery Topic: api-design Topic: release-management Skillset: engineering Skillset: devops Technology: generic Stage: delivery Stage: operations

Semantic Versioning (SemVer)

Status: Complete
Category: Delivery
Default enforcement: Soft
Author: PushBackLog team


Tags

  • Topic: delivery, api-design, release-management
  • Skillset: engineering, devops
  • Technology: generic
  • Stage: delivery, operations

Summary

Semantic Versioning (SemVer) is a versioning scheme in which a version number MAJOR.MINOR.PATCH communicates the nature of changes in each release: breaking changes increment MAJOR, backwards-compatible new features increment MINOR, and backwards-compatible bug fixes increment PATCH. SemVer creates a shared contract between package publishers and consumers: by reading a version number, a consumer can reason about the upgrade risk without reading a changelog. It is the standard for npm packages, most open-source libraries, and any API with external consumers.


Rationale

Version numbers are communication, not bookkeeping

Without a versioning convention, a version bump from 2.3.1 to 2.3.2 might be a bug fix, a new feature, or a breaking change — the consumer has no way to know without reading the full changelog. This makes automated dependency updates dangerous and slows teams down. SemVer transforms version numbers into a meaningful contract: the increment type signals what changed.

Automated tooling depends on SemVer semantics

Package managers (npm, cargo, pip), CI dependency update tools (Renovate, Dependabot), and changelogs generated from conventional commits all rely on SemVer semantics to make correct decisions. A ^1.0.0 range in package.json means “accept any 1.x.x” — that contract breaks if a 1.1.0 release contains a breaking change. Correct SemVer makes the whole dependency management ecosystem work correctly.


Guidance

The SemVer contract

MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]
  │     │     │
  │     │     └── Bug fix: backwards compatible
  │     └──────── New feature: backwards compatible
  └────────────── Breaking change: not backwards compatible

When to increment each segment:

Change typeIncrementExample
Breaking API changeMAJORRemove a field, rename a method, change a type
New backwards-compatible featureMINORAdd a new endpoint, add an optional parameter
Bug fixPATCHFix incorrect calculation, fix crash
Security fix (no API change)PATCHUpgrade vulnerable dependency

The 0.x special case

While MAJOR is 0, the API is considered unstable:

0.MINOR.PATCH
  • 0.x means any change can be breaking
  • Use 0.x during initial development before making a public contract commitment
  • Once you increment to 1.0.0, you are committing to backwards compatibility

Pre-release versions

1.2.0-alpha.1     # Alpha — work in progress
1.2.0-beta.1      # Beta — feature complete, testing in progress
1.2.0-rc.1        # Release candidate — ready for final verification

Pre-release versions have lower precedence than the associated normal version: 1.2.0-alpha.1 < 1.2.0.

Conventional Commits → SemVer automation

Combined with Conventional Commits, version bumps can be automated:

Commit typeSemVer bump
fix:PATCH
feat:MINOR
feat!: or BREAKING CHANGE: footerMAJOR
refactor:, docs:, chore:No bump

Tools like semantic-release and release-please automatically compute the next version, generate a changelog, create a git tag, and publish to the package registry based on commit history.

# .github/workflows/release.yml
- name: Create Release
  uses: google-github-actions/release-please-action@v4
  with:
    release-type: node
    # Reads conventional commits since last release
    # Computes correct SemVer bump
    # Creates changelog PR automatically

npm version ranges

Understanding how SemVer ranges work in package.json:

RangeMeaningExample binds to
^1.2.3Allow MINOR and PATCH bumps>= 1.2.3 < 2.0.0
~1.2.3Allow PATCH bumps only>= 1.2.3 < 1.3.0
1.2.3Exact version only1.2.3
>=1.2.3 <2.0.0Explicit rangeAs stated

The most common source of breaking changes in Node.js projects is a transitive dependency making an incorrect version bump.

API versioning vs. SemVer

SemVer for HTTP APIs is distinct from package SemVer. HTTP API versioning (URI versioning, header versioning) is covered in API Versioning. SemVer applies to the API’s release version communicated to consumers, not necessarily the URL structure.

Review checklist

  • All public packages use SemVer
  • MAJOR is incremented for every breaking change
  • Breaking changes are never sneaked into MINOR/PATCH releases
  • Changelog documents changes per version
  • Pre-release labels (alpha, beta, rc) are used before public production releases
  • semantic-release or equivalent automates version computation from commit history