Engineering

Semantic versioning and your changelog

5 min read

Semantic versioning tells a caller how much a release can hurt them before they read a single changelog entry. 2.4.1 to 2.5.0 says: new capability, nothing breaks. 2.5.0 to 3.0.0 says: read this one before you upgrade. A changelog and a version number are supposed to make the same claim in two formats, and most of the friction between them shows up exactly when they disagree, which is more often than the spec would suggest.

What does each number in a version actually promise?

Semantic versioning defines three numbers, MAJOR.MINOR.PATCH, each with a strict rule about what triggers it. A major bump means a breaking change: something a correct, existing integration could notice and would need to change for. A minor bump means new, backward-compatible functionality: nothing existing breaks, something new is available. A patch bump means a backward-compatible bug fix: behavior gets closer to what was documented, nothing that depended on the old behavior on purpose should notice.

BumpMeaningChangelog entry should read like
MAJOR (1.x.x -> 2.0.0)A breaking change“This requires action before you upgrade”
MINOR (1.2.x -> 1.3.0)New, compatible capability“This is available now, nothing else changed”
PATCH (1.2.3 -> 1.2.4)A compatible fix“This now behaves the way it was documented to”

The table is also a test you can run backward: if an entry does not read like its row, either the version number is wrong or the entry is underselling (or overselling) what actually happened.

What counts as breaking, for versioning purposes?

The same test that decides whether something belongs in an API changelog at all: could a correct caller, written against the old behavior and untouched since, behave differently because of this change. What is a breaking change, and how to ship one covers the decision in full, including the cases that look breaking and are not, and the ones that look small and are. The short version for versioning purposes: if the answer is yes, the version bump is major regardless of how much code the change actually touched internally. Version numbers track consequence to the caller, not effort to the team.

How should a changelog entry map to a version bump?

One entry, one bump category, stated up front. The pattern from the table above extends directly: a breaking entry sits under the version that introduced it, worded as a warning first and a description second. An additive entry sits under its minor version, worded as availability. A fix sits under its patch version, worded as a correction. Mixing categories under one entry, like folding a breaking change into the same paragraph as an unrelated fix, is how a reader misses the one thing that actually mattered.

## 3.0.0 (2026-09-07)

### Changed
- **BREAKING:** `GET /reports` now returns amounts as integers in the
  smallest currency unit (cents) instead of floats. Update any code
  that reads `amount` directly.

## 2.9.0 (2026-09-01)

### Added
- Reports can now be filtered by `status`.

## 2.8.4 (2026-08-28)

### Fixed
- `GET /reports?status=` returned an empty page instead of a 400 for
  an unrecognized status.

Reading top to bottom, the version numbers and the section labels say the same thing twice, which is the entire point: a reader scanning only the headings gets an accurate risk read before they open a single bullet.

Does the breaking-change rule apply the same way before 1.0.0?

No, and this is where most of the confusion about “was that actually breaking” comes from. SemVer is explicit that major version zero, 0.y.z, is for initial development: anything may change at any time, and the public API should not be considered stable. A 0.4.0 to 0.5.0 bump can carry a breaking change without violating the spec, because the major-version guarantee only starts once a project ships 1.0.0. A changelog entry still owes readers the same honesty about what broke; what changes is only that the version number itself is not the signal to rely on before 1.0.0 arrives.

What if your product doesn’t ship discrete versions?

Most SaaS products deploy continuously and never expose a version number to a caller, which does not remove the need for the discipline, only the number that would normally carry it. The changelog entry has to do the whole job alone: say plainly whether a change is breaking, additive, or a fix, using the same three words semantic versioning uses, even with no version field to attach them to. Some teams keep an internal version purely to anchor changelog entries to something linkable, without ever surfacing it to the caller directly.

How does this apply to an API changelog specifically?

More strictly than almost anywhere else, because an API’s callers are code, not people who can shrug off an unexpected change. API changelog: what to publish and who reads it covers the full shape of that document; the versioning discipline here is what keeps its breaking and additive sections honest. An API that offers multiple concurrent versions, like v1 and v2 served side by side during a migration window, is effectively running semantic versioning at the scale of the whole interface rather than a single package, and the same three-word vocabulary still applies to every entry.

What does Keep a Changelog say about versioning?

It ties directly to semantic versioning by name and recommends the same category vocabulary this article uses: Added, Changed, Deprecated, Removed, Fixed, Security. Keep a Changelog, implemented walks through adopting that spec in practice, including where teams tend to drift from it. The overlap is not a coincidence: both specs are trying to solve the same problem from opposite ends, one standardizing the version number and the other standardizing the entry that explains it.

FAQ

Does every changelog entry need a version number? If the product ships versions, yes, because the number is what lets a reader jump straight to “how much does this affect me” without reading the entry first. If the product deploys continuously with no version field, the entry’s own wording has to carry that signal instead.

What’s the difference between a major version bump and a breaking change entry? They should be the same event described two ways. The version number is the machine-readable signal (a caller’s tooling can gate on it); the changelog entry is the human-readable explanation of what specifically changed.

Can a patch release ever be breaking? It should not be, by definition. If one shipped anyway, do not edit or re-tag the published version: the SemVer FAQ says to release a new version that restores compatibility, or a new major if the break stays, and to document the offending version so users know to skip it.

Do internal-only changes need a version bump at all? No. Semantic versioning tracks the public interface. A refactor with no observable effect on a caller does not need a bump or a changelog entry, even if it was significant engineering work.


The technical claims in this article have not been independently reviewed. If something here is wrong, tell us and we will correct it.

Related on changeloop: Changelog generator, Developer docs

changeloop
The team building a closed-loop changelog. Your users ask, your team ships, the person who asked gets told.