The use of API specification tooling like the OpenAPI Specification has revolutionized the way we design, build, and manage APIs. In addition to helping us plan out functionality, generate documentation, and run tests, a specification can serve the all-important role of the API contract: defining precisely what consumers can expect from our API.
Unfortunately, current API specification formats have a lot of room for interpretation, says Microsoft API architect Gareth Jones. As a result, developers don’t always understand API provider expectations. This can lead to some unintentionally broken client applications.
In this article, we’ll look at four specific areas in which API specifications can fall short, and discuss how just a little bit of forethought can help avoid breaking changes.
Problem #1: The Number of Items
Most APIs have at least one endpoint that only returns one item the majority of the time. For example, consider a banking API with an
accounts endpoint. This endpoint is most likely to return data for a single bank account since most customers only have one. Developers love shortcuts, so — given the normal behavior for these endpoints — they might write their code to expect just one item. Then, in cases where there is more than one item, their code breaks.
You’ll see the same phenomenon just as often with pagination. Very often, endpoints return less than one page of data, so that’s what developers will code for. But when a response comes back that spans multiple pages, their code will break.
According to Gareth, this issue isn’t something API specifications like OAS always account for, and yet, it’s a common pain point. Part of the reason for this is that API documentation often showcases over-simplified sample response data. The lack of information therein encourages developers to take shortcuts.
The solution for this is simple: provide better communication on the number of items and pages developers can expect from each endpoint in your documentation. To that end, include sample responses that reflect the entire spectrum of variability for API responses.
Problem #2: The Size of Things
Another issue your specification probably doesn’t account for is the size of each response. Size becomes particularly relevant in the case of multimedia APIs. Developers can easily code their applications against low-resolution test images, but will they be able to handle it when the API returns a high-resolution image one hundred times bigger?
Once again, solving this means communicating what size constraints developers have to code for in your documentation. And since developers don’t always read the docs, make sure that any sample data reflects the bigger payloads they’ll need to code against.
Problem #3: Performance
Developers don’t like slow APIs. Slow API performance means slow apps, and when implementations make use of multiple calls, the delay is magnified for end users.
However, a sluggish API update can quickly become a breaking change if your consumers rely on your responses as part of a sequence, especially if developers have taken any shortcuts. Sure, most apps will be programmed to wait for a response before going any further, but what if they aren’t and your slow response prevents further code from successful execution?
Funnily enough, you can also break consumers’ applications by optimizing your API’s performance! Developers might have written code against you responding after some expected latency, and — by speeding up your API — you introduce a race condition in their application.
Fix this issue by setting performance standards for your API and being consistent with them. If you’re going to update your API to be faster or slower than the limits specified in those standards, give developers due warning and version the change.
Problem #4: Authorization
Authorization can be another problematic area that your specification doesn’t cover. In particular, the time for which an access token is valid seems like one of those things you could change without breaking anything… right?
Not always! Developers may have taken the easy route in writing their code: performing longer workflows with a single access token, instead of asking for refresh tokens. But then again, they never needed to in the past, so why bother?
To resolve this problem, encourage the use of refresh tokens throughout your documentation. Also, as above, warn your developers before making changes like this.
More Advice on Avoiding These Breaks (And Others)
In addition to the specific solutions outlined above, Gareth sees a few principles which might help us avoid breaking our consumers’ applications in other areas. The ideal fix, of course, is to use specification formats that document these finer details or at least add extensions to our existing specification tools to do so. However, that’s not all we can do.
Another suggestion Gareth proposes is to embody variability — fast, slow, big, small, one item, many items — in a dedicated mock API. The resulting mock would be purposefully difficult to build against, but consumers would know that their applications will hold up in the real world if they hold up against the mock. As Gareth says, we’d rather apps fail in development and work in production than the other way around.
Alternatively, you can pay closer attention to logs immediately following a new deployment. Aside from looking for the usual — 400s and 500s — Gareth suggests looking for any signs something unusual could be happening: more or fewer calls than usual, larger or smaller packet sizes, and other such changes.
Finally, Gareth suggests you redefine your bar for breaking changes. If your priority is business continuity, make your bar incredibly sensitive, and be aware of small changes to uncontracted areas like those above. If you prefer to move fast and break things, then do that!
An API specification often fulfills the role of the API contract, but there’s no doubt that today’s specification tooling falls short in some areas. In particular, issues like the number items, size, speed, and authorization tend to be poorly-defined (if at all) in current specifications. Forgetting to define them can easily result in accidental breaking changes. While improvements to and extensions for modern-day specification formats may solve this problem in the future, Gareth has several alternative suggestions: difficult mocks, log tracking and rethinking how you approach the concept of a breaking change.