API Design: Begin With the End in Mind
At WDT, we currently have around ten different APIs for accessing a variety of weather data. Each of these APIs was designed and built with specific use cases in mind. Why do we do that? It’s because accessibility matters a lot. You can have the most technically sound API ever developed, but if it’s not very usable, you’ve wasted time and energy. You cannot just build one API and have it be right for everyone. As Steve Yegge has said:
When software — or idea-ware for that matter — fails to be accessible to anyone for any reason, it is the fault of the software or the messaging of the idea. It is an Accessibility failure.
However, most of our APIs were built for internal purposes. As we begin to expose these APIs to clients, one of the things we’re trying to get better at is the UI experience for the consumers of those APIs. To do this, we’re undertaking a user-centered design approach. We have to understand the users and their scenarios.
How do we go about this? We begin with code samples of how the API will be used. The API design should start with these code samples in mind, and should drive the design decisions. Microsoft has actually spent a lot of time and effort studying API interface design¹. They have identified 3 different personas of developers:
- Systematic programmers work from the top down, attempting to understand the system as a whole before focusing on an individual component. They program defensively, making few assumptions about code or APIs and mistrusting even the guarantees an API makes, preferring to do additional testing in their own environment. They want not just to get their code working, but to understand why it works, what assumptions it makes and when it might fail. They are rare.
- Pragmatic programmers are less defensive and learn as they go, starting working from the bottom up with a particular task. However, when this approach fails they revert to the top-down approach used by systematic programmers. Pragmatic programmers are willing to trade off control for simplicity but prefer to be aware of and in control of this trade-off. For example, pragmatic programmers often use tools such as graphical layout editors but prefer to be able to edit the automatically generated code in case they need additional control later.
- Opportunistic programmers work from the bottom up on their current task and do not want to worry about the low-level details. They want to get their code working and as quickly as possible without having to understand any more of the underlying APIs than they have to. They are the most common persona and prefer simple and easy to use languages that offer high levels of productivity at the expense of control.
So what does this mean to us, as API designers? As mentioned above, some of that will depend on the user persona that the API is targeting. Access to a standard weather API such as current conditions and forecasts will require a different interface than an API that provides access to multiple models and parameters. In one case, you might have an “opportunistic” mobile developer that needs current weather information and forecasts, such as used in WDT’s Weather Radio mobile app. They will probably want what all standard weather APIs offer: A single call to get a rich variety of weather parameters. A “systematic” data scientist on the other hand, might want an API that offers a more detailed access to the underlying weather models, and to be able to pick and choose which model they want.
However, this doesn’t mean we should show users the implementation details. Application implementation details should not drive the design of the user interface and should not be present in the interface design. Too often, a RESTful API masquerades as a thin veneer over a database. This isn’t necessarily bad, and most of the introductory tutorials for building REST APIs begin with this exact tutorial, but I shouldn’t be able to guess the normal form of someone’s database design based on their API. The flip side of this coin is that the underlying architecture of the system needs to be flexible enough to accommodate whatever interface we desire.
To prosper, an interface must be well suited for its task — simple, general, regular, predictable, robust — and it must adapt gracefully as its users and its implementation change. — Kernighan and Pike, The Practice of Programming