Architecture

API versioning without breaking your consumers

Versioning is a policy question before it is a technical one. Decide what breaking means and how long you support old versions.

Key takeaways

  • Tightening validation is a breaking change people forget.
  • Choose one versioning mechanism and apply it consistently.
  • Publish support windows and monitor who still uses old versions.
  • Design for additive evolution to avoid version proliferation.

Define what breaks

Additive changes — new optional fields, new endpoints — are generally safe if consumers ignore unknown fields, which is worth stating in your documentation as an expectation.

Removing a field, renaming one, changing a type, tightening validation or changing the meaning of a value all break consumers. Tightening validation is the one most often overlooked, because it looks like a fix.

Pick a mechanism and be consistent

  • URL path — visible, cacheable, easy to route; the least surprising default.
  • Header — keeps URLs stable, harder to test casually.
  • Query parameter — simple and easily lost.

The mechanism matters less than applying it consistently and documenting it clearly.

Support windows and deprecation

Publish how long a version is supported once its successor ships. Announce deprecation in advance, return a deprecation header on affected responses, and monitor which consumers are still calling it.

Contacting the remaining consumers directly is what actually completes a migration. Silence followed by removal produces incidents.

Prefer evolution over proliferation

Every supported version is code to maintain and test. Design defensively so most changes are additive, and reserve new versions for genuine breaks.

Two supported versions is manageable. Five is a maintenance problem that will eventually force an abrupt removal.

Communicating change to consumers

Technical versioning mechanisms only work if consumers know what is happening. Most breakage after a version change traces to communication rather than to the mechanism.

Publish a changelog, provide a migration guide with before-and-after examples, and give a realistic timetable. Where you can identify consumers from access logs, contact them directly rather than relying on announcements being read.

Monitor usage of deprecated versions and follow up with the remaining callers. Removal on a date with active consumers still calling produces an incident that was entirely predictable.


Frequently asked questions

Long enough for consumers to migrate reasonably commonly six to twelve months for external APIs, less for internal ones where you know every consumer.

A lighter form. Knowing every consumer allows coordinated changes, but a compatibility policy still prevents surprise breakage during independent deployments.

GraphQL favours evolution over versioning: add fields, deprecate old ones, and use field usage data to know when removal is safe.

Working on this?

A conversation with our team, no charge and no obligation.

Book a conversation