Hi Friends,
Welcome to the 128th issue of the Polymathic Engineer newsletter.
Nowadays, APIs are everywhere, and they are crucial to your success when building a system. But what happens when there is a change to the API, or one of the consumers requests the addition of new features to the API? This is where versioning comes into play.
This week, we talk precisely about API versioning. The outline is as follows:
Why and When Version Your APIs
Versioning Strategies
Implementation Methods
Semantic Versioning
Lifecycle Management
Tools
Fast, Open Source Analytics – Fully Managed with Aiven for OpenSearch
This issue is brought to you by Aiven for OpenSearch®, the fully managed, open source alternative to Elasticsearch – built for high-performance, distributed search and analytics. Spin up production-ready clusters in under 10 minutes, deploy to the cloud of your choice, and offload the rest to Aiven so you can focus on building, not managing infrastructure.
Get started with OpenSearch® on Aiven – your first $100 is on us
Why and When Version Your APIs
As your application evolves, you’ll certainly need to make changes to your APIs, such as adding new features, fixing bugs, or improving performance. However, once other developers begin building applications that rely on your API, any change may break their code. This is why API versioning is so important.
Think about this situation. You have built an API that many websites use to display a wealth of financial data on their dashboards. Your API returns values in Dollars right now, but you've chosen to add Euro as well. If you just change format of the response without versioning, you could break any application that needs the old format.
In general, You don't have to make a new API version every time something changes, but you might want to do so for changes that break things. Examples are removing or renaming fields in the response, adding new required parameters, or changing how an endpoint works in a basic way.
Most of the time, changes that don't break things don't need a new version. Examples are adding optional fields to a response or making an API run faster without affecting how clients use it.
In the case of the financial API, adding an optional Euro field wouldn't break existing applications that only use Dollars. However, renaming the Dollars field to "currency" would break applications that expect the original field name.
While versioning protects users from breaking changes, creating too many versions can lead to its own problems. Each version you maintain adds to the time and cost of development and testing. In addition, multiple versions can confuse developers about which one to use.
This is why a well-thought-out versioning approach is so important: it helps you find the right balance between stability and new features. The goal is to improve your API without making things harder for your users than they need to be.
Versioning Strategies
You can choose between two main ways to handle changes to your API: the additive change strategy and the explicit versioning strategy.
The additive change method is about adding new features or fields without changing what's already there in your API. Any changes you make to your API must still work with older versions.
Things you can't do are removing or renaming existing fields, changing data types or error codes, and changing how endpoints fundamentally work.
Things you can do are add new optional fields, add new endpoints, and make features optional with opt-in parameters.
Let's see how this last point works with an example. Suppose that your financial API returns this response:
{
"stock": "AAPL",
"price": 150.25,
"currency": "USD",
"market_cap": 2500000000
}
After some time, you realize that not all users need the market cap information, which is causing an extra network load. With the additive change strategy, you can't just get rid of this field because some clients may need it.
Instead, we could add an optional query parameter like exclude_market_cap=true
. It will work like this: clients who want to lower their network load can choose to use the new behavior; other clients will not have to make any changes.
The additive strategy works well for smaller APIs that don't change much. However, it can become challenging to use as your APIs become more complex.
The explicit versioning strategy allows you to maintain multiple versions of your API at the same time. When you want to make a breaking change, you release it as a new version. This gives you more freedom to evolve your API.
The explicit versioning strategy works well for enterprise applications and products that require significant evolution over time. Companies like Stripe and Slack use this approach because it provides the flexibility needed to make substantial improvements while still supporting existing users.
In the following section, we will look at some ways to implement explicit versioning.
Implementation Methods
There are three primary methods for users to specify which API version they want to use.
The most common approach is the URL path versioning. Here, you add the version to the URL path, like this:
https://api.example.com/v1/stocks
https://api.example.com/v2/stocks
The version identifier (v1, v2) comes before the resource name. This makes it apply to all resources and methods in that version. The benefits of this approach are that it is clear and easy to understand. It makes debugging simpler since you see the version in any API call. It also works well with API documentation and testing tools.
The downside is that the resource URL changes with the version. This is not ideal if you want to use these URLs as permanent links. Despite that, many popular APIs use this approach, including GitHub and Stripe.
Instead of putting the version in the URL, another option is to use a custom HTTP header, like this
GET /stocks HTTP/1.1
Host: api.example.com
Api-Version: 2
This keeps URLs clean and consistent across versions, in line with the idea that the URL should identify the resource, not how it's represented. However, using a custom header is more complex for API consumers to implement and can cause issues with caching if not done correctly.
A third option is to use a query parameter to specify the version. Even though this is easy to set up and keeps the base URL the same, it does have some problems. People usually use query parameters for filtering, not versioning, so documentation may not include them, or they may clash with other query parameters. This method is less common but is used by some APIs, including certain Google APIs.
Each implementation method has its pros and cons, but the most important thing is to be consistent. Most major APIs use URI path versioning because it's visible and straightforward. However, some offer multiple options to accommodate different use cases.
Semantic Versioning
While versioning an API, you need a clear system for labeling each version. A popular approach is Semantic Versioning (SemVer), which uses a three-part numeric format: Major, Minor, and Patch.
Let's say your API is at version 2.0.0. Here's what each number means:
Major version (2): This number changes when you make breaking changes that aren't compatible with previous versions. For example, renaming or removing fields or changing how an endpoint works. For developers, going from 2.0.0 to 3.0.0 means they need to make changes to their code.
Minor version (0): This number increases when you add new features that are backward compatible. For instance, adding a new endpoint or an optional field. When developers move from 2.0.0 to 2.1.0, they can get new features without having to change the code they already have.
Patch version (0): This changes for bug fixes and small changes that don't add new features but fix existing ones. It should be safe for all users to switch from 2.1.0 to 2.1.1.
Using Semantic Versioning makes it clear to developers what kind of changes they can expect. When they see a major version increase, they know they need to update their code. Minor and patch versions should be safe to adopt without breaking anything.
Lifecycle Management
As your API changes and new versions come out, you'll also have to take down older ones at some point. When you release a new major version, you should set a timeline for how long you'll support the previous version. For instance, if you release version 4, you could say that version 1 will soon be no longer supported.
Usually, if you give developers 6-12 months to migrate to newer versions, it’s enough to plan and implement changes without rushing.
A practical way to communicate deprecation is through a Sunset HTTP header in API responses. This header tells developers when a particular version or endpoint will no longer be available:
Sunset: Sat, 31 Dec 2025 23:59:59 GMT
As soon as a developer sees this header, they know they have to change their interface. Requests for that version should either get an error message or be sent to an updated version after the sunset date.
Tools
Managing API versions manually gets difficult and error-prone very quickly. Luckily, there are libraries and tools designed explicitly for this purpose that come in handy.
The OpenAPI Specification, formerly known as Swagger, is a great way to describe and plan APIs. It has many advantages when it comes to versioning:
It is simple to keep track of changes over time since you can set up different definition files for each API version.
Interactive documentation makes it easy for developers to see what's new in each version.
You can use tools that compare different versions of your API specification to find changes that break things.
For example, you can use a tool like openapi-diff to compare two API specifications and see if there are any breaking changes:
openapi-diff original.json updated.json
This might show output like:
GET /stocks
Return Type:
- Changed 200 OK
Media types:
- Changed application/json
Schema: Broken compatibility
Missing property: price (number)
This tells you that removing the "price" property is a breaking change that require a new major version.
Besides comparing specifications, there are other tools that can help with versioning. For example API Gateways like Amazon API Gateway, Azure API Management, or Kong can handle versioning for you. They do this by routing requests to the proper backend based on the version information in the URL or headers.
Versioning your API may seem like a lot of work, but it's an investment in the long-term success of your API and the happiness of your users. A well-thought approach that stays consistent over time shows developers that you value their work and helps you keep their trust as your API changes.
Interesting Reads
Some interesting articles I read in the past days:
Versioning isn't optional once users rely on your API.
Simply put, Fernando!
API Versioning is important to know if we care about our customers.
Thank you so much for the mention! Appreciate it a lot! 🙏