Technical
Idempotency Keys: The Quiet Production Lifesaver
Networks fail. Retries happen. Without idempotency, a timed-out payment can charge the card twice, a retried signup can create two subscribers, a resent webhook can deliver two emails. Idempotency keys are the boring pattern that prevents all of this.
The Shape
Client generates a UUID per request. Server checks if it has seen that UUID. If yes, return the previous result. If no, do the work and store the result under the UUID.
@router.post('/subscribe')
async def subscribe(req: SubscribeRequest, idempotency_key: str = Header(...)):
cached = await cache.get(idempotency_key)
if cached:
return cached
result = await do_subscribe(req)
await cache.set(idempotency_key, result, ttl=86400)
return resultTwenty-four-hour cache window. Retries within that window return the same result. No duplicates, ever. The client does not need to know whether this was the first call or the fifth.
Where It Matters Most
Payments, outbound emails, external API calls, database writes. Anywhere a duplicate would cause a user-visible problem. I default to enabling idempotency on every POST endpoint that has side effects.
The DynamoDB Trick
DynamoDB's conditional writes give you idempotency almost for free. Write with ConditionExpression: attribute_not_exists(id) and a specific PK. The second write with the same key returns ConditionalCheckFailedException without doing anything. Read the previous item and return it.
The Client Side
Clients generate keys as UUIDs and attach as Idempotency-Key header. Mobile apps keep the same key across retries of the same logical action. A new user intent (a new button press) means a new key.
See Stripe's idempotency guide for the canonical pattern. It generalizes beyond payments. Idempotency is cheap to add and expensive to skip.
RELATED READING
The Consulting Shift I Am Making In Year Two
After a year of writing and building, my consulting practice is changing shape. Shorter engagements. Sharper outcomes.
ReadThe Frontend Shift: Shipping Less JavaScript In Year Two
A year ago I reached for Next.js for everything. This year I often reach for nothing.
ReadThe Serverless Lesson I Would Write On A Sticky Note
After a year of shipping serverless projects, one rule explains most of the wins and all of the losses.
Read