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.