I recently managed to take a feeling I’ve had about API design and formulate it into a specific recommendation: Be explicit about state when crossing system boundaries.
My API Design Scenario
One of the software packages I support is an API that provides data to a collection of small (physical) displays. The displays are deployed around a customer’s site, and they poll the API regularly for information to show. Since the hardware is out there, it’s hard to update if we want it to show something else.
The actual deploys on real customer sites varied much more than the testbed we had here for development. It’s no surprise that things differed, but they differed dramatically across different customers, and the software doesn’t handle every case.
My API Design Problem
The key problem that we hadn’t anticipated was a partial failure mode. We’ve found that, due to various complexities of setup (user accounts, permissions, rate limiting, and other things), an “all or nothing” approach wasn’t optimal. In other words, even if we can’t send 100% of the data, it’s still valuable for our displays to show 90% of it.
However, the hardware does not handle this option very well. While it’s easy to update the API so it can only send the data it’s able to, the hardware can only do one of two things:
- Show the data sent over
- Show an error on the display (if the HTTP code is an error status)
Unfortunately, there’s no way to fail partially. We can’t show and update the data, while still indicating that the unit needs attention. Adding this capability is something we will consider for a future release.
My New API Design Rule
Hence my new rule: Be explicit about state when crossing system boundaries.
Don’t automatically infer “error” from your data. It’s much much easier to be nuanced when you have a field where you can put data.
This isn’t meant as a suggestion to make a field for every single thing. It’s certainly okay for calculations to happen in a remote client.
The important thing is that key system states (success, failure, online, up to date, load, and similar metrics) are explicit. Format strings, human labels, colors, and other things that are just “data” might not matter, but to avoid unnecessary coupling, anything that will drive an interaction should be explicit in your data.