(Photo credit: Wikimedia.)
Roy Fielding’s advice on versioning APIs is, well, succinct:
In a subsequent interview, he thankfully clarifies:
Websites don’t come with version numbers attached because they never need to. Neither should a RESTful API.
In one of our own posts on HTTP, we hinted that HTTP does, in fact, support versioning. After all, as Fielding says in that same interview:
Anticipating change is one of the central themes of REST.
But if you aren’t supposed to version APIs, how do you “anticipate change,” exactly? At some point, don’t you have to version something?
The common practice is to give an API a version number and encode that into URLs. When a new version of an API comes out, the version numbers change and all those URLs become invalid.
An example of embedding the version into the URL.
This is semantically incorrect to begin with — it’s analogous to putting version numbers in database identifiers — but, more to the point, it breaks all existing links to your API. This is why Fielding says:
That is a myopic view of interface design: one where the owner’s desire for control ignores the customer’s need for continuity.
If you’re Twitter or Facebook, you can get away with this because your developers have no choice. Fortunately, we can do better, and make the lives’ of the developers who are responsible for the apps that rely on our APIs a little easier in the bargain.
HTTP provides a lot of mechanisms for “anticipating change,”
but in terms of versioning,
the trick is to version media types, not entire APIs.
The MIME media type format allows for a
which allows you to introduce new representations for the same resource.
This is why we don’t need versions for Web sites: we can introduce a new image format or video format and let the browser tell the server which formats it supports. We can play the same trick with our APIs — let clients request the new version of a data type when they’re ready for it.
An example of a custom Media Type.
Clients that haven’t yet been updated to the newer version will still work as before. Links can even be shared between — and this is pretty cool — older and newer clients. Since the links themselves don’t change, developers can incrementally introduce changes without breaking backward compatibility.
When taken in the context of what hypermedia is — in the context of an HTTP API, it’s an information graph overlaid onto the Internet — versioning media types instead of URLs means the graph itself can gradually evolve along with whatever reality it represents. But URL-based versioning zaps that graph out of existence and replaces it with an entirely new one.
This may not matter if it’s just you and your mobile app. Except that it does, the moment the law of unintended consequences kicks in. Someone wants a Web version of the mobile app. Or a partner unexpectedly wants to access the API. Or maybe it’s a second mobile app, or just another API. One client becomes two and then four and so forth and then the API needs a reboot.
And the kicker is that it doesn’t take much additional effort to avoid this problem entirely. The biggest objection we run into is that developers are conditioned to think in terms of links, instead of requests. Which is fine — we define default values for the headers for the first version, and create a ticket to make the headers explicit in subsequent versions.
curl https://api.acme.com/v1/person curl https://api.acme.com/person \ -H'accept: application/vnd.acme.org.person+json;charset=utf8;version=1.0'
Testing an API with versioned URLs versus versioned media types.
Big picture, though, when a small investment potentially has a big payoff — thanks to the law of unintended consequences — that’s a good bet. And, in this case, there are other benefits besides.
When you think of your API as providing access to an information graph that’s overlaid onto the Internet, that means it exists alongside other such information graphs. That’s what it means to create more Web, connecting your little bit of that graph to those offered by other APIs, much the way this Web site can link to that one.
But we don’t typically think of our APIs as being part of such a graph. Proponents of replacing HTTP with something else, like to point out that Fielding himself says HTTP is best for multi-organization scenarios:
REST is useful because it induces certain properties that are known to benefit multi-org systems
To begin with, this doesn’t mean those properties aren’t useful regardless. But it also turns out there are benefits in thinking like clients of your API are being developed by a different organization, even if it’s just Sally sitting next to you.
Conway’s Law states that organizations build software that reflects their internal structure. Inverting that, if you want more modular software, you start with modular teams. So, for example, if you want to get serious about microservices, each service should be developed by separate teams. Otherwise, you’ll just end up with another monolithic API, except now your dev ops team will hate you just a little bit more than they already do. (Just kidding. They don’t really hate you. Except when you say it works on my machine.)
Imagine for a moment that we have a person microservice, which is used across several other microservices. If we need to change our person microservice, do we really want to require that all the other services be versioned, too? That seems to entirely defeat the point of microservices. If we instead version media types, not URLs, updates to our Person API are minimally disruptive. The information graph is largely unchanged.
And, of course, modularity is a desirable property of software, regardless of whether there is one developer or one thousand.
There are other benefits, too. For example, we like to define schemas corresponding to our media types. That way, in addition to making incremental changes easier, we can validate requests, dramatically improving both security and reliability. We can even do this in a gateway server (by which we mean a proxy providing application-level security), reducing both the likelihood that malicious attacks reach a privileged process, and the resources they consume in the attempt.
You’re not a bad person if you put version numbers in your URLs (just don’t call it RESTful if you do). The tooling for HTTP APIs could be better, which makes it more difficult to do the right thing than it should be. (In a related story, we have an exciting announcement coming soon.) But there is an alternative, and it’s not rocket science, just media types and headers and responses with links.
And if we build our APIs the way we build Web sites, as hypermedia, the possibilities are — well, we don’t really know because most developers are still not doing that. But think of how vast the Web is today, how much information is at our fingertips, a vast graph of hypertext. Now imagine an equally rich graph of data — that’s the opportunity. In that world, changing the URLs is like changing the URLs for a Web site — it breaks the Web.
And, yes, this is basically linked data, except that you don’t need JSON-LD or SPARQL or RDF to build APIs that are basically forming information graphs. You really just need links in responses. That, by itself, doesn’t make it easy to query your data or create shared ontologies or address cache invalidation issues. But it does at least lay a foundation, and it’s relatively easy to implement. And if you build on that foundation by versioning your media types, you can incrementally introduce changes to your APIs without an extinction-level versioning event.