Crux Read real HTTP requests, URL designs, and response shapes, predict the failure, and pick the highest-leverage fix a senior would make first.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min
API design defects show up in the wire format — the request line, the URL shape, the response body. Read each snippet, find the trap, and choose the fix a senior engineer would make first.
Goal
Practise the loop you run in every API review: read the request and the resource shape, predict what breaks in production, and reach for the structural fix before patching symptoms.
This is how the API cancels an order. What is the defect, and the fix?
Heads-up PUT vs PATCH is not the problem — replacing the whole order has the same flaw. The defect is that the state transition is unguarded: the client writes status directly instead of the server enforcing the rules.
Heads-up The media type is a detail; the structural defect is that any client can set status to any value. A guarded action resource, not a different patch format, is the fix.
Heads-up It looks RESTful, but a writable status field hands the client the state machine. A real cancellation is a guarded transition the server must own, not a field the client flips.
Snippet 2 — the resource URL design
GET /getUserOrders?userId=7POST /createOrderForUser?userId=7GET /customers/7/projects/3/orders/42/items/9/discounts
Quiz
Completed
Two distinct modeling smells appear in these three lines. Name them and the redesign.
Heads-up Both are defects. getUserOrders and createOrderForUser put the action in the URL, which is RPC and forfeits the uniform interface — not an acceptable alias alongside REST resources.
Heads-up Query params for filtering and relationships are correct REST. The actual smells are the verbs in lines 1–2 and the six-level nesting in line 3, not the use of query strings.
Heads-up Casing is cosmetic. Two of these encode verbs in the path and one nests six levels deep — structural problems that consistent casing does not address.
The client sends this request, the TCP connection drops before the response arrives, and the client retries the identical request with the same Idempotency-Key. What must the server do?
Heads-up That is exactly the double-charge this header exists to prevent. The Idempotency-Key tells the server to detect the replay and return the first result, not create a second payment.
Heads-up A replay with the same key and body is not a conflict — the contract is to return the original successful response, making the retry transparent to the client. 409 would force the client to handle a non-error as an error.
Heads-up PUT and DELETE are idempotent by their semantics; the idempotency key exists precisely because POST is not, letting a creating request be retried safely.
This is a user resource serialized straight from the ORM row. What is wrong, and the structural fix?
Heads-up Removing the credential is necessary but not sufficient. is_deleted and internal_risk_score are internal, and serializing rows directly still welds the contract to column names — the structural fix is an explicit mapping layer.
Heads-up Number vs string id is a minor convention choice. The real damage is leaking a credential and internal fields and coupling the wire contract to the DB schema.
Heads-up A password hash should never be in any representation regardless of role, and the schema coupling remains. The fix is to map to a deliberate wire shape, not gate raw columns by role.
Recap
Every API defect reads off the wire: a writable status field is an unguarded transition (use a guarded action resource); verbs in the path and six-level nesting are RPC and chattiness (noun resources, shallow paths, IDs at the root); a retried POST needs an idempotency key to dedupe safely; and a row serialized straight to JSON leaks credentials and internal fields while welding the contract to your schema (map through a DTO). Read the request, find the structural smell, fix the shape — not the symptom.