Everything I know about good API design
Authentication & Credentials
- Long‑lived API keys vs tokens sparked heavy debate. Several argue a refresh‑token + short‑lived access‑token model is strictly more secure: smaller blast radius, leak containment (esp. logs/backups), forced rotation, better auditing, and natural rate‑limiting.
- Others note that a refresh token is just another long‑lived secret unless you build supporting flows and monitoring; the simplest model is still a static header key or PAT, especially for scripts and non‑engineers.
- Some propose treating the “API key” itself as a refresh token with a tiny extra auth endpoint, avoiding full OAuth/OIDC complexity.
- Mutual TLS is floated as an ideal replacement for shared secrets, but deployment and operational friction make it unpopular for many consumers.
- One contrarian view: for one‑off or classroom usage, allowing username/password auth directly to the API can dramatically simplify onboarding, with tight rate limits as mitigation.
API Versioning Strategies
- Strong split: some want versioning (“/v1”) baked in from day one for future‑proofing, discoverability, and the ability to deprecate old semantics cleanly.
- Others say most APIs never actually reach “/v2”; real‑world evolution tends to add fields/options, introduce new endpoints with better names, or replace whole services, making URL versioning mostly ceremony.
- Concern: multiple versions multiply maintenance and bug‑fix surfaces; better to treat new versions as a last resort and, if needed, implement old versions as shims atop new ones.
- Alternatives discussed:
- Version in headers or media types (e.g., custom “Version” header or vendor MIME types), keeping URLs as stable resource identifiers.
- Version in client headers with servers rejecting too‑old clients (useful for apps you control).
- Some object philosophically to “/v1/” in URLs because it versions the implementation, not the resource; others prioritize practical migration over purity.
Idempotency & Data Consistency
- Many insist idempotency support is essential, not optional; Stripe‑style idempotency keys are cited positively.
- Storing idempotency keys in Redis as a separate store is criticized: without an atomic write with the underlying mutation, certain failure modes (key written but DB change failed) break guarantees.
- Multiple commenters prefer storing idempotency identifiers alongside the domain data in the same transaction.
- Semantics of DELETE: some APIs always return 204 if the resource ends up absent (stronger idempotent feel), others prefer 404 for better client information.
Pagination & Cursors
- Cursor‑based pagination is praised for stability with concurrent inserts and for infinite scrolling, especially when cursors are opaque and can embed query state or routing hints.
- Downsides: hard to “jump to page N,” and more complexity versus simple page/offset.
- There’s frustration with tiny page sizes (or mandatory pagination) that force many sequential round‑trips; recommendation is generous defaults and pagination as a tunable option.
- On performance: OFFSET requires scanning/counting preceding rows, while “WHERE id > cursor LIMIT N” can leverage indexes more efficiently; some DB implementations might optimize, but not all do.
Stability, Users, and “Never Break Userspace”
- The “never break userspace” principle is widely endorsed but clarified: you must clearly declare what’s stable and can be relied on, versus internal/kernel‑like interfaces you reserve the right to change.
- Internal consumers are “real users” too; even if you can force them to update, churn still creates real cost, so dogfooding and upfront spec collaboration are encouraged.
- With internal APIs, instrumentation enables targeting specific consumers during migrations and sunsetting old versions more aggressively than with external customers.
Broader Meaning of “API”
- Several participants lament that “API” is now often used as shorthand for “HTTP+JSON web API,” whereas historically it referred to any application programming interface: libraries, syscalls, ABIs, in‑process interfaces, etc.
- Distinctions are drawn between API vs protocol, API vs ABI, and function signatures vs the higher‑level contract and usage rules.
- Some younger developers explicitly say they still primarily think of APIs/ABIs rather than web endpoints, while others find the web‑only usage imprecise and confusing.
GraphQL’s Role
- One viewpoint: omitting GraphQL from a 2025 API design discussion is a significant gap; it’s described as a paradigm shift that gives clients flexible querying, typed schemas, and reduces over/under‑fetching. GraphQL‑specific caches (Apollo/Relay) and CDN‑level tooling are cited as evidence that caching concerns are overstated.
- Counterpoints:
- Implementing a secure, performant GraphQL backend is seen as more complex than conventional REST/OpenAPI, with hazards like recursive queries, denial‑of‑service risk via introspection, and subtle performance issues.
- HTTP status codes often no longer map cleanly to success/error semantics, making logging/monitoring trickier.
- Named REST endpoints are considered easier to talk about and optimize, and GraphQL is characterized as powerful but easy to “hold wrong.”
Other Design Observations
- API shape tends to mirror underlying product resources; awkward internal models or over‑abstracted resources lead directly to confusing APIs and painful debugging.
- Idempotency tokens, deadlines, backpressure behavior, and “static stability” (read‑only operations working even when writes fail) are called out as important but under‑discussed concerns.
- Good documentation access is taken as a proxy for API quality; requiring contracts or sending password‑protected spreadsheets for docs is treated as a red flag.
- Standardized error payloads (e.g., RFC‑style problem details) and clear, descriptive error responses are recommended for better client experience.