Idempotency

The property that lets you retry safely. Without it, distributed systems are a minefield.

Why it matters

The previous concept told you to retry failed requests. There is a catch. What if the request actually succeeded, but you did not get the response because the network blipped?

You retry. The server runs the same request again. The user gets charged twice. The email is sent twice. The order is placed twice.

The whole strategy of "retry on failure" only works if the operation is safe to repeat. That property is called idempotency.

A request is idempotent if doing it once is the same as doing it twice. Or ten times. The end state is the same. The user cannot tell which retry "won."

Naturally idempotent. GET, PUT, DELETE

Some HTTP methods are idempotent by design.

GET /users/42 is just reading. Running it twice returns the same data. Easy.

PUT /users/42 { name: "Alice" } replaces the user with the given data. Doing it twice has the same result as doing it once. The name is "Alice" either way.

DELETE /users/42 deletes the user. The user is gone either way. First call deletes them. Second call says they are already gone, often as a 404. That is still idempotent.

The dangerous one is POST, which is usually used to create something new. Running it twice creates two things. Order placed twice. Comment posted twice. Bad.

Making POST idempotent. Idempotency keys

The standard fix is the idempotency key.

When the client makes a POST, it generates a random UUID and includes it as a header. Something like Idempotency-Key: abc-123-def.

When the server gets the request, it checks. "Have I seen this key before?" If no, process the request and store the result, indexed by the key. If yes, return the cached result. Do not process again.

Now if the client retries with the same key, the server recognizes it and returns the original result. No duplicate work.

This is how Stripe handles every write. Every charge request includes an idempotency key. You can safely retry the same charge as many times as you want. It only charges the card once. Real production APIs offer this. Use it.

Idempotency at every layer

It is not just for HTTP APIs. Idempotency matters anywhere retries can happen.

Queue consumers. At-least-once delivery means the same message can arrive twice. Process it idempotently. Use a "have I processed this message ID before" check.

Webhook handlers. Stripe sends each event with a unique ID. Store the IDs you have processed and ignore duplicates.

Database writes. INSERT ... ON CONFLICT DO NOTHING turns an INSERT into an idempotent operation.

Cron jobs and scheduled tasks. If the job runs twice (and it will, eventually), the result should be the same as running it once.

Idempotency is one of the most important properties in distributed systems. Build it in from day one. The cost of retrofitting "wait, we double-charged 10,000 users" is much higher.

Now build it yourself →