API versioning sounds straightforward, and I’d assumed that
/api/v1 in the URL path solves the problem by giving your API a version number.
Then I saw this from Fielding himself:
The reason to make a real REST API is to get evolvability … a “v1” is a middle finger to your API customers, indicating RPC/HTTP (not REST) @fielding
The plot thickens!
Three wrong approaches
Troy Hunt expands on URL path versioning’s problems, writing that Your API versioning is wrong, which is why I decided to do it 3 different wrong ways:
- URL path, e.g.
- version HTTP header, e.g.
- content negotiation HTTP header, e.g.
Each of these involves a combination of compromises:
- Breaking HTTP semantics - by using URLs to refer not to specific versions of resources
- Changing URLs when you change the API
- Making the API harder to test - by forcing clients to set HTTP headers
- Breaking existing API clients - when a new version includes incompatible changes
Some of these cause more problems than others, in practice, and your level of distaste for each may vary.
Personally, I’ve learned that while breaking HTTP semantics sounds esoteric,
it means having to write documentation to replace specifications you didn’t read.
Similarly, not using
Accept headers properly eventually leads to badly-designed home-grown
Meanwhile, Troy Hunt’s compromises don’t suit everyone. In situations like these, it helps to ask the experts.
Various HTTP experts inhabit the HTTP APIs Slack, so I explored there. The plot thickens (again) in the #versioning channel, whose topic reads:
Rule of thumb, don’t do versioning :)
When I dug deeper, I didn’t find a no versioning movement (or even a hashtag), like NoEstimates or #noprojects. Instead, I learned that versioning a whole API makes about as much sense as versioning a whole web site. Instead, you evolve the API:
- Don’t use version numbers
- Don’t break existing clients
- Change existing HTTP resources or add new ones
Fielding’s comment about ‘a real REST API’ refers to how REST’s hypermedia constraint means that API clients follow links to compatible linked resources, so they don’t need version numbers. However, even if your API doesn’t use hypermedia, this evolvability requires safe non-breaking changes to your API’s HTTP resources. Instead of explicit API-level versioning, you do this with implicit compatible versioning.
API Change Management describes how compatible versioning evolves an API by allowing only non-breaking changes to HTTP resources. This includes a strict definition of no-breaking changes to a resource’s URL, headers, body, HTTP methods, and links to other resources. To implement a breaking change, you have to add a new HTTP resource. With this approach, the API evolves without breaking existing clients.
Asbjørn Ulsberg explains this in more detail in his API Change Strategy, after which he makes a similar comment to Fielding:
slapping a version number on a web API seems like a simple solution to a simple problem, while in fact it is a very naïve solution to a very complex problem
So while HTTP may allow some simplicity, versioning remains complex.