Section 4: The Minimal Surface - API Design With Interviewer Pushback
Executive Summary: This section designs the service's entire public contract — two endpoints — and defends every decision inside it under live interviewer pressure. We deliberate the HTTP verb choice and its idempotency consequences, establish the single most important security property of the API (identity lives only in the verified token), design the validation gate and the vocabulary of failure, and survive four rounds of pushback covering concurrent updates, tenant isolation, dependency outages, and enterprise batch demands. The result is a surface so small it can be held in one hand, yet rigorous enough to anchor everything Sections 5 through 9 build beneath it.
You step to the whiteboard, and before the marker touches it, the interviewer sets the trap with a smile: "Walk me through your API." For a large system this question rewards breadth. For the color picker it rewards discipline — because the correct answer is almost embarrassingly short, and everything interesting lives in the reasoning underneath it:
GET /api/v1/color — retrieve my saved color
PUT /api/v1/color — save a new one
Study what is absent from those paths: a user identifier. That absence is the design's crown jewel, and interviewers probe it every single time. The resource is "my color," and "my" is established by cryptographic verification of the request's token — never by anything a client writes into a URL or body.
Architectural Imperative: Identity exists in exactly one place — inside a signed token that the service verifies. There is no request a client can construct that names another user. The cross-tenant attack surface isn't defended; it is structurally absent. (The verification mechanics — signature, issuer, audience, expiry — receive their full deep dive in Section 7.)
Choosing the Verb: A Small Decision Done Deliberately
Even a two-endpoint API contains a genuine design deliberation: what verb carries the write?
- POST: The generic "do something" verb. Semantically loose for our operation — POST implies creating a new subordinate resource, and repeated POSTs are permitted to create duplicates. Retrying a failed POST is therefore never automatically safe.
- PATCH: Partial modification. Correct for multi-field resources, but our resource is one value — there is no "partial" to patch, and PATCH's idempotency is contractually murky.
- PUT: Replace the resource with this representation. This is precisely our overwrite semantics from Section 1 — and PUT is idempotent by contract: delivering the same request twice leaves the same state.
| Requirement | PUT | POST | PATCH |
|---|---|---|---|
| Matches overwrite semantics | ✅ Exact fit | ⚠️ Loose fit | ⚠️ Nothing partial |
| Safe automatic retry | ✅ Idempotent | ❌ May duplicate | ⚠️ Undefined |
| Client simplicity | ✅ One shape | ✅ One shape | ⚠️ Merge rules |
Our Strategic Choice: PUT, and the idempotency is quietly load-bearing. Networks fail after a server commits but before the client hears the acknowledgment; the client retries; with an idempotent verb the retry is harmless. Stripe considered this property so essential that they engineered it into a non-idempotent world — clients send an Idempotency-Key header so a retried "charge the card" can never charge twice. We inherit the same guarantee by choosing the right verb, which is the cheapest reliability money can't even buy.
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson