100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
API Design & Best Practices
30 minintermediate

Idempotency and Safe Retries

A client sends `POST /charges` to bill a customer's card. The server receives it, charges the card successfully, and starts writing the response — and the connection drops before that response reaches the client. The client saw a timeout. It has no idea whether the charge happened. This is not a rare edge case introduced by bad infrastructure; it is a structural property of any network call — a response can be lost after the work it describes already completed, and the client on the other end of a timeout genuinely cannot tell "nothing happened" apart from "everything happened and I just didn't hear about it."

The naive fix — "if it times out, retry" — is exactly backwards for an operation like this, because retrying a request that already succeeded produces a second charge, a second email, a second shipped order, for an action the client only ever intended to perform once. The real fix isn't avoiding retries; retries are necessary and correct engineering. The real fix is making the operation itself safe to repeat, so that retrying a request that already succeeded is provably a no-op rather than a second occurrence.

This is where idempotency and cursor pagination consistently earn their reputation as the two places APIs most often get it wrong, and for the same underlying reason: both look correct in a demo where nothing ever fails or races, and both fail specifically under the real-world condition — concurrency, network flakiness — that a demo never reproduces. An idempotency design that works is what turns lesson 6's retryable-error classification from a plausible idea into something a client can actually act on automatically without risking a duplicate side effect.

Analogy🏏Cricket
🏏 Think of it like cricket: A boundary is hit, the ground umpire signals four, but a sudden burst of crowd noise or a dropped radio link means the scorers in the box genuinely aren't sure whether the signal registered. The safe protocol isn't to just signal again and hope the scorers apply it exactly once, and it isn't to do nothing and risk the boundary never being recorded — it's that every signal is tied to a specific, already-known delivery: this over, this ball, this specific event, uniquely identified before the signal is even given. If the umpire repeats the signal a second time to be sure the box caught it, the scorers don't add a second boundary to the total, because the signal was never "a boundary just happened, add four runs" in the abstract — it was always "delivery number so-and-so is a boundary," and a scorer checking their own record sees that delivery already logged and simply confirms it, adding nothing. Just as repeating a boundary signal for the same already-identified delivery doesn't double the runs recorded, retrying an API request carrying the same idempotency key for the same already-identified operation shouldn't double the side effect it causes. Just as the delivery's identity — not the raw fact that a boundary occurred — is what the scorers actually key their record on, a specific operation's identity — not the raw fact that a charge request arrived — is what a server should key its record on when deciding whether to execute it again. The insight is that safety under repetition doesn't come from avoiding retries, it comes from giving every retriable action an identity specific enough that repeating it is recognizably the same action, not a new one.
Lesson 9 of 35
0% complete