text
| 1 | # API Design Review |
| 2 | |
| 3 | Review in this order. It is roughly the order of how expensive each mistake is to fix |
| 4 | once a client depends on it. |
| 5 | |
| 6 | ## 1. The model |
| 7 | |
| 8 | Get this wrong and nothing downstream saves you. |
| 9 | |
| 10 | - **Do the resources match how users think, or how the database is arranged?** These are |
| 11 | frequently not the same. The API is a contract with a client, not a view of your |
| 12 | schema. |
| 13 | - **Is the grain right?** One call per screen is chatty. One call that returns |
| 14 | everything is a coupling nobody can evolve. |
| 15 | - **Are relationships expressed consistently?** Nested paths, or ids and a second call. |
| 16 | Pick one and hold it. |
| 17 | |
| 18 | ## 2. Naming |
| 19 | |
| 20 | Permanent, and free to get right now. |
| 21 | |
| 22 | - Consistent case, one convention, everywhere. Do not mix `userId` and `user_id`. |
| 23 | - Plural collections, singular members. `/users`, `/users/{id}`. |
| 24 | - No verbs in resource paths. The method is the verb. |
| 25 | - Names that will still be true later. `email` outlives `emailAddress2`. |
| 26 | - Booleans named for the positive: `enabled`, not `disabled`, and never `notDisabled`. |
| 27 | |
| 28 | ## 3. Errors |
| 29 | |
| 30 | The most-skipped part of API design and the one clients complain about most. |
| 31 | |
| 32 | - **Every error has a stable machine-readable code**, distinct from the HTTP status. |
| 33 | Clients must not branch on the human-readable message. |
| 34 | - **The message says what to do about it**, not just what went wrong. |
| 35 | - **Validation errors name the field.** One error per bad field, not a sentence listing |
| 36 | them. |
| 37 | - **Status codes are used correctly:** 400 malformed, 401 unauthenticated, 403 |
| 38 | authenticated but not permitted, 404 absent, 409 conflicts with current state, 422 |
| 39 | well-formed but semantically invalid, 429 rate limited with a `Retry-After`. |
| 40 | - **No 200 with an error body.** That breaks every generic client. |
| 41 | - **Errors never leak internals.** No stack traces, no SQL, no internal hostnames. |
| 42 | |
| 43 | ## 4. Collections |
| 44 | |
| 45 | - **Pagination exists from day one**, even when the list is short today. Adding it later |
| 46 | is a breaking change. |
| 47 | - **Cursor-based, not offset-based**, for anything that can change while a client pages. |
| 48 | - **A documented maximum page size**, enforced. |
| 49 | - **Filtering and sorting are explicit and finite.** An open query language is an API you |
| 50 | can never change. |
| 51 | - **The response envelope is consistent**: items plus pagination metadata, the same |
| 52 | shape on every collection. |
| 53 | |
| 54 | ## 5. Evolution |
| 55 | |
| 56 | Decide now how this changes later. |
| 57 | |
| 58 | - **How is a breaking change delivered?** Version in the path, in a header, or never. |
| 59 | Whatever the answer, write it down before the first client integrates. |
| 60 | - **Which fields are required?** Every required field is permanent. Optional with a |
| 61 | sensible default is nearly always the better call. |
| 62 | - **Are enums closed?** A client that switches exhaustively on your enum breaks when you |
| 63 | add a value. Say in the docs whether values may be added. |
| 64 | - **Additive changes must be safe.** Clients ignore unknown fields, and you never |
| 65 | repurpose an existing one. |
| 66 | |
| 67 | ## 6. Everything else that becomes permanent |
| 68 | |
| 69 | - Timestamps: ISO 8601, UTC, with an offset. Never a bare local time, never epoch |
| 70 | seconds where milliseconds might later be needed. |
| 71 | - Money: integer minor units plus a currency code. Never a float. |
| 72 | - Identifiers: opaque strings. A client should never parse one, and a numeric id |
| 73 | encourages exactly that. |
| 74 | - Nullability: distinguish "absent" from "explicitly null" and document which you mean. |
| 75 | - Idempotency: any unsafe method a client may retry needs an idempotency key. |
| 76 | - Rate limits: documented, with headers a client can read before hitting them. |
| 77 | |
| 78 | ## Reporting |
| 79 | |
| 80 | Group findings by cost to fix later, not by severity today. Lead with anything that |
| 81 | becomes permanent the moment a client integrates, because that is the only window to |
| 82 | change it for free. |
| 83 |