What they're testing
Whether you think about clients you don't control — installed mobile apps, partner integrations.
The short answer~30 seconds
Three approaches: in the URL (/v2/orders), in a header (Accept: application/vnd.acme.v2+json), or date-based like Stripe (Stripe-Version: 2026-08-16). URLs are the easiest to debug and cache, hence the most common; headers are more "RESTful" but awkward with curl and easily stripped by proxies. Breaking changes are: removing or renaming a field, narrowing a type, adding a required input field, or changing what a value means. Adding a field to a response is NOT breaking, provided clients ignore unknown fields.
The long answer
The most important point is that the real cost of versioning isn't shipping v2 — it's MAINTAINING v1 alongside it for years. Every security patch applies to both, every database change must suit both, and your team has to remember how both behave. So the best strategy is usually avoiding the need: design for extension, and treat each forced version bump as a small failure of the previous design.
Stripe's approach is worth studying because it separates two things people merge: the version belongs to the ACCOUNT, not the call. Clients are pinned at their integration date, and the server runs a chain of transformers that downgrade the modern response into the older shape. That leaves one business-logic path with compatibility confined to the edge. Expensive to build, and the only way I know of to carry dozens of versions without losing your mind.
On "adding fields isn't breaking": that only holds if clients genuinely ignore unknown fields, and many don't — a strict decoder throws. So document that clients MUST tolerate unknown fields, and better still verify it with compatibility tests rather than trusting the promise.
What they'll ask next
?How do you know who still uses v1 so you can retire it?
Measure per client id, not just traffic: how many accounts, which endpoints, last call. Then announce, set a date, and run brownouts — disable v1 for a few minutes in a quiet window so any remaining clients discover it before the real cutoff.
?How does GraphQL handle versioning?
It replaces versions with per-field @deprecated: the schema evolves continuously, old fields stay, and you track who still selects them. It sounds nicer, at the cost of a schema that only grows — and removing a field still needs the same measure-announce-retire process.
These lose points
- Cutting a v2 for one small change. Each version is a long-term maintenance burden, not a label.
- Having no retirement plan. Every version needs to know in advance how it dies.