Hypermedia APIs: Don’t version anything.

In my last post I, perhaps controversially, set the constraint that an API should not couple itself or document tightly to URI paths and patterns.  This comes as a stark contrast to many of the popular trends within the API space, but the long term benefits to the service over its life far outweigh the complexity and cost of implementation.  In this post I would like to discuss an additional constraint which is in part corollary to the last guideline: do not version anything at all in the service.

I will first start out by addressing the elephant in the room, this guideline also comes at a stark contrast to popular trends in the API space, as well as the established trend from some of the largest Silicon Valley technology companies.  These two guidelines add a great deal of complexity to initial API design and architecture for public APIs.  Most of these leading organizations are driven by concerns with speed to market, and therefor do not allow themselves the appropriate design time before beginning to build applications, or are far more concerned with a larger audience’s familiarity with a particular design strategy than creating a better API design.  These concerns are certainly important from a business perspective, however it is often incorrectly presented as a technical limitation and guideline, when in fact it is driven almost entirely by business motivations.

If the rush for faster minimum viable products is driven by a business concern, what are the technical benefits associated with a hypermedia web API style?  The often cited axiom in the hypermedia web API space is WWBD, or what would browser do?  This is particularly apt given HTML is probably the most familiar hypermedia format to any user of the internet regardless of their awareness of this fact.  Netflix and other continuous delivery champions are famous for deploying code to production hundreds of times per day, however as a user of their services you are never aware of any change in the service platform.  The only way this can be done is if you are never aware or tightly coupled to any type of versioning within the service interface.  Your browser never knows, or is concerned in any way with, the version of the Netflix software it is querying.  This point perfectly addresses the style of CRUD api which includes a version number within its URI pattern, but does not cover all ways to version.

The other shortcut often taken during API design is to apply a version to the MIME type itself, and has been demonstrated to be beneficial in the very near term.  This solution does manage the version disconnect at the service layer, but leaves un-patched clients unable to fully consume the service until new client versions can be distributed.  Even though this strategy is one we are very familiar with as API service integrators, it is extremely consumer hostile as all the service version management responsibility have been dumped on the consumer.  Worse still this type of versioning is unique to each integration, greatly increasing the difficulty of a service which aggregates functionality from multiple APIs to create some or all of its responses.  This strategy will solve your concerns for versioning, but it comes at a hefty cost to the API’s consumers, and if there is completion in the space, this poor experience could result in the loss of a client or consumer.

Both of these reasons taken in isolation or together should be enough to convince a reasonable designer of the importance of removing all versioning from their API.  However, there is a more fundamental reason to exclude versioning entirely and it goes back to the very first guideline.  Versioning is a solved problem within the HTTP application protocol.  I previously discussed the ETag strategy to perform cache control, but this is nothing more than a more specific form of representation versioning.  If part of the structure or field of a message representation changes between versions, normal validation processing should handle this change, and the client can update its local representation and model cache for the service from the service itself.  If a historical representation of a resource is required for audit or some other need, the memento header exists to handle requesting a resource and representation as it existed at a certain point in time.

Clearly this is not be the simplest solution, however it is a far more useful and standard way to version the resources and messages of an API.  By adhering to the standard way to perform this action, a sophisticated http consumer can always know the status of any data it holds locally and remain unconcerned about the version of the service running.  Furthermore, this sophisticated client can be used to consume other APIs which follow these guidelines, with little or no additional integration effort.

Leave a Reply