One of the most common pieces of over-the-counter advice with assumed universal applicability of Restful APIs is that you should version your APIs. There are differences of opinion over the best approach to versioning but it seems that there’s universal acceptance that it’s a GoodThing(™). I’d like to suggest a slightly more nuanced attitude towards API versioning, which is that you should hope you shouldn’t have to version, aim not to version, but not preclude the possibility of versioning at a later date if you have to.
So to get us started let’s review what we mean by API versioning – it’s a client server thing and it’s about ensuring that the two have a compatible notion of what the API will do / should do and what the request / response formats must contain. If the client and server communicate via the same API version then they’re compatible, with no guarantees if they’re not communicating with the same version. As I mentioned before there are different approaches to versioning – some people embed a version number in their URI structure, some people attach a version number to their media type and use content-negotiation to ensure API compatibility. There’s a religious war going on about which is best and, luckily, we don’t have to enter that here because the 20,000ft perspective is the same – if a client requests a resource using a version that the server doesn’t support or understand then the client receives an error, typically a 4xx series error (because HTTP is the only protocol you can develop Restful APIs in, right…) such as 404 or 406, although other response codes are available.
The value of API versioning is that it may allow the server to implement breaking API changes without breaking existing clients. So, lets say you want to remove a field from a response document, then you can create ‘v2’ of your API without that field, while still supporting clients on ‘v1’ who may use that field. An important thing to note is that, unlike many other versioning ideas in software, API versioning is not about identifying every change to an API, only those which could reasonably break a client. If you just need to add a field to an API it would stay at v1, because existing v1 clients shouldn’t be affected by that change. So, not every API change prompts a version number change – just the breaking ones. And that amounts to either a deletion or repurposing of a field in a response and adding a required field in a request document.
The big difficulty with versioning is the effort associated with maintaining older versions. As the server-side resource model drifts into new territory the difficulty associated with maintaining the older resource model using a shared infrastructure increases. The requirement to maintain the behaviour of the ‘old’ API is going to put constraints on what you can expect in other parts of your system – so if v2 and v1 requests end up going into a shared database it may become difficult to modify the underlying table structure to best represent the business domain due to the nature of the v1 API structure. Conversely, it’s also possible that as the internal server-side infrastructure (whether that’s operational or computational) evolves to best serve the current business model so there’ll have to be effort put into the earlier API implementation to make appropriate use of the shared resources. The v1 implementation may have to do more work to enrich incoming data, to handle internal errors, to implement unexpected computational models (such as asynchronous communication) and suchlike.
In short, older API versions are effectively technical debt and, like all technical debt, they need to be paid down. Naturally it’s not as simple as that in practice but the principle stands. The difficulty is exactly the nature of the interaction which an API is designed to solve – distributed communication. The problem is that the lifecycle of the client and server are necessarily decoupled. That’s a desirable property of a distributed system. And so API versioning is the go-to solution. Nonetheless, you can’t get away from the fact that the versioning solution introduces technical debt which must be payed down.
In a purely distributed environment that’s pretty much the end of the story – as a server side developer you can only give people a timeline for change, deprecate the old api with a stern warning and ‘some time’ later stop supporting it and deal with the barrage of clients which are stubbornly using it still. The truth, however, is that most APIs developed are actually internal and have a small number of known clients. The dynamics in this sort of situation are quite different although the underlying motivation is exactly the same – the desire to move the API forward and to deprecate, and ultimately remove, ‘undesired’ features. But when you can interact with your clients directly, and their number is relatively small, then you can work with them to move their clients forward to using the new aspects of an API, with the old ones in place, and then you can remove the old aspects of the API – thus keeping it clean and ensuring that new clients don’t accidentally use ‘old’ aspects of the API. Large changes should be broken down into smaller individual changes which are not going to cause client breakage. The only breaking change is removing old features that are no longer used. Everything else involves adding new things, although the ‘new’ may involve resurrecting ‘old’ things like names but with a new implementation. All without worrying about a version number – rather the clients and server evolve their API organically and yet still in a decoupled fashion. So you get the benefits of a decoupled development model and the benefits of not maintaining multiple ‘versions’ of the API.
So, looking back, my overall point is not that API versioning is a BadThing(™). It’s clearly not, and in certain situations it’s absolutely the only way to manage change without introducing breakage. However, API versioning is not the only solution to the problem of managing change in an API and it certainly doesn’t, on its own, deal with the ultimate problem of actually removing ancient features – which is what actually allows the API to keep changing. Like The Red Queen in Alice’s Adventures Through The Looking Glass, the API must keep moving to remain in the same place, and that means shedding the old as much as gaining the new. And this is the perspective from which we should consider our approach to API versioning – how can we keep the API relevant, without allowing cruft to accrete. The received wisdom that we should always explicitly version our API is problematic in that it doesn’t begin from this standpoint, it simply suggests an easy to implement but imperfect solution without detailing the corollary idea that a deprecation management strategy is needed to avoid older API versions strangling our capability to move the API forward.
And so my advice is to understand your situation, who your clients are, whether you can communicate with them and whether they’re amenable to working with you to manage change. And then consider whether explicit versioning is really necessary for your situation. It may be, and it my not be. In either case, the important thing to do is to manage the removal of old and crufty features before they become problems. In other words – manage your technical debt.
http://www.troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html
Leave a Reply