Skip to content

Idempotency

A property of an operation where performing it many times has the same effect as performing it once. It is what makes retrying a write operation safe.

Reading a record is naturally idempotent. Asking for invoice 4471 a hundred times leaves the world exactly as it was. Writing is not: charging a card a hundred times charges the card a hundred times.

This matters because network failures are ambiguous. When a request times out, you cannot tell whether the server never received it, received it and crashed, or completed it and lost the response on the way back. If the operation is idempotent, you do not need to know. You retry and move on.

Idempotency keys are how most APIs offer this. You generate a unique identifier for a logical operation, send it as a header on the request, and send the identical value on every retry of that same operation. The server stores the key alongside the result. When it sees the key again, it returns the stored result instead of doing the work a second time.

The detail that breaks implementations: the key must be generated once, at the point the operation is created, and persisted with the record. A key generated inside the retry loop is a fresh random string on every attempt, which gives the server no way to recognise a duplicate. This is the most common way the pattern is applied incorrectly.

A concrete example. An order pipeline writes an order to its own database with a UUID, then posts to a payment provider with Idempotency-Key: <that UUID>. The first attempt times out after the charge succeeded. The retry carries the same key, the provider recognises it, and returns the original charge result. One charge, no reconciliation, no support ticket.

When a provider offers no idempotency support, do not retry writes blindly. Record the intent locally before the call, and on failure query the provider for the current state rather than repeating the operation. See also silent failure for what happens when you skip both.