Medium system-design api

How should you version a REST API without breaking clients?

The 30 second answer

I treat the OpenAPI description as the contract and prefer compatible extensions over new versions: add optional fields, keep existing meaning, and make clients tolerant of additions. If a breaking change is unavoidable, I would version the media type and negotiate it, rather than putting a version segment in the URL. That is also what Zalando's public RESTful API Guidelines require. Format selection with Accept is a different problem from evolving the semantic contract.

OpenAPI is the usual machine-readable contract for HTTP JSON APIs. It describes paths, methods, parameters, request and response schemas, security schemes and examples. It is a description of an HTTP API; it is not itself REST, and an OpenAPI file does not make an API hypermedia-driven.

Zalando's public guidelines are API First: define the API in OpenAPI before coding the implementation, as a single self-contained YAML file, and publish the specification. New APIs are encouraged to use OpenAPI 3.1. Property names and query parameters must be snake_case; path segments must be kebab-case and verb-free. JSON payloads must use an object as the top-level structure, including for collections, so fields can be added later.

On compatibility, Zalando's rules are:

  • Must not break backward compatibility.
  • Should prefer compatible extensions and should avoid versioning.
  • If an incompatible change cannot be avoided, teams must use media-type versioning (content negotiation) and must not use URL versioning.
  • Clients must be prepared to accept compatible extensions (Postel's law).

That versioning stance is a product rule in those guidelines, not a REST or HTTP requirement. HTTP itself versions the protocol (HTTP/1.1, HTTP/2, HTTP/3) and leaves application-level versioning to API designers. Other organisations do put a version segment in the path; if an interviewer asks for that, I would explain the trade-off honestly: a path prefix is obvious to humans and easy to route, but it forks the resource identifier and often leads to copying the whole API when only one representation changed. Media-type versioning keeps the same URI and lets clients opt into a new representation with Accept.

Format selection and semantic versioning are different problems. Choosing JSON versus XML, or gzip, is content negotiation over the same contract. Changing the meaning of a field, removing a property, or renaming a resource is contract evolution. An existing Kuchup question covers how Spring MVC selects JSON or XML; this answer is about whether the contract itself changed.

Alternate interview scenarios

"Should we put a version segment in every path?"

I would not, if I can avoid it. I would first make additive, compatible changes and keep one URI. If a breaking change is unavoidable, I would follow the public Zalando rule: version the media type and negotiate it, rather than minting a second identifier for the same resource. If the organisation already standardised on path prefixes, I would not fight that in the interview, but I would still keep old versions running until clients have moved.

"How do you add a field without breaking old mobile clients?"

Make it optional, give it a sensible default on the server, and never reuse the old field's meaning. Clients should ignore unknown properties. That is compatible extension, and it does not need a new version.

Sources (3)
  1. Zalando RESTful API and Event Guidelinesopensource.zalando.com
  2. OpenAPI Specificationspec.openapis.org
  3. Zalando RESTful API Guidelines, Compatibilityopensource.zalando.com