Principles of Good API Design
This blog post was automatically generated (and translated). It is based on the following original, which I selected for publication on this blog:
Everything I know about good API design.
Principles of Good API Design
Designing APIs is a delicate balancing act between ensuring ease of use and maintaining long-term flexibility. An API should be so familiar that developers can intuitively use it without extensive documentation. However, APIs are difficult to change; modifications can disrupt users' software. This tension requires API designers to prioritize simplicity while also considering future adaptability.
The Imperative: "WE DO NOT BREAK USERSPACE"
The cardinal rule of API maintenance is to avoid breaking userspace. Altering or removing fields can cause widespread failures. The principle is similar to Linus Torvalds' famous slogan, emphasizing the duty to protect downstream consumers from harm. A single breaking change can ripple through countless dependent systems.
How to Evolve APIs Responsibly
When significant changes are necessary, API versioning offers a solution. By serving both old and new versions simultaneously, existing users can continue using the old version, while new adopters can opt into the new one. While versioning allows for evolution, it introduces complexity for both users and maintainers.
Each new version adds to the maintenance burden, and users may find it confusing to navigate documentation across different versions. Thus, API versioning should be a last resort.
The Product-API Relationship
The success of an API is intrinsically linked to the product it serves. People use APIs to achieve specific goals. If the underlying product lacks value, even the most elegant API will not attract users. Conversely, a highly desirable product can succeed despite a flawed API.
Product Design Influences API Design
A well-designed API mirrors the underlying product's structure. Poorly designed products often lead to awkward APIs, exposing internal complexities to consumers.
Practical Considerations
- API Keys: Implement long-lived API keys for authentication to ease the initial integration process.
- User Base: Recognize that API users include non-professional engineers. Simplify complex processes like OAuth handshakes.
- Idempotency: For critical operations, support idempotency keys to ensure requests can be safely retried without creating duplicates. Idempotency should generally be optional.
- Rate Limiting: Implement rate limits and kill switches to protect against abuse or unintentional overload.
- Pagination: Utilize cursor-based pagination for large datasets to maintain performance.
- Optional Fields: Allow users to specify which fields are included in the API response to reduce the overhead of expensive-to-serve data.
Internal APIs
Internal APIs cater to a different audience. Modifications are safer due to the smaller user base and the ability to update code directly. However, internal APIs should still prioritize idempotency for key operations.
Conclusion
Effective API design balances flexibility and ease of use. Prioritize stability and avoid breaking changes whenever possible. While versioning offers a path for evolution, it should be used judiciously. Remember that the value of the underlying product ultimately determines the API's success. Designing good APIs is a combination of technical skill and understanding the needs and capabilities of the users.