Idempotency in HTTP
Prompt
A client hits your API to charge a card, the response times out, and the client retries the exact same request. Walk me through how HTTP's notion of idempotency decides whether that retry is safe — and which methods give you that guarantee for free.
How this round runs
This is a conversation that escalates — I'll keep asking "and what happens when…" to find the edge of what you know. A calibrated "I'm not certain, but reasoning from the spec…" beats a confident wrong answer, and I'm listening for whether you can connect the property to a real consequence, not just recite a table.
Model answer
I'll frame it around the property first, then the per-method consequences. Idempotency means N identical requests leave the server in the same state as one — the resulting state is invariant, not necessarily the response body. That's the whole reason it matters: networks lose responses, so a client often can't tell "succeeded" from "never arrived." If the method is idempotent, retrying is safe; if it isn't, a blind retry can double-charge or create a duplicate.
Going a layer down to why each method lands where it does: GET, HEAD, OPTIONS are
idempotent because they're also safe — they mutate nothing, so repetition is trivially
invariant. PUT and DELETE are idempotent but not safe: a PUT /users/123 with a
full representation overwrites to the same target value every time, and a second
DELETE /users/123 leaves the resource just as gone as the first (the status may differ —
204 then 404 — but the state is identical, which is what the property is actually about).
POST is the odd one out: it's defined as non-idempotent because its conventional meaning
is "create a subordinate resource," and two POST /orders calls create two orders. That's
exactly the case where a timeout-and-retry is dangerous. The honest boundary for me: the
spec defines this at the semantic level — the server implementation can break the
guarantee (a non-idempotent PUT) or add one where the verb doesn't (an idempotent POST),
so the method only tells you the intended contract, not what a given endpoint actually does.
- Framed idempotency as state-invariance, distinct from a fixed response body, before listing methods
- Separated safe from idempotent (DELETE is idempotent but not safe) instead of conflating them
- Tied the property to the concrete consequence: a lost response makes retries safe only when idempotent
- Reasoned about WHY each verb's semantics put it where it is, not just memorized the table
- Flagged that the method states intent, not what the endpoint's implementation actually guarantees
- Is POST ever idempotent? → idempotency keys: client sends a unique key, server dedupes on it and replays the stored response on retry
- And what happens when two retries race with the same idempotency key concurrently? → the key needs a uniqueness constraint / atomic insert, not just a read-then-write check
- DELETE returns 404 on the second call — does that violate idempotency? → no, the property is about state, not the status code