Idempotency


The Problem#

Your payment system charges a customer twice. Your message processor creates duplicate orders. Your deployment script corrupts production. These failures share a common cause: operations that aren’t idempotent.

Idempotency means an operation produces the same result whether executed once or multiple times. In distributed systems, this isn’t a nice-to-have—it’s the difference between reliable software and chaos.

Why Networks Make Everything Harder#

Network calls fail in exactly three ways: before the request, during processing, or after the response. Only one of these is safe to retry:

// Client calls payment API
POST /payments { amount: 100, cardToken: "abc123" }

// Three failure scenarios:
// 1. Network fails before server receives request → Safe to retry
// 2. Server processes payment but response is lost → DANGEROUS to retry
// 3. Server fails during processing → Unknown state

Without idempotency, scenario 2 creates duplicate charges. With idempotency, the second request returns the same result as the first without side effects.

The Idempotency Contract#

True idempotency requires three guarantees:

1. Same Result Multiple identical requests return identical responses

2. No Side Effects Additional executions don’t change system state

3. Deterministic Behavior Outcome depends only on request content, not timing or sequence

These aren’t independent requirements—violating any one breaks reliability.

Implementation Strategies#

Natural Idempotency#

Some operations are inherently idempotent:

PUT /users/123 { name: "John", email: "john@example.com" }
DELETE /users/123
GET /users/123

// Setting absolute values is naturally idempotent
// Getting or deleting specific resources is idempotent

Idempotency Keys#

For operations that aren’t naturally idempotent, use unique keys:

POST /payments
Headers: { "Idempotency-Key": "payment-user123-20250914-001" }
Body: { amount: 100, userId: 123 }

// Server logic:
if (paymentExists(idempotencyKey)) {
  return existingPayment; // Same response, no side effects
}
return processNewPayment(request, idempotencyKey);

Database-Level Guarantees#

Use constraints to enforce idempotency:

CREATE TABLE payments (
  id UUID PRIMARY KEY,
  idempotency_key VARCHAR(255) UNIQUE NOT NULL,
  amount DECIMAL(10,2),
  user_id INTEGER
);

-- Unique constraint prevents duplicate processing
-- Even if application logic fails

When Idempotency Breaks Down#

Time-Dependent Operations

POST /posts { content: "Posted at " + new Date() }

Different timestamps make responses non-identical.

External State Dependencies

POST /orders { productId: 123, quantity: 5 }
// Fails if inventory depleted between requests

External state changes break deterministic behavior.

Cumulative Operations

POST /accounts/transfer { from: A, to: B, amount: 100 }
// Each retry transfers more money

Additive effects violate the no-side-effects guarantee.

The Business Case#

Idempotency isn’t just about preventing duplicate charges. It enables:

  • Aggressive Retry Logic: Clients can retry safely, improving perceived reliability
  • Simplified Error Handling: “When in doubt, retry” becomes a viable strategy
  • Operational Confidence: Deployments and migrations can be rerun safely
  • Debugging Simplicity: Reproduce issues by replaying the exact same requests

Key Insight: Idempotency transforms unreliable networks into reliable systems. Without it, every retry is a gamble. With it, retries become a tool.

Implementation Guidelines#

Design for Idempotency First

  • Use PUT instead of POST when setting absolute values
  • Store operation results with unique keys
  • Separate “check if done” from “do the work”

Handle Edge Cases

  • Return stored results for completed operations
  • Return appropriate errors for invalid retry attempts
  • Set reasonable timeouts for idempotency key expiration

Test Religiously

  • Verify identical responses for multiple identical requests
  • Confirm no side effects from retry attempts
  • Test concurrent requests with the same idempotency key

Conclusion#

Idempotency is the foundation of reliable distributed systems. It transforms the fundamental unreliability of networks into predictable behavior.

Design for idempotency from the start. Your operations team will thank you when deployments can be safely rerun. Your customers will thank you when payments never duplicate. And you’ll thank yourself when debugging becomes reproducible.

“The same action, repeated, yields the same result” — The promise of idempotent operations