API versioning has no “right way” and I am fairly convinced that API evolution is usually a better option for both API authors and clients of those APIs. Evolving APIs smoothly without breaking any existing clients and making it easy for them to consume new features seems like the best of both worlds. GraphQL, for example, chooses API evolution over versioning.
At frequent and incremental release cycles it's usually easy to evolve an API without introducing any breaking changes, until it isn't. At one point or another evolving an existing endpoint can have costly underlying implications on the codebase. I've seen many businesses struggling to find a stable vision for their product which can lead to large shifts in business direction which means large changes in APIs - and remember business always wins. If some kind of versioning eventually becomes a necessary evil, how and when should one version ?
The answer is a range of possibilities. One that goes from the most conservative approach to more liberal ones.
Microsoft, for example, doesn't discard API evolution but dictates that all APIs support explicit versioning:
All APIs compliant with the Microsoft REST API Guidelines MUST support explicit versioning. It's critical that clients can count on services to be stable over time, and it's critical that services can add features and make changes.
As an organization, if you're following Microsoft REST API Guidelines, your APIs should support versioning already from the get go.
Next question is when to increment the version number. Microsoft says:
Services MUST increment their version number in response to any breaking API change.
This part sounds very trivial at first. But what is a breaking change ?
Teams MAY define backwards compatibility as their business needs require.
Clear examples of breaking changes:
- Removing or renaming APIs or API parameters
- Changes in behavior for an existing API
- Changes in Error Codes and Fault Contracts
- Anything that would violate the Principle of Least Astonishment
So backwards compatibility as a concept contains some clear objective areas as well as some subjective areas that are highly dependent on the nature of a business. It turns out that Azure follows a more conservative approach following Hyrum's Law and even defines the addition of a new JSON field in a response to be not backwards compatible. Office365 on the other hand perfectly allows it.
So one interesting key takeaway here is that even if you didn't change a single thing in your API interface (URL, parameters, payload etc.) but radically changed the behaviour of an existing endpoint, you have introduced a breaking change. So it's not always about mechanical changes but also about what that change means in your business domain.
ASP.NET API Versioning
One of the best ways to version a .NET API is to go to aspnet-api-versioning repo and use a package that's suitable to your Web API version. These packages make it very easy and elegant to introduce versioning semantics to an API.
For my experiments I've been using the sample project based on ASP.NET Core.
Azure API Manager
Azure API Management is an API Gateway Service in its essence but it can do a lot of things when properly configured. For organizations that are creating and managing large number of APIs, Azure API Manager can serve as a central point of discovery and governance.
Microsoft recently added versioning capabilities to API Manager and is supporting a few main versioning schemes such as url path, http header and url querystring. So now developers can add explicit versioning to their version-less APIs or publish their versioned APIs through API Manager for other benefits.
Such a service can be invaluable for an organization but for developers and operations it's yet another moving part to keep an eye on and maintain. All of a sudden, deploying your APIs isn't sufficient anymore and now you also have to properly publish and configure them through Azure API Manager at each deployment cycle. In the long run, integration with the API Manager can only succeed if all these steps can be automated as part of a continuous delivery pipeline.
One nice thing about API Manager is that it can automatically import APIs that have implemented the OpenAPI Specification. That makes a big part of the automation a piece of cake. One bad thing about API Manager is that it's a fairly young service in Azure with some rough edges, insufficient documentation and inconsistencies around automation. 80% of what you need is low hanging fruit, remaining 20% makes you cry - and you need 100% to be successful with it.
Open API Specification & Swagger
Here is a complete definition, emphasis mine:
The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic.
Yes I know OpenAPI Specification doesn't support Hypermedia and thus it can't be REST but let's accept the fact that most of us are building "RPC over HTTP APIs" and move on to the benefits.
Swashbuckle.AspNetCore repo is the home of Swagger tools for documenting API's built on ASP.NET Core. When you annotate your action methods using the constructs from this library you get a human readable interactive documentation as well as a machine readable JSON representation of you API surface. If you're running multiple versions of your API side by side Swagger got you covered as well. You can represent each version of your API docs separately.
Here's how a human readable Swagger API doc looks like, notice the dropdown that let's the reader switch to different versions.
And here is the same API spec (v3) in computer readable format:
After this introduction, in the next follow up post I'll talk about a frictionless developer experience for versioning .NET APIs and automatically publishing those versions to Azure API Manager. I'll share a PowerShell script that can run as a VSTS release step, automatically detect all available versions of an API and publish them to Azure API Manager.
Thanks for reading and until then, take care!
Update: Second part of this post is published. You can find it here.