Why an API needs a version at all

Free preview 3 min to read

An internal HTML page can change shape overnight. Every visitor gets the new markup on the next request, because nothing outside your own deploy reads that HTML as data. A JSON API response is different as soon as one outside client parses it. Change the shape, and every client still expecting the old one breaks, on a schedule you don't control and usually can't see coming. This app has clients like that: partners call its /partners/api/v1/... endpoints from their own systems.

A change that looks small and isn't

Take a hypothetical /api/v1/orders/42 endpoint returning:

{ "id": 42, "total": 19.99 }

total is a Float in dollars and cents. A later refactor moves money to integer cents everywhere, the usual fix for rounding errors in financial code, and the same endpoint now returns:

{ "id": 42, "total_cents": 1999 }

Nothing is wrong internally; integer cents fixes a real class of rounding bugs. But every external client that wrote order.total.to_s now reads nil where a number used to be, or crashes on a missing key. The bug isn't in the new code, which shipped correctly. It's in the assumption that "correct now" and "compatible with what's already out there" are the same property. They aren't, and in an API that gap has a name: a breaking change.

Who gets hurt, and why you don't see it

An HTML page's client is a browser you don't have to coordinate with: it fetches, renders, discards and fetches again. An API's client is code someone else wrote and deployed, and nobody there is watching your commits. It might poll once a minute for months without anyone touching it. The break doesn't show up in your test suite, your staging environment or your error tracker. It shows up in their logs, hours or days later, and reaches you, if it reaches you at all, as a support ticket that starts with "your API changed".

What versioning buys

Versioning isn't a substitute for careful code; the cents refactor above was careful and correct. It's a second, explicit promise on top of "the code works": this shape, addressed by this name, keeps meaning what it already means. /api/v1/orders/42 can keep returning { "total": 19.99 } for as long as v1 lives, while /api/v2/orders/42 returns { "total_cents": 1999 } from the day it ships. Both are correct, and neither has to bend to the other's shape. The rest of this course is the mechanics that make the split hold: in the URL, in a request header, and in what the server renders once it knows which version was asked for.

Key takeaways

  • A JSON API response is a promise a client depends on. "Correct" and "compatible with what's already deployed" are different properties.
  • The float-to-cents example is a common shape of breaking change: correct internally, incompatible externally, and invisible to your own tests because nothing in your codebase is the client that breaks.
  • An API client is usually code you don't control and can't see running, such as a partner's integration, so the break surfaces in someone else's logs, on their schedule.
  • Versioning is an explicit promise that a named shape keeps its meaning, so a new, equally correct shape can ship under a new name instead of replacing the old one under existing clients.
Lesson 1 of 7 · free

Keep going with lesson 2

URL-path versioning: the shape most Rails apps reach for first

Puts the version in the path with nested namespaces, giving separate controllers per version and visible, cache-friendly URLs.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...