API Design
The contract between your service and its consumers — design it for stability, clarity, and evolution.
Why
An API is a published interface that's far harder to change than an internal function — every breaking change breaks every consumer at once. Good API design (consistent naming, versioning, pagination, error model) makes the API learnable in minutes and evolvable over years without breaking clients. Bad API design forces every consumer to write brittle glue code and makes every improvement a coordinated migration. The API is also the documentation that developers actually read; a clear one reduces integration support tickets by an order of magnitude.
When
Before any consumer integrates. Takes 1-2 days for the core contract. Revisit per major version.
How
1. Choose the style: REST (resource-oriented), gRPC (perf + typed), GraphQL (flexible reads)
- Default to REST unless you have a specific reason otherwise
2. Name resources as nouns (/users, /orders/{id}), actions as sub-resources (/orders/{id}/cancel)
3. Use standard verbs (GET/POST/PUT/PATCH/DELETE) consistently; don't overload POST
4. Pagination: cursor-based for large lists (offset breaks under inserts)
5. Errors: consistent {code, message, details} envelope + standard HTTP status
6. Versioning: /v1/ prefix; never break within a version, add new endpoints for changes
7. Document with OpenAPI; generate clients from the spec, not by hand
8. Add idempotency keys for any non-safe POST (payments, sends)Example
POST /v1/orders
body: { user_id, items[], idempotency_key }
201 → { id, status, total, created_at }
409 → { code: "DUPLICATE", message: "idempotency_key already used", details: { existing_id } }
GET /v1/orders?cursor=abc&limit=20
200 → { items[], next_cursor }
Error envelope (consistent across all endpoints):
{ "code": "VALIDATION_ERROR", "message": "items is required", "details": { "field": "items" } }Common Problems
- ⚠Using offset/limit pagination — it skips or duplicates rows when items are inserted between page fetches; use cursors.
Best Practices
- ✓Never remove a field within a version — deprecate it (keep returning it) and remove in the next major version.
- ✓Treat the OpenAPI spec as the source of truth; if the code and spec disagree, the spec wins and the code is a bug.