I recently worked on a project to define APIs for a Google Cloud product. Having mostly worked on graphical user experience or APIs for consumption by internal teams, I really enjoyed the experience of exploring the subtleties and uniqueness of defining external APIs.
Unfortunately, not much has been written about API user experience design in the public forums, and whatever has been written doesn’t go beyond reiterating coding best practices. That’s why, in this blog, I will talk of three distinct design principles for designing the experience of an API user.
1. “Simple-to-start” vs “Code-breaks-later”
In contrast to Graphical interface users who interact with the interface regularly, API users prefer “write-and-forget” APIs. They will most likely revisit the code only if it breaks, which should be avoided at all costs. Therefore, unlike GUI, APIs have to be designed for non-repeat interactions.
In general, a better experience in API requires stricter design in contrast to the GUI.
In a typical GUI experience, users generally click and play with the interface and learn slowly. Even if the user is not following an efficient process or not supplying the right parameters in the first few attempts, it’s fine. Idea is to give users more freedom to experiment with the product even if it’s a possibly flawed process. That’s why most of the popular consumer Apps – Facebook, Gmail, Maps, Uber, or Headspace will let you click anywhere or do anything with the product.
In contrast, for API users, feedback should be given quicker. Rather than letting users continue with a possibly flawed process, we should fail imperfect API calls with a relevant error (fail-fast design). That way users can avoid writing code that breaks later.
2. Initiated by human but consumed by machines
One quirk of designing an API user experience is the duality of consumption. Initial onboarding has to be designed for humans who will write the code. However, subsequent consumption would be via a program running this code. The program just cares to keep running, and perhaps never break. The human who consumes initially, on the other hand, had a more complex set of requirements.
The onboarding happens either through the GUI or developer documentation. Some best practices here include:
- As quickly as possible the documentation should jump to code examples. Even the GUI experience should lean toward how code can be written to replicate the GUI behavior. For example, in the Google Cloud console, users get an option to generate a REST command for the action they just performed.
- Just mentioning APIs in CRUD format is okay, but a step further would be to illustrate code examples for critical user journeys. For example, if CUJ (critical user journey) is creating resource A, listing resources B, and updating resource C, the documentation should illustrate the corresponding API calls.
- When in doubt, lean toward simple user flow focussed on providing a good learning experience.
3. “Simple, orthogonal interactions” vs “complex, under-the-hood interactions”
In GUI, it’s a good practice to abstract complex sets of working behind simple clicks. It’s okay for this click to result in multiple effects – creating a resource and deleting other resources. As long as it aligns with the mental model of the user, it’s okay to combine complex sets of action.
In contrast, a better-designed API results in just one effect (or action) and avoids side-effects. In other words, API users prefer orthogonally defined APIs. Calling an API should change just one thing without affecting others. If that’s the case, a relatively small set of primitive constructs can be combined in powerful ways by developers to suit their needs.
For example, if calling POST resource A doesn’t delete resource B, users can orchestrate the behavior of their choice by making two independent API calls.
In essence, two API calls to orthogonally defined APIs are better than one API call that results in a complex set of changes.