
Top 10 Ways to Accidentally Break an API
Identifying API Breaking Changes

Photo by Raghavendra V. Konkathi on Unsplash
If you’ve ever built or used APIs, you’ll quickly come across the idea of breaking and non-breaking API changes. The straightforward definition is that a breaking change will break client integrations. That said, depending on how a client is implemented, any change could break a client, so this answer is too simplistic. What folks really mean is that the change shouldn’t break a client that was implemented in a reasonable way, but what does that mean in practice? Sometimes non-breaking changes are also referred to as additive changes, which is a good rule of thumb for most cases, but still doesn’t always work.
In this post, I’ll discuss some of the more common types of breaking changes. I’ll skip some super obvious ones, like removing an entire endpoint so that we can focus on a bit of the nuance.
- Add additional server-side validation. This includes anywhere the server adds new or makes existing validation stricter. In cases where the server is simply adding validation for a case that never worked, it’s likely a non-breaking change (e.g., adding validation that a maximum must be a non-negative number). Still, any new limits or restrictions that are added or tightened would be breaking changes. For example, decreasing your maximum allowed limit would be considered a breaking change, but increasing it would not. Similarly, no longer allowing new usernames to end in
123would be a breaking change. - Add new required fields. Adding newly required query parameters, request headers, or request body fields are all breaking changes. Additionally, if an existing query parameter, request body field, or request header is now required but wasn’t previously, that is also a breaking change. Any of these may be added as optional assuming the default response does not change when the parameter is not included. For example, if your endpoint adds a sort query parameter that can be
nameorcreatedAt, but previously only returned the list sorted byname, if the sort is not included in the request, the response should still be sorted byname. - Remove response body fields or response headers. Removing anything the server previously sent is nearly always a breaking change.
- Change permissions. In the same way that making it so that a particular user can no longer do a specific action in the UI — approve a change, for example — changing it so that a subset of API keys can no longer do an action is also problematic. It is considered non-breaking if permissions become more permissive or if new permissions are added that only apply to new functionality added at the same time. Otherwise, it’s wise to be careful of permission-related changes.
- Remove an auth type. This is similar to the previous one, but if you have multiple authentication formats that can be used by your APIs, such as OAuth, Bearer Token, etc., removing one of them will cause tokens that worked previously to no longer work, and that is a breaking change.
- Remove query parameters. This applies to both required and optional query parameters. If the client could previously filter based on a query parameter but no longer can, even if no error is thrown, this is considered breaking. Without the query parameter, a different response set would be returned.
- Change the type of a field. This includes fields in both the request body and the response body. This could be changing a number to a more generic string, or more commonly, it’s changing a single value to an array. This is why you might see something in a resource body like
ownerandadditionalOwners, which is a strong indicator that the team decided to change a field with a single value into one that could have multiple in a non-breaking way. - Remove an Enum Option. This one is a bit more nuanced. For example, if the enum is what type of audit log this is for, and the server is no longer sending
folderaudit logs because it removed its folder feature, that would not be a breaking change. However, if the client tries to filter byfolder, the server should still treat this as a valid option and return nothing (as opposed to returning 400 for an invalid type). On the request side, if the client can create audit logs, the client should still be able to create an audit log of typefolderfor it to be a non-breaking change. Note that changing an enum value is equivalent to removing one of the enum values and adding a different one, so the same rules apply. - Significantly change behavior. In many cases, these may not directly break the client, but should still be considered breaking changes. While this could encompass a lot, and probably shouldn’t be possible in a well designed API, it would be something like a case where previously a POST just created a user, but now the POST creates that user as well as creates a user group and adds the user to that group (or vise versa). Another example would be a case where previously a DELETE only deleted a folder if it had nothing in it, but now the DELETE cascades down.
- Change the return type or response code. Like many of the rest, changing the object returned from an endpoint or the response code is typically a breaking change. There are a few exceptions; for example, changing an error from a
5xxto a4xxprovides more information to the user and is almost always acceptable. Similarly, errors are almost always handled the same on the client side, so even changing from a403to a404is unlikely to cause issues (although it might, so I would probably classify this one as a maybe-breaking change). However, changing success codes, such as from a200to a204or back, is likely problematic. More trickily, a201to a202would also be a breaking change since the created object is no longer returned (and the object is not immediately created).
There are many ways to create breaking changes in an API, and many of them are nuanced. This is just a top 10 list. What other ways have you found?
If you’re interested in this topic, also check out my posts on REST API best practices and HTTP Status Codes.
