I've recently read a quite useful article on API design best practices.
But I wasn't quite comfortable with one point in it, the one advocating putting a "v1" in the API URL paths to support versioning. After thinking about it a little I have decided to explain my reasons in more detail.
Similarly to code expressed in more lines of code, adding more abstraction layers prematurely should be considered a liability, not an asset. Once we decide on the convention to put the version number in the URL, in order to be consistent, we have to incur the cost of maintaining the convention for all our API endpoints.
Moreover, having the version number can support a false feeling that making incompatible API changes is actually cheap. It is not. You have to think about all the clients of your API and support (including regression testing etc.) multiple versions of your API until you are sure a certain version is not used by anyone.
Another important thing to consider is that in practice it is quite hard to draw the line when exactly an API becomes backwards incompatible, for example:
- Although it is not nice or recommended, it can happen that there are clients not ignoring newly added response attributes.
- Is changing caching policy, returning a response using Transfer-Encoding: chunked instead of Content-Length etc. a compatible API change?
- You just fix a bug in your API, but there may be clients who work around the bug and actually rely on the buggy behaviour!
Only the clients of your API can tell for sure if a change is really compatible or not.
In many cases you decide that even though in theory your API change may not be backwards-compatible, you just expect the clients to adjust. A typical scenario is when you have control over the development of the client(s), like front-end developers connecting to a back-end developed within the same agile team. Incrementing a version number in the URL in such a case "just because it is what the book says" would be just an unnecessary additional work and a cause of fights.
A major API change quite often brings a change of semantics of your endpoints, so maybe just renaming them to their accurate business meaning is enough and you do not need "v1" and "v2".
If you really need to support multiple versions, you probably just need it for one or a few endpoints.
Now if your "v1" is at the root of the path, you
- either have to change the path of all endpoints when you just needed to change a few of them
- or you break the path hierarchy because some endpoints will be under "v1", while others under "v2"
If "v1" is at the end of the path then you cannot deal with it as with a common prefix in just one place, you have to maintain it in all the endpoints. Doubts and inconsistencies can pop up very quickly: Is it /customers/v1/{id} or /customers/{id}/v1 ?
So, I think it is better to skip "v1" and wait until you really need a "v2". And when it happens, you should consider, instead of putting the "v2" in the URL path, to use an HTTP request header (either a custom one or maybe as a suffix of "Content-type").
Top comments (1)
This is a fascinating discussion about versioning. A few months ago, I gave a presentation on the same topic. I completely agree that we should not blindly follow best practices without considering our own specific needs.
However, if we decide to version our APIs, one thing is absolutely crucial: communication. Proper documentation is an excellent way to communicate any breaking changes. Regardless of who our API consumers are, they must be made aware of any upcoming changes in the API contract.