Breaking Change

A breaking change is incompatible with a supported interface, including its behavior and meaning. It can stop a consumer from working or make its results wrong without changing the payloadโ€™s shape. Reviewing only field names and types therefore misses part of the contract.

Assess two things: whether the change preserves the promised interface, and what existing consumers actually depend on. Unsupported assumptions can cause real failures and deserve investigation and migration planning. They do not automatically extend the supported contract or determine a major version bump.

The usual cases, and who they break

ChangeWho it breaks
Adding an optional fieldUsually nobody โ€” but only because consumers ignore what they do not recognize. A strict validator, or code that persists whole payloads into a fixed schema, breaks anyway
Adding a required inputCallers that omit the newly required input
Removing a field, or leaving it always emptyAnyone reading it. Emptying a field is removal with extra steps
Adding a new enum valueConsumers that handle the old set exhaustively โ€” a common pattern, and one nobody remembers is there
Tightening validationCallers whose input was previously accepted, including those relying on a bug you just fixed
Changing what a field meansConsumers that depend on the old meaning; the change may be detected or may silently alter results

For example, RFC 9457 requires consumers to ignore unknown problem-detail extensions so that providers can add them compatibly. A client that rejects such an extension may still fail in production, but that failure does not make the addition a violation of this rule. Where the interface has no such extension policy, check the supported validators and storage mappings before assuming an addition is compatible.

The quiet ones

Several of the changes in that table announce themselves: a failed parse, a rejected request, an unhandled case. Someone sees those, usually within hours.

The trap is assuming that only the last row is quiet. Whether a break is visible depends on what the consumer does with the thing you changed, not on whether the change was structural. Remove an optional field and a consumer that substitutes a default keeps running and computes something different. Add an enum value and a consumer whose branch has a catch-all arm files it under the wrong category. Tighten a nullable field to non-null and a consumer that treated null as not applicable now treats the substituted value as real. In each case parsing succeeded, nothing was logged, and the output changed.

A changed meaning can also produce plausible results. If active customers previously included trial accounts, excluding them changes counts for consumers that relied on that definition. A validation check might catch it; without one, a dashboard can keep rendering and the discrepancy may surface only during a later comparison.

Announce changes to supported meanings, provide a migration or version where needed, and date the definition so readers can compare periods correctly. Documenting meanings early also exposes assumptions. An undocumented interpretation may be a dependency to manage, but it is not automatically a permanent promise.

Four things providers assume wrongly

  • It is a bug fix, so nobody can break. A consumer may have worked around the bug. Check that operational impact separately from whether the fix remains compatible with the declared API.
  • It is deprecated, so nobody uses it. Deprecation is an announcement. Usage records provide evidence, but zero calls in an observation window do not rule out infrequent jobs or unobserved consumers.
  • Nobody has complained about it, so nobody depends on it. Silence is what a working dependency sounds like.
  • The client will handle it. There is rarely one client. There are several, of different ages, and at least one belongs to someone you cannot phone.

The compatibility direction is also a choice rather than a given, and the two directions protect different people: new readers handling old data is not the same guarantee as old readers handling new data, and which one you need depends on who upgrades first. That is the same reasoning as schema compatibility. Confluent’s Schema Registry documentation is a good source for the direction vocabulary, and it notes that “Avro, Protobuf, and JSON Schema have different compatibility rules,” so a change that passes in one format may not in another.

Deciding, and avoiding

A four-step judgment covers most proposed changes, and the first step is the one usually skipped.

  1. Find the affected consumers. Combine usage data with registrations, lineage, and owner confirmation. Map shared credentials to applications and record gaps in the inventory.
  2. Ask what their code does with the thing you are changing โ€” reads it, branches on it, stores it, validates it. Each implies a different exposure.
  3. Check whether the failure would be visible. An invisible break is worse than a loud one and deserves more caution, not less.
  4. Then choose: ship it, version it, or reshape it into something compatible.

Three reshapings avoid most versions. Add rather than change, keeping both forms populated during a transition. Make the new behavior opt-in through a parameter or header, so existing callers are untouched. Or expand, migrate, contract โ€” accept both shapes, move consumers, then remove the old one. Where a version is genuinely needed, semantic versioning requires a major bump for an incompatible change to a stable, declared public API. Actual failures caused only by unsupported use do not automatically determine that bump. The retirement half โ€” announcing, measuring, and removing โ€” is covered under deprecation.

How this fits with the rest of an interface’s obligations โ€” errors, retry safety, ordering, and limits โ€” is worked through in You Cannot Un-publish an API.

References


Discover more from Insightful Data Lab

Subscribe to get the latest posts sent to your email.

Similar Posts

Questions, corrections, or additional insights?

This site uses Akismet to reduce spam. Learn how your comment data is processed.