Idempotency & Concurrency
The network loses responses, so clients retry. Idempotency keys, dedup vs idempotency, optimistic concurrency with versions, lost-update prevention, and consistency the contract admits to.
Every lesson below names the consumers, the design question and the guarantee before recommending anything. Recommendations come with what they cost, when not to use them, and how they evolve.
A response can be lost after the server did the work, so every client will eventually retry a request that already succeeded. Idempotency is the contract property that makes that retry safe — and money paths without it double-charge.
A client-generated key turns "did my POST land?" into a question the server can answer: check the store, replay the saved result or process and save. The hard parts are scope, expiry, parameter mismatches, and two identical requests in flight at once.
Idempotency makes a repeated request produce the same outcome and hands that outcome back. Deduplication detects that a message was already seen and drops it. Related, frequently confused — and each one fails when asked to do the other's job.
Let concurrent writers proceed without locks, but make every update state which version it read. A stale version gets a 409 or 412 instead of silently destroying someone else's write — and the contract must say who untangles the conflict.
A reads v1, B reads v1, A writes, B writes — and A's change is gone without an error, a log line, or a conflict. The anatomy of the most silent data-loss bug an API can have, and what a version check turns it into.
A client POSTs a resource, then GETs it — and gets a 404. Nothing is broken unless the contract said otherwise. Read-after-write, monotonic reads and staleness bounds are promises consumers build UI and logic on, so they must be written down.
One request that charges payment, reserves inventory and books shipping cannot be atomic — the ACID boundary died at the first network hop. What replaces it is a contract that models the in-between states: workflows, state machines and compensation.
A timeout is not a failure — it is the absence of an answer. The contract owes clients the missing half of their retry loop: what is retryable, how long to wait, how to back off, and what the server will do to protect itself when everyone retries at once.