Why Retries Need Idempotency: The Real Fix for Duplicate Payments and Orders
April 4, 2026
Retries are the most common fix engineers reach for when a request fails. They are also the most common way to corrupt your data.
The pattern that bites every team eventually looks like this. A client sends a payment request. The server processes it, writes a row to the charges table, calls Stripe, and starts forming the response. Somewhere on the way back, the TCP connection drops, or the load balancer times out, or the client gives up at 30 seconds while the server happily took 31. The client retries. The server, which has no idea this is the same logical request, charges the card a second time.
The retry was not the bug. The missing idempotency was.
The fix is structurally simple. The client generates a unique idempotency key per logical operation, usually a UUID, and sends it as a header like Idempotency-Key. The server checks a small table keyed by that value. If the key has already been processed, the server skips the work and returns the previously stored response. If not, it processes the request and writes the response under that key before returning.
That turns retries from dangerous into boring. Same key means same logical operation. The side effect happens once even if the request arrives ten times.
A few things matter in practice that the textbook version skips.
First, the idempotency record must be written in the same transaction as the side effect, or you reintroduce the duplicate. If you charge the card and then crash before persisting the key, the retry will charge again. Stripe handles this by writing the key first, then the side effect, then the result, with state transitions guarded by row locks.
Second, you need a TTL. Idempotency tables grow forever if you do not expire them. 24 hours is a reasonable default for payments, longer for things like order creation where retries can come from human action.
Third, the key has to be chosen by the client, not the server. A server-generated key cannot deduplicate a retry, because the retry generates a new one.
This pattern matters most for any operation with externally visible side effects: payments, order placement, booking flows, transactional emails, event publishing to Kafka. Anywhere a duplicate would be embarrassing or expensive.
Retries make your system reliable against transient failures. Idempotency is what makes that reliability safe to use.
Retries improve reliability, but only idempotency keys make them safe. Same key means same logical operation, so the side effect happens once even if the request arrives twice.
Originally posted on LinkedIn. View original.