Question presented to candidate: "You need to change the shape of a response field in a way existing clients cannot handle. How do you ship that change without breaking them?"
What a strong answer should cover:
- URI versioning (
/v1/users,/v2/users) β the most common approach in practice. The version is visible in every request, trivially cacheable and routable, and easy for any client or tool (including a browser address bar) to understand β at the cost of "polluting" the URI with something that is arguably metadata about the representation, not the resource itself. - Header-based versioning (a custom header like
Api-Version: 2, or content negotiation via a versioned media type inAccept, e.g.application/vnd.myapi.v2+json) β keeps the URI itself stable across versions, which some argue is more "correct" REST design (the resource identity does not change; its representation does) β at the cost of being less visible/discoverable and slightly harder to test by just pasting a URL in a browser. - The decision that matters more than the mechanism: what actually constitutes a breaking change worth a new version β removing or renaming a field, changing a field's type or meaning β versus a purely additive change (a new optional field) that existing clients can safely ignore and does not need a version bump.
- A deprecation policy is not optional once you have multiple live versions: a
Deprecation/Sunsetresponse header (or an equivalent documented policy) telling clients a version will stop being supported, with a real timeline, is what actually lets you retire an old version instead of supporting it forever. - Whichever mechanism is chosen, the routing/dispatch logic should be a thin layer, not duplicated business logic per version β a version-specific response shape is usually just a transformation applied to one shared underlying implementation, not two independently maintained code paths.
- There is no single universally "correct" choice between URI and header-based versioning β this is a real, debated trade-off in API design, and the strongest answer names the trade-off explicitly rather than asserting one is simply better.
Clarifying questions expected:
- "Who are the API's actual consumers β a public API with unknown third parties, or an internal service with a small, known set of clients?" β public APIs lean toward the more visible/discoverable URI approach in practice.
- "Is there an existing deprecation/sunset policy, or would this be the first versioned endpoint in the service?" β decides how much process needs to be built alongside the code.
Code / implementation expected: Yes β implementing at least two of the three strategies against a real router and showing they dispatch correctly is the concrete deliverable.