8 lessons

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.

RequirementConsumersResource ModelStyleContractValidationAuthorizationErrorsIdempotencyPaginationVersioningObservabilityEvolutionTrade-offs

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.

Idempotency: Surviving the Retry
▶ lab

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.

Q · When the same request arrives twice — and it will — does the system produce one effect or two?
Idempotency Keys: The Mechanism
▶ lab

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.

Q · How does the server recognize a retry of a request it already processed — and what exactly does it return for one?
Idempotency vs Deduplication

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.

Q · Do I need to answer a repeated request with its original outcome, or silently discard a repeated message?
Optimistic Concurrency: Versions and If-Match
▶ lab

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.

Q · When two clients update the same resource from the same starting point, how does the second one find out — and what is it supposed to do then?
The Lost Update, Step by Step
▶ lab

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.

Q · Between a client's read and its write, someone else wrote — whose change survives, and does anyone find out?
Consistency as a Contract Clause

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.

Q · After a successful write, which reads see it, when — and did we tell consumers, or leave them to find out?
There Is No Transaction Across APIs

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.

Q · This operation touches three systems and the second one just failed — what state is the caller looking at, and what does the contract say about it?
Retries and Timeouts as Contract Guidance

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.

Q · The request failed or timed out — should this client try again, when, how many times, and does the server survive everyone deciding "yes"?