Skip to content
API Design Learn/Auth & Versioning
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Versioning Without Breaking Everyone

7 min read

You'll learn to

  • -Compare URL-path versioning against header-based versioning and justify a choice
  • -Use a Deprecation header and a communicated sunset timeline instead of breaking old clients without warning

Every API eventually needs to change in a way that would break existing clients - and unlike an internal codebase, you cannot simply update every caller at the same time you ship the change. Versioning is how an API makes breaking changes without breaking everyone who depends on it today.

What Counts as Breaking

  • -Breaking: removing a field, renaming a field, changing a field's type or meaning, removing an endpoint, changing required request parameters.
  • -Non-breaking (safe to ship without a version bump): adding a new optional field to a response, adding a new endpoint, adding a new optional query parameter.

The practical implication: most API evolution should be additive and non-breaking, reserving an actual version bump for the genuinely rare case where a breaking change is unavoidable - not treating every change as an excuse to cut a new version.

URL-Path Versioning: Simple and Explicit

The most common, most explicit versioning approach
GET /v1/orders/42
GET /v2/orders/42

URL-path versioning (`/v1/...`, `/v2/...`) is explicit, cacheable, and trivially visible in logs and browser history - its downside is that it puts the version at the resource-identity level, which purists argue is philosophically odd (is `/v1/orders/42` really a different resource than `/v2/orders/42`?), but this objection rarely matters in practice, and URL-path versioning is by far the most common real-world convention for exactly the simplicity and visibility reasons above.

The Alternative: Header-Based Versioning

Versioning via a request header instead of the URL
GET /orders/42
Accept: application/vnd.example.v2+json

Header-based versioning keeps the URL itself stable across versions (arguably more "correct" REST, since the resource identity never changes), at the cost of being less visible - you can't tell which version a request is targeting just by looking at a URL in a browser or a log line. Either approach is defensible; the interview signal is picking one deliberately and being able to justify the trade-off, not treating the choice as arbitrary.

Deprecating Without Breaking Anyone by Surprise

Warning clients before a version is actually removed
GET /v1/orders/42
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 01 Aug 2026 00:00:00 GMT
Link: </v2/orders/42>; rel="successor-version"

A `Deprecation` header (announcing this version is on its way out), a `Sunset` header (the actual date it stops working), and a `Link` header pointing at the replacement give clients real time and a clear migration path - versus silently removing the old version on a deploy, which turns a planned migration into an unplanned incident for anyone who hadn't moved yet.

A version bump is not an excuse to skip backward-compatible design discipline going forward - "we'll just bump to v3 next time" treated as the default answer to every breaking change tends to fragment client support across many simultaneously-live versions, each needing to be maintained.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

Design Version Control in the API Design Lab's REST Foundations act.

ScaleDojo Logo
Initializing ScaleDojo