From feature request to API design
Turn specific feature requests into durable APIs.
· 5 min read
Open-source library ideas often start as user requests. The trap is implementing the request exactly as described. You ship something that solves one person's case, but the library now carries a feature everyone else has to understand.
This post is about turning a narrow request into an API that fits more people. The example comes from Medium Zoom, a library I created and maintain. It is written for library authors, though the same instincts help in product work too.
The term "users" in this blog post refers to the users of library APIs (i.e. developers).
Keep the problem you're solving in mind
A library should stay focused. A useful way to avoid drift is to write down the project's philosophy.
For Medium Zoom, the philosophy is simple: reproduce the image zoom experience from Medium in vanilla JavaScript.
Every accepted feature makes the next one easier to accept. Start by asking whether the existing API can solve the user problem. If it cannot, and the demand keeps appearing, look for the broader use case. To do that, dig into the why.
That philosophy gives you a boundary. It says what the library offers and what it does not. When building a library, aim to minimize your API surface area. The fewer concepts users need to learn, the more room they have to compose.
Understand your users' needs
It is easy to misunderstand a request if you jump straight from issue comment to implementation.
Users often describe the solution they want, not only the problem they have. That solution can be too specific to their app.
By default, Medium Zoom shows a clean overlay with only the zoomed image. In 2017, a user asked for a close button on the overlay. Adding a closeButton option would have solved that issue, but it would have been tied to one UI decision.
After asking what they were trying to build, the better API became templates. Templates let users zoom inside their own container and render additional elements around the image.
That solved the close-button case and opened the door to richer examples, including a Facebook-style zoom and a Dropbox Paper-style zoom.
Do not stop at how users want to use your API. Ask why they need the feature.
Don't be too specific
Building a library is different from building an application because you cannot see every use case ahead of time. You are designing a tool other people will compose in contexts you do not control.
A good API gives developers enough power to build on top of it without exposing so much surface that the abstraction disappears.
Creating an API that is too specific is known as overfitting in statistics:
In statistics, overfitting is "the production of an analysis that corresponds too closely or exactly to a particular set of data, and may therefore fail to fit additional data or predict future observations reliably".
The Medium Zoom close-button request was overfit. It solved one visual requirement while adding a permanent option to the public API.
The more you add, the more users have to learn. A useful way to keep flexibility without adding one option per request is inversion of control: let users provide part of the behavior instead of hardcoding every variant.
Start with documentation
Start by documenting the feature from the user's point of view.
When designing Medium Zoom, I spent a lot of time writing documentation before implementation. It forced the API to cover the cases I cared about and made the boundaries visible before code existed.
This keeps you focused on the API you wish you could use. Implementation details will try to bend the design toward what is easy internally. Documentation gives you a cleaner target before those constraints take over.
This reasoning makes you forget about what you already know and leads to creative ideas. It comes close to first-principles thinking:
First-principles thinking is one of the best ways to reverse-engineer complicated problems and unleash creative possibility. Sometimes called “reasoning from first principles,” the idea is to break down complicated problems into basic elements and then reassemble them from the ground up. It’s one of the best ways to learn to think for yourself, unlock your creative potential, and move from linear to non-linear results.
Do not rush to implementation. Document the usage first. This technique is called README-Driven Development.
Tips
Start small before creating a complete API. Avoid covering use cases that have not proved necessary yet; immature problems often produce the wrong abstractions.
It is easier to add a feature than to remove one. Once an option ships, users build on it, examples mention it, and maintainers start depending on it too.
If you are unsure, expose the idea as unstable and gather feedback. If a stable API needs to go away, it usually requires a major version. Either way, explain why the feature is changing and how users can achieve the same result with the replacement.
Conclusion
When creating or extending an API:
- Know which problem the library is meant to solve
- Ask why the feature is needed
- Prefer a general primitive over a narrow option
- Document the API before implementing it
These are lessons I learned working on open-source libraries, including Medium Zoom and the InstantSearch suite at Algolia: InstantSearch.js, React InstantSearch, Vue InstantSearch, and Angular InstantSearch.