Handling Breaking Changes in an API

Photo by dylan nolte on Unsplash

An API is an Application Programmable Interface. It’s a critical piece and pretty much the standard now. You’ll see a lot of companies leveraging these APIs to deliver content or data to their users. They hold the business logic while the mobile apps (like android, iOS) contain information on how the data needs to be presented to the users.

The business is ever-growing and the market keeps changing. To keep up with the market demands companies need to keep pushing out new features or enhance existing features.

The mobile applications are installed on the user’s device, which means that to roll out the smallest of things, they would have to release a new patch of their applications. In a lot of cases users would not update their applications, and not everyone wants to roll out a mandatory update. This makes backward compatibility a necessity.

The backend API changes need to be backwards compatible to the mobile applications that are running on older versions of the API.

We know an API has breaking changes when:

  • A key from the response JSON has been removed.
  • A mandatory parameter has been introduced.
  • A mandatory field in the JSON body of the request has been introduced or was optional before but now is mandatory.
  • The API URL or its path has changed and cannot be handled by a redirection (can be solved using proxies, which out of scope for this post)

Notice, the addition of new keys to the output of the API response or the addition of optional parameters are not considered as breaking changes because it would not impact existing consumers of the APIs.

There are 3 main approaches to handle breaking changes:

  • API Versioning.
  • Version as request parameter or header.
  • Feature Toggle Parameters.

To ease out the explanation of the three approaches, I am going to use an example. Let’s assume we have the below API of which the response is poorly designed and we need to change the API response to a more readable JSON body

URL: https://subdomain.domain.com/api/users/a1b2c3Old JSON Response:
{
"uId": "a1b2c3",
"nm": "Carrot Radish",
"usrnm": "iam.carrot",
"eml": "carrot@radish.com"
}
New JSON Response
{
"userId": "a1b2c3",
"name": "Carrot Radish",
"username": "iam.carrot",
"email": "carrot@radish.com"
}
  • Remember we have mobile applications in production
  • Once the API is ready, we would integrate the changes in the mobile apps but that would take an overall time of 1 week due to testing cycles.
  • Also, the product team does not want to release a mandatory update and so the backend team would have to maintain both versions until the acceptance rate of the app update crosses 75%.

API versioning is adding a version to the URLs of the APIs. This allows mobile apps to reach out to specific versions of the backend service and ensure user experiences are not affected.

https://subdomain.domain.com/api/v1.2/users/a1b2c3

Notice the v1.2 as part of the above API URL, this way the backend team can develop a new endpoint, and then the mobile team can integrate it without impacting the older patch. I recommend looking at Semantic Versioning for this.

Another way would be to pass the version as a parameter or header as part of the request and the controller can act accordingly.

  • As header: The RFC-4229 Section 2.2.17 has provisionally registered Version being passed as a header.
  • As parameter: You can also optionally go with a ?version=1.2 kind of a parameter.
https://subdomain.domain.com/api/v1.2/users/a1b2c3?version=1.2

In both cases, the control lies with the controller and the developer writing the controller. So the application team need not maintain two different endpoints instead of a simple switch statement. Another advantage is that you could simply keep making non-breaking changes to the API and both the old and the new versions would support it, only their output schema would be different.

This is not an industry term but a term that I like to use for the approach. The version need not be passed instead the new functionalities can be toggled on/off based on parameters. Using definitive parameter names can impactfully help in increasing the readability of code and predicting the behavior of the request. Below is an example:

https://subdomain.domain.com/api/users/a1b2c3?newSchema=true

where the newSchema is an optional parameter and defaults to false.

You can also leverage valueless parameters and change the newSchema=true to enableNewSchema and make the API URL more readable, like below

https://subdomain.domain.com/api/users/a1b2c3?enableNewSchema

Some more examples of using valueless parameters can be if we want to remove the email address from the output we can provide another valueless parameter ignoreEmail

https://subdomain.domain.com/api/users/a1b2c3?enableNewSchema&ignoreEmail

The above URL would respond with the new JSON schema and would not provide the email as part of the response.

Remember to selectively include or exclude fields from the API response leveraging GraphQL is highly recommended but the valueless parameters would also be a way to achieve this, in case you do not want to switch to GraphQL.

This solution provides the same advantages over the solution 1 as the solution 2 did with the bonus of better readability and easy maintenance.

A comparison of the three approaches that we discussed

Now I know from the above comparison it seems that I’d always either recommend solution 2 or solution 3. It has nothing against the solution 1. On the flip side, the solution 1 is widely used across the industry.

The only reason why I prefer the other two solutions is that it’s way easier to change stuff on the backend with those approaches, especially if the API is being used by multiple teams and they all have different change requests that have breaking changes.

I don’t need to keep getting the URLs or parameters changed from the mobile teams or rather wait for them to migrate to the new API. I just build, test, deploy, and inform them to change and they can take their own sweet time in doing so.

The goal of this article was to lay out the different strategies for handling such cases and not to downplay either of them. Do you have any other strategies? Please feel free to use the comments section to share. I’d love to hear about them.

Like this article? Follow @iam_Carrot on Twitter. Maybe click the 👏 button a few times to show your support ⬇⬇

A Tech Enthusiast, develops software at Amazon, made a computer print “Hello World” once; didn’t leave software since. Such an obedient fella that computer 🖥️