no-to-versioning-LEAD
Reda Hmeid
Reda Hmeid Principal Consultant

Tech Focus Thu 10th October, 2024

Just say no – to versioning APIs

As a concept, non-versioning is controversial, but I believe that we should never version our APIs. 

In my experience, we all agree on the fundamental, core principle that producers of APIs should not break consumers of APIs through change. That’s helpful common ground to start a debate – but let’s define the principle more clearly: API consumers expect never to be broken by a change in the API that they consume. 

I’m not the first to push the idea of no-versioning – the concept was first described by Roy Fielding, who wrote a thesis describing RESTful APIs. Fielding introduced the world to the concept of HATEOAS (though the thesis never uses the weird acronym) and talks about no-versioning in the context of REST APIs. He adopts the attitude of ‘be damned if you go for non-REST’. 

REST is an elegant architectural style that makes building evolvable APIs easy, without succumbing to versioning. This post isn’t a place for detailing REST (this book is a particularly good resource) but using RESTful principles for API design is probably a strong starting position. 

However, REST isn’t the only solution, and good API design can overcome versioning. 

API producers have broadly taken two approaches to not breaking consumers. The most common practice and the one that’s considered to be the standard by most, is to version APIs, deprecating the older versions and encouraging consumers to migrate. There are multiple ways of describing the version in the request. The most visible (and the one most organisations default to) is in the URL. Others will pass the version via the Accept header in the request. You’ll likely come across several resources that suggest this is best practice. 

However, API versioning has downsides regardless of the patterns used. For one thing, supporting more than one version of anything is a pain. Finding a bug in one version necessitates confirming it doesn’t exist in other versions. If it does, you need to fix it in all versions (meaning more testing is needed) or fix the latest version only, and insist that consumers move to a version without the bug – which may be the latest, or does a superseded version need fixing? 

Of course, some patterns minimise the pain of versioning. And advocates of versioning will argue that the concerns I raise can be resolved – but this is fixing a self-inflicted problem. No-versioning stops the problem in its tracks. The API consumer benefits either way: they don’t need to change, are not broken by changes to the API, always have the latest functionality, and the documentation they need to read is minimal. 

I started this post by saying that we should never version APIs. Backtracking slightly, I believe that versioning is unnecessary because I’ve yet to come across a situation in API design and development that actually needed it. And I’ve yet to be convinced in practice that versioning is a necessity, or even preferable to the alternative. 

Why you may want to version your API (but don’t need to) 

I have yet to come across a situation in API design that needed versioning. Below is a sanitised version of the sort of conversation I have, when suggesting that we shouldn’t version:

What if we need to add a new mandatory field in the request? 

If it’s really mandatory, why have an API version that doesn’t require it?  

We need versioning to carry out new business logic 

Again, this shouldn’t be described as ‘mandatory’ new business logic – at best, it’s an optional field that may change the path your code goes down. If it’s truly mandatory, why support a version that doesn’t need it?  

We made a spelling mistake in the field name, and if we change field names, it will break the consumers 

So don’t change the field name, if consumers are using it fine. If it isn’t causing harm, leave it. If you really want to change a spelling or name, support both – maybe only document the new one, but don’t break if the old spelling is used. 

We want to change the structure of our response 

Articulating why you need to change the structure may help you to design it in a non-breaking way. If necessary, consider content negotiation, where a consumer includes the media type they expect in the “accept” header. If the structure is significantly different maybe it isn’t a new version of the API but a new representation of the resource that the API returns. The key is to handle the change internally rather than forcing consumers to change. 

Github versions APIs

I think Github does APIs really well. If you used them as your exemplar, you wouldn’t go far wrong. Github is on version 4 of their APIs. How do we reconcile the two views – GitHub is good, but don’t do versioning? 

Well, Github is only on version 4 of their APIs after 13 years. That’s one new version every three (and a bit) years. But still, it is a new version. 

Another thing to note is that all the APIs were versioned at the same time, rather than individual versioning. There is a reason for that. The move from version 3 to version 4 was a move from REST APIs to GraphQL. This isn’t about versioning the APIs, but rather the architectural style.

While I’m not privy to the thinking behind this, if you visit Github’s docs home page they do not talk about version 3 and version 4 of their APIs, but the REST APIs and GraphQL APIs. I’d suggest this is further evidence they see this as a change of architectural style. 

You  will likely identify other scenarios requiring versioning, which I would love to hear about in the comments. I’m even happy to work with people on the design. I don’t want this to seem like a straightforward thing to do, especially if you do not start from a solid API design base. 

When you consider versioning as an option, take into account the following:

  • Make all changes to response or request optional rather than mandatory.
  • If they are mandatory changes, why do you support a version that doesn’t have those changes? 
  • Keep the API contract as simple as possible. Limit the number of input requirements to what is absolutely necessary to make the system work. Any business logic inputs should be retrieved from internal sources as much as possible. By making the API do the work, changes to the contract will be rare.
  • Would the change to the API benefit every consumer? If not, think about how impactful the change is for consumers who don’t need it?
  • Would you support every version forever? Or will you start to decommission versions and force consumers to move across?
  • Is the change due to new business logic? If so, will the new business logic be part of every version?
  • If the change is due to new business logic, should that even be a concern for your consumers?
  • Does a new version make things easier or more challenging for the consumers of your APIs?

Ultimately, ask yourself, “Does the benefit of this change justify the time, effort and impact of supporting multiple versions, to our consumers and us?”

The success of your APIs is in making them easy to consume. That should include not forcing the consumer to change. Rethink the approach to anything that makes API consumption more difficult. Do the hard work at the design stage, and hopefully, you’ll have easy to use, evolvable APIs that will be maintainable and keep everyone happy.