Skip to content

CONCEPT Cited by 1 source

Breaking change requires major bump

Definition

A breaking change to a component (or API, or message schema) requires a major version bump. Once the major version has bumped, both the old and the new major version must be supportable side-by-side for as long as any client still pins the old major version. The rule is a discipline that, when combined with a migrate function at the major-version boundary, enables heterogeneous-client fleets (old apps + new apps) to coexist indefinitely without forced upgrade.

Canonical wiki instance (Yelp Konbini)

Konbini's worked example: Yelp wants to change Button's text parameter from String to a custom FormattedText supporting http links. Verbatim: "This is a breaking change because older clients will receive a different type of object for the text field, which they don't know how to interpret." The JSON definition's version field bumps from 0.8 to 1.0:

{
  "name": "cookbook.Button",
  "version": "1.0",            // was "0.8"
  "parameters": {
    "text": { "type": "FormattedText",
              "description": "A formatted text ..." },
    ...
  }
}

Konbini auto-generates CookbookButtonV1 alongside the still- existing CookbookButtonV0; it also generates an empty migrate() method that developers must implement to backport V1 to V0 for clients whose spec version still references CookbookButton: "0.8".

What counts as breaking

  • Type change on an existing parameterStringFormattedText.
  • Removal of a parameter — older clients expecting the field will see it absent.
  • Rename of a parameter — JSON key change breaks deserialisers.
  • Narrowing of a union / enum — removing allowed values.
  • Change in required-ness — making an optional field required.
  • Semantic change — same shape, different meaning (subtle and hard to catch — not always a type signal, but still breaking).

What does not count as breaking (no major bump needed)

  • Adding an optional parameter. Old clients ignore it. Minor bump only.
  • Adding an enum value — if older clients have explicit unknown handling. If not, this is secretly breaking.
  • Documentation changes.

Preconditions for the discipline to hold

  • Version is part of the wire format. Every request / every serialised component carries its version so both sides can reason about compatibility.
  • Backward-compat story at every major boundary. Either a migrate function from N+1 → N, or an explicit decision not to support clients that pin N (failing closed).
  • Old majors don't disappear silently. Removing V0 from the code base while clients still pin it breaks those clients — old majors need to live until the last client has upgraded past them.

Contrasts

  • Protocol Buffers field compatibility — forbids rename-or-retype of existing fields by convention, using "add new fields, keep field tags stable, reserve removed tags". No version number in the wire; everything is additive.
  • GraphQL schema evolution — deprecation directives on fields, with GraphQL servers handling the "field requested but deprecated" case. Looser than semver major bumps; enforcement depends on schema-lint tooling.
  • REST API versioning (/v1/ vs /v2/) — per-endpoint, coarser than Konbini's per-component.

Seen in

Last updated · 550 distilled / 1,221 read