<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged idempotency</title>
<link>https://coderlegion.com/tag/idempotency</link>
<description>Powered by Question2Answer</description>
<item>
<title>Why Pressing the Button Twice Shouldn't Double Your Bill: Understanding Idempotency</title>
<link>https://coderlegion.com/27045/why-pressing-the-button-twice-shouldnt-double-your-bill-understanding-idempotency</link>
<description>&lt;p&gt;Have you ever clicked a &quot;Submit Payment&quot; button on an online shop, watched the page freeze, and felt a sudden wave of panic? You wonder: &lt;em&gt;Did it go through? Should I click it again, or will I be charged twice?&lt;/em&gt; The technology that protects you from this exact nightmare is called &lt;strong&gt;idempotency&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In computer science, idempotency is a property of an action or design where repeating the action multiple times produces the exact same result as doing it just once. In other words, if an operation is idempotent, running it a hundred times has no more effect on the system's final state than running it a single time. This concept acts as a vital safety guarantee across modern software architectures, preventing accidental duplicates and keeping data consistent.&lt;/p&gt;
&lt;h4 id=&quot;the-elevator-analogy&quot;&gt;The Elevator Analogy&lt;/h4&gt;
&lt;p&gt;To understand how this works in the physical world, consider an elevator call button. When you walk up to an elevator bank and press the &quot;Up&quot; button, the button illuminates, sending a signal to the elevator's controller to dispatch a car to your floor. If you get impatient and press that same button ten more times, the system does not dispatch ten separate elevators, nor does it charge the building's electrical system ten times over. The final state is exactly the same: one elevator is scheduled to stop at your floor. The elevator button is an everyday example of an idempotent interface.&lt;/p&gt;
&lt;h4 id=&quot;why-it-matters-in-software-engineering&quot;&gt;Why it Matters in Software Engineering&lt;/h4&gt;
&lt;p&gt;In the technology sector, engineers rely on idempotency to build resilient systems that can survive unstable network connections. When you buy something online, your web browser communicates with a payment gateway (a secure service that processes credit cards). If your internet connection briefly drops after you press &quot;Buy,&quot; your browser might fail to receive the confirmation signal from the payment gateway. Because the browser doesn't know if the transaction succeeded, it is programmed to automatically retry the request.&lt;/p&gt;
&lt;p&gt;Without idempotency, the payment gateway would treat each retry as a brand-new transaction, charging your credit card multiple times for a single order. To prevent this, software developers design API (Application Programming Interface, which is how systems talk to one another) endpoints to be idempotent. They do this by requiring an &quot;idempotency key&quot;—a unique, one-time identifier generated by the client application for that specific transaction. When the server receives a request, it checks its database to see if it has already processed a request with that specific key. If it has, it simply returns the cached response from the first successful attempt instead of running the billing logic again.&lt;/p&gt;
&lt;h4 id=&quot;idempotency-in-code&quot;&gt;Idempotency in Code&lt;/h4&gt;
&lt;p&gt;Here is a simple example in JavaScript demonstrating how this mechanism operates in code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// A simulated database of processed transaction IDs
const processedTransactions = new Set();

function processPayment(idempotencyKey, amount) {
  // Check if we have already handled this exact request
  if (processedTransactions.has(idempotencyKey)) {
    return {
      status: &quot;success&quot;,
      message: &quot;Duplicate request ignored. Returning previous transaction details.&quot;,
      amount: amount
    };
  }

  // If it is a new request, process the payment and store the key
  processedTransactions.add(idempotencyKey);
  
  // Imagine complex credit card processing logic happens here
  return {
    status: &quot;success&quot;,
    message: `Successfully charged $${amount} for new transaction.`,
    amount: amount
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-final-takeaway&quot;&gt;The Final Takeaway&lt;/h4&gt;
&lt;p&gt;Ultimately, idempotency is the silent architecture of reliability in a chaotic digital landscape. It shifts the burden of managing unpredictable internet dropouts away from anxious users and places it squarely on smart system design, ensuring that patience-testing screen freezes never translate into administrative or financial disasters.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;resources&quot;&gt;Resources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Saurav-TB-Pandey/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-hook-lab:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;npm package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect with me on LinkedIn:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/pandeysaurav/&quot;&gt;Saurav Pandey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/09/the-thermostat-rule-guide-to.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27045/why-pressing-the-button-twice-shouldnt-double-your-bill-understanding-idempotency</guid>
<pubDate>Sun, 13 Sep 2026 08:32:47 +0000</pubDate>
</item>
<item>
<title>Idempotency in APIs - Why Your Retry Logic Can Break Everything (And How to Fix It)</title>
<link>https://coderlegion.com/26972/idempotency-in-apis-why-your-retry-logic-can-break-everything-and-how-to-fix-it</link>
<description>&lt;p&gt;Modern applications rely heavily on APIs. But networks are unreliable - requests fail, time out, or get retried.&lt;/p&gt;
&lt;p&gt;Now imagine this scenario:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User clicks &lt;strong&gt;“Pay Now”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Request sent to server&lt;/li&gt;
&lt;li&gt;Payment processed successfully&lt;/li&gt;
&lt;li&gt;But response is lost due to network issue&lt;/li&gt;
&lt;li&gt;Client retries the request&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;💥 Payment gets processed again. User is charged twice.&lt;/p&gt;
&lt;p&gt;This is one of the most dangerous and common problems in distributed systems.&lt;/p&gt;
&lt;p&gt;The solution? &lt;strong&gt;Idempotency.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-idempotency-&quot;&gt;What is Idempotency?&lt;/h2&gt;
&lt;p&gt;An operation is &lt;strong&gt;idempotent&lt;/strong&gt; if performing it multiple times produces the same result as performing it once.&lt;/p&gt;
&lt;p&gt;In simple terms:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Same request → same effect → no duplicate side effects&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Create Order (without idempotency):
Request sent twice → 2 orders created ❌

Create Order (with idempotency):
Request sent twice → only 1 order created ✅
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Idempotency ensures system correctness even when retries happen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-do-retries-happen-&quot;&gt;Why Do Retries Happen?&lt;/h2&gt;
&lt;p&gt;Retries are extremely common due to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network timeouts&lt;/li&gt;
&lt;li&gt;Server crashes&lt;/li&gt;
&lt;li&gt;Client retries&lt;/li&gt;
&lt;li&gt;Load balancers retrying requests&lt;/li&gt;
&lt;li&gt;Mobile networks instability&lt;/li&gt;
&lt;li&gt;API gateway retries&lt;/li&gt;
&lt;li&gt;Reverse proxy retries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Retries are normal. Duplicate side effects are not.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;which-http-methods-are-idempotent-&quot;&gt;Which HTTP Methods Are Idempotent?&lt;/h2&gt;
&lt;p&gt;According to HTTP specification:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Method &lt;/th&gt;&lt;th&gt;Idempotent &lt;/th&gt;&lt;th&gt;Explanation&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;GET    &lt;/td&gt;&lt;td&gt;✅ Yes      &lt;/td&gt;&lt;td&gt;Fetch data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PUT    &lt;/td&gt;&lt;td&gt;✅ Yes      &lt;/td&gt;&lt;td&gt;Replace resource&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DELETE &lt;/td&gt;&lt;td&gt;✅ Yes      &lt;/td&gt;&lt;td&gt;Delete resource&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;POST   &lt;/td&gt;&lt;td&gt;❌ No       &lt;/td&gt;&lt;td&gt;Create resource&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PATCH  &lt;/td&gt;&lt;td&gt;❌ No       &lt;/td&gt;&lt;td&gt;Partial update&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;POST is NOT idempotent by default.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /orders
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling twice → creates 2 orders.&lt;/p&gt;
&lt;p&gt;This must be handled explicitly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;real-world-example-payment-api-problem&quot;&gt;Real-World Example: Payment API Problem&lt;/h2&gt;
&lt;p&gt;Client sends:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /payments
{
  &quot;user_id&quot;: &quot;123&quot;,
  &quot;amount&quot;: 100
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If client retries due to timeout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /payments
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without idempotency → duplicate payment ❌&lt;/p&gt;
&lt;p&gt;With idempotency → single payment ✅&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;solution-idempotency-key&quot;&gt;Solution: Idempotency Key&lt;/h2&gt;
&lt;p&gt;Client sends a unique key with request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /payments
Idempotency-Key: abc123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Server behavior:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If key is new → process request&lt;/li&gt;
&lt;li&gt;If key already exists → return previous result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guarantees single execution.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-idempotency-works-internally&quot;&gt;How Idempotency Works Internally&lt;/h2&gt;
&lt;p&gt;Flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Client generates unique key&lt;/li&gt;
&lt;li&gt;Sends request with key&lt;/li&gt;
&lt;li&gt;Server checks database/cache for key&lt;/li&gt;
&lt;li&gt;If key exists → return stored response&lt;/li&gt;
&lt;li&gt;If not exists → process request&lt;/li&gt;
&lt;li&gt;Store key and response&lt;/li&gt;
&lt;li&gt;Return response&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;go-implementation-example&quot;&gt;Go Implementation Example&lt;/h2&gt;
&lt;p&gt;Let’s implement idempotent payment API using Go.&lt;/p&gt;
&lt;h3 id=&quot;database-table&quot;&gt;Database Table&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;idempotency_keys table:

key (primary key)
response
created_at
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;go-example-basic-version-&quot;&gt;Go Example (Basic Version)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;type IdempotencyRecord struct {
    Key      string
    Response string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;handler-implementation&quot;&gt;Handler Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;func PaymentHandler(w http.ResponseWriter, r *http.Request) {

    key := r.Header.Get(&quot;Idempotency-Key&quot;)

    if key == &quot;&quot; {
        http.Error(w, &quot;Idempotency-Key required&quot;, http.StatusBadRequest)
        return
    }

    // Check if key exists
    record, exists := getIdempotencyRecord(key)

    if exists {
        w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
        w.Write([]byte(record.Response))
        return
    }

    // Process payment
    paymentID := processPayment()

    response := fmt.Sprintf(`{&quot;payment_id&quot;: &quot;%s&quot;}`, paymentID)

    // Store key and response
    saveIdempotencyRecord(key, response)

    w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
    w.Write([]byte(response))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;example-flow&quot;&gt;Example Flow&lt;/h2&gt;
&lt;p&gt;First request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /payments
Idempotency-Key: abc123

Response:
{
  &quot;payment_id&quot;: &quot;pay_001&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Retry request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /payments
Idempotency-Key: abc123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Response returned from storage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;payment_id&quot;: &quot;pay_001&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No duplicate payment created.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;production-implementation-using-redis-recommended-&quot;&gt;Production Implementation Using Redis (Recommended)&lt;/h2&gt;
&lt;p&gt;Redis is ideal because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extremely fast&lt;/li&gt;
&lt;li&gt;Supports expiration (TTL)&lt;/li&gt;
&lt;li&gt;Perfect for temporary idempotency storage&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;redis-example-in-go&quot;&gt;Redis Example in Go&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;func PaymentHandler(w http.ResponseWriter, r *http.Request) {

    key := r.Header.Get(&quot;Idempotency-Key&quot;)

    val, err := redisClient.Get(ctx, key).Result()

    if err == nil {
        w.Write([]byte(val))
        return
    }

    paymentID := processPayment()

    response := fmt.Sprintf(`{&quot;payment_id&quot;: &quot;%s&quot;}`, paymentID)

    redisClient.Set(ctx, key, response, time.Hour*24)

    w.Write([]byte(response))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;important-handle-race-conditions&quot;&gt;Important: Handle Race Conditions&lt;/h2&gt;
&lt;p&gt;Two requests may arrive simultaneously with same key.&lt;/p&gt;
&lt;p&gt;Solution: Use atomic operations.&lt;/p&gt;
&lt;p&gt;Redis example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET key value NX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NX = set only if not exists&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;important-best-practice-store-full-response&quot;&gt;Important Best Practice: Store Full Response&lt;/h2&gt;
&lt;p&gt;Do not just store key.&lt;/p&gt;
&lt;p&gt;Store:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;response body&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because retry must return exact same response.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;example-database-schema-postgresql-&quot;&gt;Example Database Schema (PostgreSQL)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE idempotency_keys (
    key TEXT PRIMARY KEY,
    response JSONB,
    status_code INT,
    created_at TIMESTAMP DEFAULT NOW()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Primary key ensures uniqueness.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;real-world-systems-that-use-idempotency&quot;&gt;Real-World Systems That Use Idempotency&lt;/h2&gt;
&lt;p&gt;Critical systems use idempotency extensively:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payment APIs&lt;/li&gt;
&lt;li&gt;Order creation APIs&lt;/li&gt;
&lt;li&gt;Financial transactions&lt;/li&gt;
&lt;li&gt;Booking systems&lt;/li&gt;
&lt;li&gt;Wallet transfers&lt;/li&gt;
&lt;li&gt;Stripe payments&lt;/li&gt;
&lt;li&gt;Banking APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without idempotency, financial systems would break.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;common-mistake-developers-make&quot;&gt;Common Mistake Developers Make&lt;/h2&gt;
&lt;p&gt;❌ Using retry without idempotency&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;retry 3 times on failure
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This multiplies side effects.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Retry logic: 3 times
Result: 3 payments
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dangerous bug.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;idempotency-vs-duplicate-prevention-using-unique-constraints&quot;&gt;Idempotency vs Duplicate Prevention Using Unique Constraints&lt;/h2&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UNIQUE(user_id, order_id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents duplicates at DB level.&lt;/p&gt;
&lt;p&gt;But idempotency is better because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Works at API level&lt;/li&gt;
&lt;li&gt;Handles retries cleanly&lt;/li&gt;
&lt;li&gt;Returns same response&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Best approach: use BOTH.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-should-you-use-idempotency-&quot;&gt;When Should You Use Idempotency?&lt;/h2&gt;
&lt;p&gt;Use idempotency for operations that create side effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payment creation&lt;/li&gt;
&lt;li&gt;Order creation&lt;/li&gt;
&lt;li&gt;Wallet transfer&lt;/li&gt;
&lt;li&gt;Booking&lt;/li&gt;
&lt;li&gt;Resource creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not required for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET requests&lt;/li&gt;
&lt;li&gt;Read-only operations&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-client-should-generate-idempotency-key&quot;&gt;How Client Should Generate Idempotency Key&lt;/h2&gt;
&lt;p&gt;Use UUID:&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;550e8400-e29b-41d4-a716-446655440000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;uuid.New().String()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unique per operation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;idempotency-key-expiration-strategy&quot;&gt;Idempotency Key Expiration Strategy&lt;/h2&gt;
&lt;p&gt;Keys should expire after some time.&lt;/p&gt;
&lt;p&gt;Common TTL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;24 hours&lt;/li&gt;
&lt;li&gt;48 hours&lt;/li&gt;
&lt;li&gt;7 days (depending on business need)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Redis makes expiration easy.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;example-architecture&quot;&gt;Example Architecture&lt;/h2&gt;
&lt;p&gt;Client → API Gateway → Service → Redis → Database&lt;/p&gt;
&lt;p&gt;Redis handles idempotency keys&lt;br&gt;
Database handles actual data&lt;/p&gt;
&lt;p&gt;Fast and reliable.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;real-production-scenario-mobile-network-retry&quot;&gt;Real Production Scenario: Mobile Network Retry&lt;/h2&gt;
&lt;p&gt;Mobile user taps &quot;Place Order&quot;&lt;/p&gt;
&lt;p&gt;Network slow → timeout&lt;br&gt;
Client retries automatically&lt;/p&gt;
&lt;p&gt;Without idempotency → duplicate orders&lt;/p&gt;
&lt;p&gt;With idempotency → safe retry&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;important-difference-safe-retry-vs-unsafe-retry&quot;&gt;Important Difference: Safe Retry vs Unsafe Retry&lt;/h2&gt;
&lt;p&gt;Unsafe retry:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /orders
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Safe retry:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /orders
Idempotency-Key: unique-key
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;idempotency-vs-exactly-once-execution&quot;&gt;Idempotency vs Exactly-Once Execution&lt;/h2&gt;
&lt;p&gt;Exactly-once execution is impossible in distributed systems.&lt;/p&gt;
&lt;p&gt;Idempotency provides practical solution.&lt;/p&gt;
&lt;p&gt;It ensures same final state.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;best-practices-checklist&quot;&gt;Best Practices Checklist&lt;/h2&gt;
&lt;p&gt;Always:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use idempotency keys for POST APIs&lt;/li&gt;
&lt;li&gt;Store keys in Redis or database&lt;/li&gt;
&lt;li&gt;Store full response&lt;/li&gt;
&lt;li&gt;Use expiration&lt;/li&gt;
&lt;li&gt;Handle race conditions&lt;/li&gt;
&lt;li&gt;Use UUID keys&lt;/li&gt;
&lt;li&gt;Combine with database constraints&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;key-takeaway&quot;&gt;Key Takeaway&lt;/h2&gt;
&lt;p&gt;Retries are normal.&lt;/p&gt;
&lt;p&gt;Duplicate side effects are dangerous.&lt;/p&gt;
&lt;p&gt;Idempotency ensures your APIs remain safe, reliable, and production-ready.&lt;/p&gt;
&lt;p&gt;It is not optional for critical APIs - it is essential.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;If your API handles payments, orders, bookings, or financial transactions - idempotency is mandatory.&lt;/p&gt;
&lt;p&gt;Without it, retries can silently corrupt your system.&lt;/p&gt;
&lt;p&gt;With it, your APIs become reliable, predictable, and safe.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26972/idempotency-in-apis-why-your-retry-logic-can-break-everything-and-how-to-fix-it</guid>
<pubDate>Sat, 12 Sep 2026 13:30:46 +0000</pubDate>
</item>
<item>
<title>The &quot;Press-It-Twice&quot; Problem: Why Idempotency is Your API's Best Friend</title>
<link>https://coderlegion.com/25251/the-press-it-twice-problem-why-idempotency-is-your-apis-best-friend</link>
<description>&lt;h4 id=&quot;what-is-idempotency--1&quot;&gt;What is Idempotency?&lt;/h4&gt;
&lt;p&gt;Idempotency is a core design property of software systems where performing an operation multiple times produces the exact same result as running it once. In plain terms, it guarantees that if a command is accidentally repeated, the system behaves as if it only happened the first time. The server safely ignores any duplicate instructions while still confirming that the job was successfully done.&lt;/p&gt;
&lt;h4 id=&quot;the-elevator-button-analogy&quot;&gt;The Elevator Button Analogy&lt;/h4&gt;
&lt;p&gt;Imagine you are waiting in a lobby and press the button to call the elevator. The button lights up. If you get impatient and press that same button five more times, what happens? The elevator does not arrive any faster, and five different elevators do not suddenly descend to pick you up. The first press changed the state of the elevator system (it registered your call), and every subsequent press was safely ignored because the desired state had already been reached. The button is idempotent.&lt;/p&gt;
&lt;p&gt;In contrast, think of a non-idempotent action like buying a snack from a vending machine. If you press the button for a bag of chips once, you get one bag. If you press it five times, you will be charged five times and receive five bags. In software, we want critical actions—like checking out of an online store—to behave like the elevator button, not the vending machine.&lt;/p&gt;
&lt;h4 id=&quot;why-idempotency-matters-in-tech&quot;&gt;Why Idempotency Matters in Tech&lt;/h4&gt;
&lt;p&gt;In modern web development, networks are inherently unreliable. When you click &quot;Buy Now&quot; on a website, a request travels across the internet to a server. If the server processes your payment but your internet drops before you receive the confirmation screen, your browser doesn't know if the transaction succeeded. If you (or your browser) retry the request, a poorly designed system might charge your credit card a second time.&lt;/p&gt;
&lt;p&gt;By implementing idempotency, engineers prevent these costly duplicate operations. When a request is sent, it includes a unique identifier called an &quot;idempotency key&quot; (often a random string generated by the client). The server records this key. If it sees the same key again, it simply returns the saved response from the first attempt rather than processing the transaction a second time. This is vital for payment gateways, database migrations, and background email dispatchers.&lt;/p&gt;
&lt;h4 id=&quot;idempotency-in-action-javascript-&quot;&gt;Idempotency in Action (JavaScript)&lt;/h4&gt;
&lt;p&gt;Here is a simple example of how you can implement idempotency in a payment handler using a key-value store to track processed requests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const processedPayments = new Map();

function processPayment(idempotencyKey, amount, accountId) {
  // 1. Check if we have already processed this exact request
  if (processedPayments.has(idempotencyKey)) {
    console.log(&quot;Duplicate request detected. Returning cached result.&quot;);
    return processedPayments.get(idempotencyKey);
  }

  // 2. Perform the actual operation (mock transaction)
  console.log(`Processing fresh payment of $${amount} for account: ${accountId}`);
  const transactionResult = {
    status: &quot;success&quot;,
    transactionId: Math.floor(Math.random() * 100000),
    amount: amount,
    processedAt: new Date().toISOString()
  };

  // 3. Store the result associated with the unique key
  processedPayments.set(idempotencyKey, transactionResult);

  return transactionResult;
}

// First attempt: Processes successfully
const key = &quot;unique-order-xyz-123&quot;;
processPayment(key, 150.00, &quot;user_abc&quot;);

// Second attempt (e.g. user double-clicks or network retries): Returns cached data safely
processPayment(key, 150.00, &quot;user_abc&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h4&gt;
&lt;p&gt;Building idempotent APIs is the ultimate insurance policy against the chaos of the internet. It transforms fragile, duplicate-prone web transactions into bulletproof operations by ensuring that no matter how many times a client retries, your application state remains consistent, reliable, and trustworthy.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;resources-1&quot;&gt;Resources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Saurav-TB-Pandey/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-hook-lab:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;npm package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect with me on LinkedIn:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/pandeysaurav/&quot;&gt;Saurav Pandey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/08/double-clicks-and-network-glitches-how.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/saurav_tb_pandey/the-press-it-twice-problem-why-idempotency-is-your-apis-best-friend-3j3g&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25251/the-press-it-twice-problem-why-idempotency-is-your-apis-best-friend</guid>
<pubDate>Fri, 04 Sep 2026 06:30:04 +0000</pubDate>
</item>
<item>
<title>Idempotency: The Secret to Preventing Double Payments and Network Glitches</title>
<link>https://coderlegion.com/25253/idempotency-the-secret-to-preventing-double-payments-and-network-glitches</link>
<description>&lt;p&gt;Idempotency is a property of an operation where executing it multiple times has the exact same effect as executing it once. It means that if a network glitch or an impatient user triggers the same action repeatedly, the system state doesn't change after the first successful attempt. In simple terms, it is a design guarantee that safety-checks repetitive requests so they don't cause duplicate side effects.&lt;/p&gt;
&lt;h3 id=&quot;the-elevator-button-analogy-1&quot;&gt;The Elevator Button Analogy&lt;/h3&gt;
&lt;p&gt;Think about an elevator button. When you walk up to the elevator lobby and press the button for floor 5, the button lights up, registering your request. If you get impatient because the elevator is taking too long and press the button five more times, nothing different happens. The elevator doesn't queue up five separate trips to the fifth floor, nor does it spin out of control. The action of pressing the button is idempotent: one press or ten presses yield the exact same result.&lt;/p&gt;
&lt;p&gt;Conversely, a non-idempotent real-life action is buying a soda from a vending machine. If you press the button for a drink three times, you will be charged three times, and three sodas will drop out.&lt;/p&gt;
&lt;h3 id=&quot;why-idempotency-matters-in-software-engineering&quot;&gt;Why Idempotency Matters in Software Engineering&lt;/h3&gt;
&lt;p&gt;In the real world of software engineering, network connections are inherently unreliable. When you buy a pair of shoes online, your browser sends a request to the server to process your payment. If your Wi-Fi drops at that exact millisecond, your browser might not receive the confirmation message, even if the payment went through. Without idempotency, if the website automatically retries the payment—or if you manually click &quot;Submit&quot; again out of frustration—you would be charged twice.&lt;/p&gt;
&lt;p&gt;Software engineers use idempotency to build robust APIs (Application Programming Interfaces) that can safely receive the exact same payment request multiple times without charging the customer's credit card more than once. It is the ultimate shield against duplicate transactions, duplicate user sign-ups, and scrambled database records.&lt;/p&gt;
&lt;h3 id=&quot;implementing-idempotency-in-code&quot;&gt;Implementing Idempotency in Code&lt;/h3&gt;
&lt;p&gt;Here is a simple example of how engineers implement this safety net in JavaScript using an in-memory cache of unique identifiers, often called &quot;idempotency keys&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// A simplified system to process payments safely
const processedPayments = new Set();

function chargeUser(paymentId, amount, userId) {
  // Check if we have already successfully processed this specific payment ID
  if (processedPayments.has(paymentId)) {
    return {
      status: &quot;success&quot;,
      message: &quot;Duplicate request ignored. Transaction was already completed.&quot;
    };
  }

  // Simulate the actual credit card charging logic
  console.log(`Charging user ${userId} the amount of $${amount}...`);
  
  // Save the unique payment ID so we never process it again
  processedPayments.add(paymentId);

  return {
    status: &quot;success&quot;,
    message: `Successfully charged $${amount}.`
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code, the &lt;code&gt;paymentId&lt;/code&gt; acts as a unique token generated by the client app. If the network drops and the client sends the exact same &lt;code&gt;paymentId&lt;/code&gt; again, the server recognizes it instantly, bypasses the actual charging logic, and returns the original successful state without executing a double charge.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway-1&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;Ultimately, idempotency is the cornerstone of defensive programming in distributed networks. Rather than hoping that connections never drop or that users never double-click, professional developers design their systems to expect failure and handle retries gracefully. Making your systems idempotent is the single best way to ensure data integrity and build trust with your users, transforming fragile, error-prone operations into robust, bulletproof services.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;resources-2&quot;&gt;Resources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Saurav-TB-Pandey/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-hook-lab:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;npm package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect with me on LinkedIn:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/pandeysaurav/&quot;&gt;Saurav Pandey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/08/demystifying-idempotency-building.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/saurav_tb_pandey/idempotency-the-secret-to-preventing-double-payments-and-network-glitches-1e8d&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25253/idempotency-the-secret-to-preventing-double-payments-and-network-glitches</guid>
<pubDate>Thu, 03 Sep 2026 14:19:27 +0000</pubDate>
</item>
<item>
<title>The Golden Rule of API Design: Understanding Idempotency</title>
<link>https://coderlegion.com/25267/the-golden-rule-of-api-design-understanding-idempotency</link>
<description>&lt;p&gt;Have you ever clicked a &quot;Submit&quot; button on a website, experienced a sudden freeze, and wondered if clicking it again would charge your credit card twice? In the world of software development, engineers prevent this exact nightmare using a core design concept called idempotency.&lt;/p&gt;
&lt;p&gt;Idempotency is a property of an action or system design where performing an operation multiple times produces the exact same result as performing it once. In simple terms, it means that if an instruction is repeated, the system is smart enough to ensure that the outcome does not change after the first successful attempt. If you send the same command ten times, the system state behaves as if you only sent it once.&lt;/p&gt;
&lt;p&gt;To understand this, imagine an elevator call button. When you walk up to an elevator and press the &quot;Up&quot; button, the button lights up, and the elevator is summoned to your floor. If you become impatient and press that same button five more times, the elevator does not speed up, nor does it summon five different elevators. The elevator system registered your initial request, and all subsequent presses are ignored because they are idempotent. The end result remains identical: one elevator arrives at your floor.&lt;/p&gt;
&lt;p&gt;In the tech industry, idempotency is a daily necessity because computer networks are inherently unreliable. When a mobile application communicates with a server to process a $50 payment, three things must happen: the app sends the request, the server processes the payment, and the server sends back a confirmation. If the network drops &lt;em&gt;after&lt;/em&gt; the payment is processed but &lt;em&gt;before&lt;/em&gt; the confirmation reaches the app, the user's phone will report an error. &lt;/p&gt;
&lt;p&gt;Without idempotency, when the app automatically retries the payment request—or when the user gets frustrated and taps the payment button again—the server would charge the user another $50. By designing the payment API to be idempotent, engineers protect users from duplicate charges. The server recognizes a unique &quot;idempotency key&quot; sent with the transaction, notices that this specific transaction has already been completed, and safely returns the original success message without running the payment again.&lt;/p&gt;
&lt;p&gt;Here is a simple JavaScript code snippet demonstrating how an idempotent payment processor might look in backend software:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// A database simulation of already processed payment identifiers
const completedPaymentKeys = new Set();

function processTransaction(idempotencyKey, amount) {
  // 1. Check if we have already successfully processed this exact request
  if (completedPaymentKeys.has(idempotencyKey)) {
    return {
      status: &quot;ignored&quot;,
      message: &quot;This transaction was already processed. No extra charge applied.&quot;
    };
  }

  // 2. Perform the actual work if it is a new request
  console.log(`Charging the customer $${amount}...`);
  
  // 3. Save the key so we recognize it if it gets sent again
  completedPaymentKeys.add(idempotencyKey);

  return {
    status: &quot;success&quot;,
    message: `Successfully charged $${amount}.`
  };
}

// First attempt succeeds
console.log(processTransaction(&quot;tx_98765&quot;, 50)); 

// Second attempt (retry due to network lag) is safely ignored
console.log(processTransaction(&quot;tx_98765&quot;, 50));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ultimately, idempotency is the ultimate defensive programming practice for distributed networks where things can and will go wrong. By ensuring that your actions are safely repeatable, you eliminate the risk of duplicate data creation, incorrect billing, and system inconsistencies, resulting in a predictable and trustworthy user experience.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;resources-3&quot;&gt;Resources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Saurav-TB-Pandey/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-hook-lab:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;npm package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect with me on LinkedIn:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/pandeysaurav/&quot;&gt;Saurav Pandey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/08/why-idempotency-is-secret-to-crash.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/saurav_tb_pandey/the-golden-rule-of-api-design-understanding-idempotency-2pic&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25267/the-golden-rule-of-api-design-understanding-idempotency</guid>
<pubDate>Tue, 01 Sep 2026 01:11:43 +0000</pubDate>
</item>
<item>
<title>Retry Is Not Re-Decide: Idempotency-by-ID Is the First Invariant for LLM Pipelines</title>
<link>https://coderlegion.com/25570/retry-is-not-re-decide-idempotency-by-id-is-the-first-invariant-for-llm-pipelines</link>
<description>&lt;p&gt;&lt;em&gt;Against an LLM endpoint there is no “again.” A retry is a re-sample, and if the call is a decision, your fault tolerance is quietly moonlighting as a sampling policy nobody chose.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Researchers at Thinking Machines Lab recently ran an experiment that every engineer with a &lt;code&gt;max_retries=3&lt;/code&gt; in their LLM pipeline should sit with. They sent the same prompt to Qwen3-235B one thousand times, at temperature zero, greedy decoding, the setting everyone calls “deterministic.” They got back 80 distinct completions. The outputs were identical for the first 102 tokens. At token 103, the paths split: some completions said “Queens, New York,” others said “New York City,” and from there the texts diverged for good.&lt;/p&gt;
&lt;p&gt;Eighty different answers to one question, at the temperature that is supposed to mean “always the same answer.”&lt;/p&gt;
&lt;p&gt;The cause is the genuinely surprising part. It is not floating-point folklore, and it is not information leaking between requests. The forward pass of a production inference server lacks what the authors call batch invariance: the numerical result of the computation depends on the batch it ran inside. In their words, “when a non-batch-invariant kernel is used as part of a larger inference system, the system can become nondeterministic.” Your request gets batched with whatever other traffic hit the server that millisecond, the kernel's reduction order shifts with batch shape, tiny numerical differences change one token, and autoregression amplifies one token into a different document. Which means the answer you got depends, in a small but real way, on who else was talking to the model at the time. Nobody reasons about that when they add a retry loop. Almost nobody knows it is true.&lt;/p&gt;
&lt;p&gt;Now put that fact next to the humble retry, the most reflexive piece of plumbing in distributed systems. The whole mental model of a retry is: the operation failed, so run &lt;em&gt;the same operation&lt;/em&gt; again. Against an LLM endpoint, that operation does not exist. There is no “again.” Attempt two runs in a different batch, against different concurrent load, and measurably often returns a different answer. The word “retry” is false advertising. What you built is a re-sample. And if the call you are re-sampling is a &lt;em&gt;decision&lt;/em&gt; (a judge model scoring an eval item, a classifier routing a ticket, an extractor committing a value), then your retry loop is not fault-tolerance machinery. It is a second-opinion machine that silently overwrites first opinions, with the tiebreak decided by whichever attempt happened to complete.&lt;/p&gt;
&lt;p&gt;Classical pipelines have their own version of duplicate trouble, and it is well-trodden territory: a recovery process requeues finished work, duplicates fan out, you pay twice and clean up the mess. We have written about that failure shape before, and the distributed-systems literature has owned it for decades, back to the impossibility results about exactly-once delivery. But notice what makes the classical case survivable: a duplicate execution of a deterministic task produces a &lt;em&gt;copy&lt;/em&gt;. Wasteful, embarrassing, semantically harmless. Run the same video transcode twice and you get the same video. The LLM case breaks that comfort. A duplicate execution produces a &lt;em&gt;disagreement&lt;/em&gt;. The duplicate is not a copy of your data; it is a competing claim about the truth, and unless you built something to prevent it, the loser of that competition vanishes without a trace.&lt;/p&gt;
&lt;h3 id=&quot;the-retry-loop-is-a-biased-sampler&quot;&gt;The retry loop is a biased sampler&lt;/h3&gt;
&lt;p&gt;Here is the part I have not seen anyone say about eval pipelines, so take it as this essay's argument rather than a citation.&lt;/p&gt;
&lt;p&gt;Nobody retries uniformly. You retry on failure: the timeout, the rate-limit response, the malformed JSON that would not parse. And those failure modes are not independent of content. The long, gnarly, ambiguous items are precisely the ones that blow past token limits, time out on slow decodes, or produce output mangled enough to fail parsing. Easy items complete on attempt one. Hard items complete, disproportionately, on attempt two or three, in a different batch, as a different sample from the model's answer distribution.&lt;/p&gt;
&lt;p&gt;Follow that to its conclusion. The verdicts in your results table are not “the model's judgment on each item.” They are “the model's judgment conditioned on the attempt that happened to complete,” and the conditioning is strongest on exactly the difficult items you built the eval to probe. Your dataset of verdicts has quietly become a weighted draw from the model's opinion distribution, where the weights were assigned by infrastructure noise: server load, batch composition, whoever else was querying that second. Two runs of the “same” eval suite can disagree, not because the model changed, but because the lottery of which attempts landed came out differently. When someone then asks why this week's pass rate moved two points, you will look for a model regression, a data change, a prompt bug. The real culprit can be that your retry policy is a sampling policy, and nobody chose it.&lt;/p&gt;
&lt;p&gt;It helps to be concrete about what the pollution looks like downstream, because “phantom duplicates” sounds abstract until you have debugged one. Two committed verdicts for one item means your aggregation query either double-counts the item, keeps whichever row it met last, or crashes on a uniqueness assumption someone made three services away. Pass rates drift depending on query order. A dashboard shows 1,043 judgments for a 1,000-item suite and nobody can say which 43 items are twins, or which of each twin's two opinions is the one a human already acted on. The cleanup is a forensic exercise in deciding, after the fact, which of two model opinions was “real.” There is no principled answer to that question. The only good time to answer it is before the second opinion exists.&lt;/p&gt;
&lt;p&gt;That is why the fix matters more than its size suggests. It is tempting to call it a one-line check: “this ID already has a committed verdict, skip.” As a first approximation, yes. Once a decision is committed for an item, later attempts, stragglers, requeues, and duplicate messages hit the check and become no-ops. You pay for the compute once, and, more importantly, exactly one opinion per item enters the record. The retry loop goes back to being fault-tolerance instead of a hidden second-opinion generator.&lt;/p&gt;
&lt;p&gt;But the one-line framing undersells both the pattern's pedigree and its subtlety, and the subtlety is where implementations quietly go wrong. Two corrections, both standard, both worth the paragraph.&lt;/p&gt;
&lt;h3 id=&quot;the-key-is-a-design-and-the-check-is-a-constraint&quot;&gt;The key is a design, and the check is a constraint&lt;/h3&gt;
&lt;p&gt;First: what the ID keys on. This pattern is not folk wisdom; it has a written standard. The IETF's httpapi working group produced a draft specifically for it, the Idempotency-Key HTTP header, which exists to “make non-idempotent HTTP methods such as POST or PATCH fault-tolerant,” was explicitly inspired by the production idempotency machinery at Stripe and PayPal (with a nod to Mark Nottingham's old “POST Once Exactly” draft), and recommends a UUID or similar random identifier as the key. The draft's most recent revision dates to late 2025; drafts lapse, but the pattern it codifies is running in every serious payment API on earth. And the sentence in it that earns its keep for our purposes is this one: “The idempotency key MUST be unique and MUST NOT be reused with another request with a different request payload.”&lt;/p&gt;
&lt;p&gt;Read that clause against the naive implementation, which keys on &lt;code&gt;item_id&lt;/code&gt; alone. The moment your prompt template changes, your judge model rolls to a new version, or your rubric gets edited, the request payload for that item is different, and the correct behavior is a &lt;em&gt;new&lt;/em&gt; verdict. A bare item-ID check cannot tell the difference; it will pin the stale verdict forever and silently serve you last month's judgment against this month's rubric. The spec already told you the naive version is wrong. The key that honors it is composite: something like &lt;code&gt;(item_id, prompt_version, model_id, rubric_version)&lt;/code&gt;. Same payload, same verdict, forever; changed payload, fresh decision. The idempotency key is not a deduplication hack. It is a declaration of which changes are supposed to change the answer, which makes designing it a small act of epistemology, not plumbing.&lt;/p&gt;
&lt;p&gt;Second: how the check executes. “Check if a verdict exists, then write one” reads correctly and is a race. Two workers, both holding a retry for the same item, both run the existence check, both see nothing, both write. Under exactly the concurrency that motivated your retries, check-then-write manufactures the duplicate decisions it was built to prevent. The real one-liner is not an &lt;code&gt;if&lt;/code&gt; statement; it is a uniqueness guarantee in the storage layer: a UNIQUE constraint on the composite key with an &lt;code&gt;INSERT ... ON CONFLICT DO NOTHING&lt;/code&gt;, a conditional put, a transactional upsert. Let the database refuse the second opinion atomically. The difference between the &lt;code&gt;if&lt;/code&gt; and the constraint is invisible in the demo and decisive in production, which is the profile of most distributed-systems bugs worth knowing about.&lt;/p&gt;
&lt;p&gt;One placement note, since the IETF draft describes a client-server header and your pipeline probably is not one. The header pattern exists because Stripe cannot trust its clients; the key travels with the request so the &lt;em&gt;server&lt;/em&gt; can deduplicate. Inside your own eval pipeline you own both sides, which simplifies the answer: enforce at the layer where the truth lives, the verdict store, and let every producer be as dumb as it likes. Workers do not coordinate, do not check, do not care; they attempt writes, and the constraint arbitrates. Pushing the invariant to the data layer is what makes it survive the next refactor, the second worker pool, and the colleague who adds a new retry path without reading this essay.&lt;/p&gt;
&lt;p&gt;There is also a systemic reason to get this right that goes beyond your own bill. The distributed-systems community has a name, metastable failure, for outages that sustain themselves after the trigger is gone, and a HotOS '21 study by Bronson and colleagues found retry storms to be a classic engine of them: retries amplify load, which causes failures, which cause retries. Every failure they studied came from real production cloud systems. LLM pipelines add a nasty twist, and flag this as inference rather than citation: the resource being exhausted is a metered token quota, and rate-limit responses are themselves the retryable error, so an undisciplined retry loop burns exactly the budget the pipeline needs to recover while generating more of the signal that triggers it. Idempotency keys, retry budgets, and backoff with jitter are the standard triad; the key is the piece that caps the &lt;em&gt;decision&lt;/em&gt; damage while the other two cap the load.&lt;/p&gt;
&lt;h3 id=&quot;what-idempotency-does-not-buy&quot;&gt;What idempotency does not buy&lt;/h3&gt;
&lt;p&gt;Honesty about the limit, because it points at the deeper practice. Idempotency-by-ID commits &lt;em&gt;a&lt;/em&gt; verdict per key. Not the &lt;em&gt;right&lt;/em&gt; verdict. If attempt one drew an unlucky sample from those eighty possible completions, the constraint now faithfully preserves the unlucky answer forever. Idempotency buys reproducibility and cost control. It does not buy accuracy.&lt;/p&gt;
&lt;p&gt;If accuracy on contested items is what you need, the answer is not retrying harder; it is the mirror image of a retry. Sample the model deliberately: run five judgments on purpose, with an aggregation rule you wrote down, a budget you chose, and the variance &lt;em&gt;recorded&lt;/em&gt; in your results instead of hidden behind whichever attempt survived. A retry is unplanned resampling with the disagreement discarded. An ensemble is planned resampling with the disagreement measured. The same API calls, opposite epistemics. You can also, at the infrastructure level, buy determinism outright: the Thinking Machines team shipped batch-invariant kernels that made all one thousand completions bitwise identical, at a real performance cost, 26 seconds for standard vLLM versus 42 with their improved deterministic attention kernel. Determinism is purchasable, and for some compliance-shaped workloads it is worth the price. For everyone else, the cheap resolution of the whole essay is the one-row version: the call whose result you never re-request costs nothing extra, drifts nowhere, and disagrees with nobody.&lt;/p&gt;
&lt;p&gt;So, the practical insight, in the order you should apply it. Give every decision in your pipeline a committed identity: a composite key naming the item and everything that legitimately changes the answer. Enforce it with an atomic constraint, not a check. Let retries hit the constraint and die as no-ops, so your fault tolerance stops moonlighting as an unlogged sampling policy. And when a decision is important enough that one draw from eighty possible answers is not good enough, do not retry your way to confidence. Decide, deliberately, how many opinions to buy, and write down how much they disagreed. The retry loop was never qualified to make that call. At token 103, it does not even know it is making one.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Thinking Machines Lab, “Defeating Nondeterminism in LLM Inference.” (1,000 temperature-0 completions from Qwen3-235B yielding 80 distinct outputs, identical for the first 102 tokens; lack of batch invariance: “when a non-batch-invariant kernel is used as part of a larger inference system, the system can become nondeterministic”; batch-invariant kernels producing 1,000 bitwise-identical completions at a performance cost: 26s standard vLLM vs 55s unoptimized / 42s improved deterministic attention.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IETF httpapi Working Group, “The Idempotency-Key HTTP Header Field” (draft-ietf-httpapi-idempotency-key-header-07, most recent revision October 2025; draft has since lapsed). Purpose: “make non-idempotent HTTP methods such as POST or PATCH fault-tolerant.” Key clause: “The idempotency key MUST be unique and MUST NOT be reused with another request with a different request payload”; UUID recommended; acknowledged inspiration from PayPal and Stripe practice and Mark Nottingham's “POST Once Exactly.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bronson, N., Aghayev, A., Charapko, A., &amp;amp; Zhu, T. (2021). “Metastable Failures in Distributed Systems.” HotOS '21. (Self-sustaining failure states; retry storms as amplification; all failures studied drawn from production cloud systems; mitigations including backoff with jitter and retry budgets.) The token-quota feedback specific to LLM APIs is this essay's inference, not the paper's claim.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fischer, M., Lynch, N., &amp;amp; Paterson, M. (1985). &lt;em&gt;JACM.&lt;/em&gt; (The classical impossibility backdrop for exactly-once delivery: referenced, not re-argued; exactly-once &lt;em&gt;processing&lt;/em&gt; via idempotent consumers remains achievable, which is this essay's subject.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The biased-sampler analysis of retry-on-failure conditioning eval verdicts on completion is presented as this essay's argument.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Give every decision in your pipeline a committed identity, and let exactly one opinion per item enter the record.&lt;/p&gt;
&lt;p&gt;That is the invariant &lt;strong&gt;Chain of Consciousness&lt;/strong&gt; is built to hold. Every decision an agent commits gets a tamper-evident record keyed to what it was deciding about and the version of everything that legitimately changes the answer, so a retry, a straggler, or a duplicate message hits a committed identity instead of overwriting a first opinion with a second. One decision, one durable entry, reproducible on replay, with the disagreement measured on purpose when you choose to sample rather than discarded by whichever attempt happened to land.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/hosted-coc/&quot;&gt;See Hosted Chain of Consciousness&lt;/a&gt;  ·  &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/whitepaper/theory-of-agent-trust/&quot;&gt;Read the Theory of Agent Trust&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install chain-of-consciousness&lt;/code&gt;  ·  &lt;code&gt;npm install chain-of-consciousness&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or the whole trust stack at once: &lt;code&gt;pip install agent-trust-stack&lt;/code&gt; / &lt;code&gt;npm install agent-trust-stack&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/retry-is-not-re-decide-idempotency-by-id-is-the-first-invari/&quot;&gt;vibeagentmaking.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>DevOps &amp; Infra</category>
<guid isPermaLink="true">https://coderlegion.com/25570/retry-is-not-re-decide-idempotency-by-id-is-the-first-invariant-for-llm-pipelines</guid>
<pubDate>Fri, 28 Aug 2026 04:45:50 +0000</pubDate>
</item>
<item>
<title>Understanding Idempotency in Software Engineering</title>
<link>https://coderlegion.com/25234/understanding-idempotency-in-software-engineering</link>
<description>&lt;h2 id=&quot;idempotency&quot;&gt;Idempotency&lt;/h2&gt;
&lt;p&gt;In software engineering, an operation is considered idempotent if applying it multiple times has the same result as applying it once.&lt;/p&gt;
&lt;p&gt;For example, an API endpoint PUT /user/1 that updates a user's name is idempotent because making the same request twice leaves the system in the same state. A POST /user endpoint that creates a new user each time is not idempotent.&lt;/p&gt;
&lt;p&gt;This is a test post from the react-hook-lab publishing workflow!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25234/understanding-idempotency-in-software-engineering</guid>
<pubDate>Mon, 24 Aug 2026 12:14:11 +0000</pubDate>
</item>
</channel>
</rss>