<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sundarapandy</title>
    <description>The latest articles on DEV Community by Sundarapandy (@sundarapandy).</description>
    <link>https://dev.to/sundarapandy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3876705%2Fde19f23b-c7fe-4a97-b596-6283a1af595a.png</url>
      <title>DEV Community: Sundarapandy</title>
      <link>https://dev.to/sundarapandy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sundarapandy"/>
    <language>en</language>
    <item>
      <title>What Happens When Two Transactions From the Same Wallet Use the Same Nonce?</title>
      <dc:creator>Sundarapandy</dc:creator>
      <pubDate>Wed, 14 Oct 2026 06:16:58 +0000</pubDate>
      <link>https://dev.to/sundarapandy/what-happens-when-two-transactions-from-the-same-wallet-use-the-same-nonce-141l</link>
      <guid>https://dev.to/sundarapandy/what-happens-when-two-transactions-from-the-same-wallet-use-the-same-nonce-141l</guid>
      <description>&lt;p&gt;Imagine a backend service sending transactions from the same Ethereum wallet. The first transaction is still pending when the application creates another one. Because the application has not updated its nonce correctly, both transactions are assigned the same nonce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now what happens?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Can both transactions execute? Does one replace the other? Why might the second transaction return a &lt;strong&gt;nonce too low&lt;/strong&gt; or &lt;strong&gt;replacement transaction underpriced&lt;/strong&gt; error?&lt;/p&gt;

&lt;p&gt;These situations are common when applications submit multiple EVM transactions concurrently. Understanding how nonce management works is therefore important for developers building wallets, trading systems, DeFi applications, payment platforms, and blockchain automation services.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Nonce in an EVM Transaction?
&lt;/h2&gt;

&lt;p&gt;A nonce is a sequence number associated with an account. For transactions sent from the same Ethereum or EVM-compatible account, the nonce determines their transaction order.&lt;/p&gt;

&lt;p&gt;For example, a wallet might have transactions like this:&lt;br&gt;
&lt;strong&gt;Nonce 10 → Transaction A&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Nonce 11 → Transaction B&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Nonce 12 → Transaction C&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The transaction with nonce 10 needs to be processed before the transaction with nonce 11, and so on.&lt;/p&gt;

&lt;p&gt;The nonce is not a global blockchain counter. It belongs to the individual sender account. This means two different wallets can have the same nonce without creating a conflict.&lt;/p&gt;

&lt;p&gt;The problem occurs when &lt;strong&gt;two transactions from the same wallet use the same nonce.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How Can Two Transactions Get the Same Nonce?
&lt;/h2&gt;

&lt;p&gt;A nonce conflict can happen when multiple transactions are generated almost simultaneously.&lt;br&gt;
Consider an application that reads the wallet's current transaction count:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Current nonce = 15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application creates Transaction A:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transaction A → Nonce 15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before Transaction A is confirmed, another process asks for the wallet's nonce. If the application does not account for the pending transaction, it may create:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transaction B → Nonce 15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now both transactions are competing for the same nonce.&lt;/p&gt;

&lt;p&gt;This can happen in several ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple backend workers send transactions from the same wallet.&lt;/li&gt;
&lt;li&gt;Several API requests trigger blockchain transactions at nearly the same time.&lt;/li&gt;
&lt;li&gt;The application tracks only confirmed transactions.&lt;/li&gt;
&lt;li&gt;Nonce state is stored locally but is not synchronized between workers.&lt;/li&gt;
&lt;li&gt;An RPC node provides a view of the account state that does not include a transaction already being managed elsewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv3wmioi3lok3lh1riocq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv3wmioi3lok3lh1riocq.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For applications sending transactions concurrently, nonce assignment needs to be treated as a coordinated process rather than something every worker calculates independently.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Happens When Both Transactions Enter the Mempool?
&lt;/h2&gt;

&lt;p&gt;When a transaction is submitted, it is propagated to nodes and may remain pending before being included in a block.&lt;/p&gt;

&lt;p&gt;If two transactions from the same account use nonce 15, they cannot both represent separate sequential positions in the account's transaction sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transaction A&lt;br&gt;
Sender: 0xABC...&lt;br&gt;
Nonce: 15&lt;/p&gt;

&lt;p&gt;Transaction B&lt;br&gt;
Sender: 0xABC...&lt;br&gt;
Nonce: 15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The transactions are effectively competing for the same nonce position.&lt;/p&gt;

&lt;p&gt;EVM clients have transaction-pool rules for handling this situation. Depending on the client and transaction conditions, a second transaction may be rejected, or it may be considered as a replacement candidate for the first transaction.&lt;/p&gt;

&lt;p&gt;This is where transaction fees become important.&lt;/p&gt;

&lt;p&gt;A replacement transaction generally needs to satisfy the node's replacement rules, which commonly require a sufficient fee increase over the transaction already occupying that nonce in the local transaction pool.&lt;/p&gt;

&lt;p&gt;That is why submitting another transaction with the same nonce does not automatically mean the first one will be replaced.&lt;/p&gt;
&lt;h2&gt;
  
  
  Can Both Transactions Be Confirmed?
&lt;/h2&gt;

&lt;p&gt;No. Two transactions from the same account cannot both be included in the canonical chain using the same nonce.&lt;/p&gt;

&lt;p&gt;Suppose the following transactions exist:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transaction A → Nonce 15 → Send 1 ETH&lt;br&gt;
Transaction B → Nonce 15 → Call Smart Contract&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Transaction A is included in the canonical chain, nonce 15 has been consumed by that account.&lt;/p&gt;

&lt;p&gt;Transaction B cannot subsequently execute as another transaction with nonce 15.&lt;/p&gt;

&lt;p&gt;The result can therefore look like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transaction A → Nonce 15 → Confirmed&lt;br&gt;
Transaction B → Nonce 15 → Not executable as a second canonical transaction&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sequencing mechanism is important because it prevents multiple conflicting transactions from the same account from occupying the same transaction position.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Replacement Transaction Scenario
&lt;/h2&gt;

&lt;p&gt;There is an important distinction between an accidental duplicate nonce and an intentional replacement.&lt;/p&gt;

&lt;p&gt;Suppose a user sends a transaction with nonce 15, but it remains pending:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nonce: 15&lt;br&gt;
Fee: Lower&lt;br&gt;
Status: Pending&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user may submit another transaction using the same nonce with a higher fee:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nonce: 15&lt;br&gt;
Fee: Higher&lt;br&gt;
Status: Pending&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The purpose is to give the replacement transaction a better chance of being accepted by nodes and included in a block.&lt;/p&gt;

&lt;p&gt;Wallet applications may expose this behavior through features such as speed up or transaction replacement.&lt;/p&gt;

&lt;p&gt;A replacement does not mean both transactions will execute. The nonce still represents one transaction position.&lt;/p&gt;

&lt;p&gt;If the replacement is accepted and eventually included in the canonical chain, the original transaction cannot later execute using that same nonce.&lt;/p&gt;

&lt;p&gt;This is why developers should think of nonce replacement as a competition for one transaction slot rather than two independent transactions.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Developers See When Nonce Management Goes Wrong
&lt;/h2&gt;

&lt;p&gt;Nonce conflicts often become visible through RPC or wallet errors.&lt;br&gt;
&lt;strong&gt;nonce too low&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This generally indicates that the submitted nonce is already below the account's expected transaction sequence. For example, another transaction may already have consumed that nonce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;replacement transaction underpriced&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can occur when an application submits a transaction with the same nonce as an existing pending transaction but does not increase the fee enough to satisfy the node's replacement requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;already known&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A node may return this when the transaction being submitted is already known to its transaction pool.&lt;/p&gt;

&lt;p&gt;There can also be less obvious symptoms.&lt;/p&gt;

&lt;p&gt;A transaction may remain pending because a later transaction depends on an earlier nonce. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nonce 20 → Pending&lt;br&gt;
Nonce 21 → Waiting&lt;br&gt;
Nonce 22 → Waiting&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even though transactions &lt;strong&gt;21&lt;/strong&gt; and &lt;strong&gt;22&lt;/strong&gt; have already been created, the account's transaction sequence cannot simply skip nonce &lt;strong&gt;20&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is one reason nonce problems can appear as broader transaction-processing failures in production systems.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Applications Prevent Nonce Conflicts
&lt;/h2&gt;

&lt;p&gt;For a simple wallet sending occasional transactions, nonce management may appear straightforward. It becomes much more important when several application processes can send transactions from the same account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track Pending Transactions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applications should account for transactions that have already been submitted but have not yet been confirmed.&lt;/p&gt;

&lt;p&gt;Looking only at the latest confirmed transaction can cause multiple workers to select the same next nonce.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use a Nonce Manager
&lt;/h2&gt;

&lt;p&gt;A dedicated nonce manager can assign each outgoing transaction a unique sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction Request
  ↓
Nonce Manager
  ↓
Nonce 25 assigned
  ↓
Transaction signed
  ↓
RPC submission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next request receives nonce 26, even if transaction 25 is still pending&lt;/p&gt;

&lt;h2&gt;
  
  
  Serialize Transaction Creation
&lt;/h2&gt;

&lt;p&gt;A transaction queue can prevent several workers from assigning the same nonce simultaneously.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction Requests
 ↓
Transaction Queue
 ↓
Nonce Assignment
  ↓
Transaction Signing
  ↓
RPC Node
   ↓
Blockchain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially useful for exchanges, automated trading systems, payment services, and other applications that generate transactions at high frequency.&lt;/p&gt;

&lt;p&gt;Handle Replacements Carefully&lt;/p&gt;

&lt;p&gt;If a transaction needs to be replaced, the application needs to preserve the same nonce while applying the appropriate replacement fee rules.&lt;/p&gt;

&lt;p&gt;It should also track which transaction is currently considered active for that nonce.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Real-World Example
&lt;/h2&gt;

&lt;p&gt;Consider a wallet whose next available nonce is &lt;strong&gt;42&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Two requests arrive almost simultaneously:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Request A → Nonce 42 → Transfer tokens&lt;br&gt;
Request B → Nonce 42 → Call a smart contract&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both transactions cannot independently execute.&lt;/p&gt;

&lt;p&gt;If Request A is accepted into the transaction pool and Request B does not meet the replacement requirements, Request B may be rejected.&lt;/p&gt;

&lt;p&gt;If Request B is submitted as a valid replacement with the required fee increase, it may replace Request A in the relevant transaction pool.&lt;/p&gt;

&lt;p&gt;Eventually, only one transaction can consume nonce 42 on the canonical chain.&lt;/p&gt;

&lt;p&gt;After that, the next transaction from the wallet needs the following nonce:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nonce 42 → One transaction&lt;br&gt;
Nonce 43 → Next transaction&lt;br&gt;
Nonce 44 → Next transaction&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key point is that the nonce is tied to the account's transaction sequence, not to the individual application request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Nonce Management Matters in Production
&lt;/h2&gt;

&lt;p&gt;Nonce conflicts become more significant as blockchain applications scale.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.coinsclone.com/cryptocurrency-exchange-development-company/" rel="noopener noreferrer"&gt;crypto exchange&lt;/a&gt; may have multiple services interacting with the same operational wallet. A trading bot may generate several transactions within seconds. A DeFi application may trigger automated contract interactions based on market conditions.&lt;/p&gt;

&lt;p&gt;If all these processes independently determine the next nonce, collisions can occur.&lt;/p&gt;

&lt;p&gt;A production transaction system therefore needs to treat nonce assignment as part of its infrastructure.&lt;/p&gt;

&lt;p&gt;A typical architecture might include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Services
  ↓
Transaction Queue
  ↓
Nonce Manager
  ↓
Signer
   ↓
RPC Provider
   ↓
Blockchain Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach separates transaction generation from nonce assignment and gives the application a controlled way to track pending, confirmed, failed, and replacement transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A nonce is more than a number attached to an EVM transaction. It determines where that transaction fits into the sequence of transactions sent by a particular account.&lt;/p&gt;

&lt;p&gt;When two transactions from the same wallet use the same nonce, they compete for the same transaction position. Both cannot execute with that nonce on the canonical chain. Depending on the transaction-pool rules and fee configuration, one may replace the other while pending, or the second transaction may be rejected.&lt;/p&gt;

&lt;p&gt;For developers, the bigger lesson is that nonce management needs to account &lt;strong&gt;for pending transactions, concurrent requests, transaction replacement, and transaction ordering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As blockchain applications move toward higher transaction volumes and more automated workflows, reliable nonce management becomes an important part of the transaction infrastructure itself.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>exchange</category>
      <category>cryptowallet</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>How Exchanges and Indexers Handle Blockchain Reorgs: Confirmation Depth, Rollbacks, and Finality</title>
      <dc:creator>Sundarapandy</dc:creator>
      <pubDate>Wed, 07 Oct 2026 10:15:05 +0000</pubDate>
      <link>https://dev.to/sundarapandy/how-exchanges-and-indexers-handle-blockchain-reorgs-confirmation-depth-rollbacks-and-finality-2mca</link>
      <guid>https://dev.to/sundarapandy/how-exchanges-and-indexers-handle-blockchain-reorgs-confirmation-depth-rollbacks-and-finality-2mca</guid>
      <description>&lt;p&gt;A blockchain transaction can appear confirmed one moment and become part of a different chain history later. For a normal wallet user, a short blockchain reorganization, or reorg, may go unnoticed. For an exchange or blockchain indexer, however, it can create a much bigger problem.&lt;/p&gt;

&lt;p&gt;Imagine a user deposits 2 ETH into an exchange. The transaction is included in a block, the exchange detects it, and its internal system begins counting confirmations. A few blocks later, the network reorganizes. The block containing the deposit is no longer part of the canonical chain.&lt;br&gt;
The exchange now has a question to answer: Should the deposit still be considered valid?&lt;/p&gt;

&lt;p&gt;This is where confirmation depth, reorg detection, rollbacks, and finality become important. Exchanges and indexers cannot simply assume that the latest blockchain state is permanent. They need mechanisms to identify chain changes, reverse affected application state, and determine when a transaction is safe to treat as final.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is a Blockchain Reorg?
&lt;/h2&gt;

&lt;p&gt;A blockchain reorganization occurs when the network replaces part of its recent chain history with another valid chain branch.&lt;/p&gt;

&lt;p&gt;Under normal conditions, blocks form a sequence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Block 100 → Block 101 → Block 102 → Block 103&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But competing blocks can sometimes be produced around the same point:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;→ Block 102A → Block 103A&lt;br&gt;
Block 100 → Block 101&lt;br&gt;
                 → Block 102B → Block 103B&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The network eventually selects one branch as the canonical chain. The blocks belonging to the other branch are no longer part of the accepted chain.&lt;/p&gt;

&lt;p&gt;This means transactions included in the discarded blocks need to be reconsidered.&lt;/p&gt;

&lt;p&gt;A transaction might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remain included in the new canonical chain&lt;/li&gt;
&lt;li&gt;Become pending again&lt;/li&gt;
&lt;li&gt;Be included in a replacement block&lt;/li&gt;
&lt;li&gt;Disappear from the canonical chain entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For applications that store blockchain data in databases, this distinction matters. An indexer may have already processed an event from a block that is later removed. An exchange may have already detected a deposit that is no longer confirmed on the canonical chain.&lt;/p&gt;

&lt;p&gt;A reorg therefore turns blockchain data from a simple append-only stream into something applications must sometimes &lt;strong&gt;reconcile and rebuild&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Reorgs Are a Problem for Exchanges and Indexers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Exchanges and indexers both consume blockchain data, but they use it differently.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;indexer&lt;/strong&gt; continuously reads blocks, transactions, logs, and contract events and converts them into structured data that applications can query.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;exchange&lt;/strong&gt; uses blockchain data to manage deposits, withdrawals, balances, confirmations, and settlement.&lt;/p&gt;

&lt;p&gt;Both systems face the same fundamental issue:&lt;/p&gt;

&lt;p&gt;A transaction being included in a block does not necessarily mean that its position in the blockchain is permanently settled.&lt;/p&gt;

&lt;p&gt;For an indexer, a reorg can invalidate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indexed blocks&lt;/li&gt;
&lt;li&gt;Transaction records&lt;/li&gt;
&lt;li&gt;Token transfers&lt;/li&gt;
&lt;li&gt;Smart contract events&lt;/li&gt;
&lt;li&gt;Calculated balances&lt;/li&gt;
&lt;li&gt;Application-specific state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an exchange, the consequences can be even more sensitive. A deposit could be credited to a user's internal balance before the block containing that deposit becomes sufficiently stable.&lt;/p&gt;

&lt;p&gt;This is why production systems usually separate states such as detected, confirmed, credited, and finalized instead of treating every observed transaction as immediately trustworthy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Confirmation Depth: How Many Blocks Are Enough?
&lt;/h2&gt;

&lt;p&gt;Confirmation depth is one of the simplest ways applications reduce reorg risk.&lt;/p&gt;

&lt;p&gt;Suppose a transaction is included in Block 500:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction → Block 500
        ↓
     Block 501
        ↓
     Block 502
        ↓
      Block 503
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As additional blocks are added after Block 500, the transaction gains more confirmations.&lt;/p&gt;

&lt;p&gt;An exchange might define a policy such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0 confirmations → Detected&lt;br&gt;
1 confirmation  → Confirmed&lt;br&gt;
6 confirmations → Credit deposit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The exact number depends on the blockchain, asset, risk model, transaction value, and network conditions.&lt;/p&gt;

&lt;p&gt;The basic principle is straightforward:&lt;br&gt;
The deeper a transaction sits inside the chain, the less likely a normal short reorg will remove it.&lt;/p&gt;

&lt;p&gt;However, confirmation depth does not mean absolute finality.&lt;br&gt;
Waiting for additional blocks introduces a trade-off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fewer confirmations&lt;/strong&gt;: faster deposits but greater reorg exposure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More confirmations&lt;/strong&gt;: greater safety but slower user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An exchange therefore needs to choose a confirmation policy that balances security and usability.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Happens During a Reorg?
&lt;/h2&gt;

&lt;p&gt;Consider a deposit included in Block 502:&lt;br&gt;
&lt;code&gt;Block 500&lt;br&gt;
   ↓&lt;br&gt;
Block 501&lt;br&gt;
   ↓&lt;br&gt;
Block 502 → 2 ETH deposit&lt;br&gt;
   ↓&lt;br&gt;
Block 503&lt;/code&gt;&lt;br&gt;
The exchange's indexer has already processed the transaction.&lt;br&gt;
Now the network produces a competing branch:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Block 500&lt;br&gt;
   ↓&lt;br&gt;
Block 501&lt;br&gt;
   ↓&lt;br&gt;
Block 502A&lt;br&gt;
   ↓&lt;br&gt;
Block 503A&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the original Block 502 is removed from the canonical chain, the exchange's internal representation is now potentially outdated.&lt;/p&gt;

&lt;p&gt;The application must determine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which block was replaced?&lt;/li&gt;
&lt;li&gt;What is the common ancestor?&lt;/li&gt;
&lt;li&gt;Which blocks were removed?&lt;/li&gt;
&lt;li&gt;Which replacement blocks were added?&lt;/li&gt;
&lt;li&gt;Which transactions and events were affected?&lt;/li&gt;
&lt;li&gt;Does the original deposit still exist in the canonical chain?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important point is that a reorg is not simply a "missing block" problem. It is a &lt;strong&gt;state consistency problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The application must bring its internal database back into agreement with the canonical blockchain.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Blockchain Indexers Detect Reorganizations
&lt;/h2&gt;

&lt;p&gt;A reorg-aware indexer needs more than block numbers.&lt;br&gt;
It should track information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block number&lt;/li&gt;
&lt;li&gt;Block hash&lt;/li&gt;
&lt;li&gt;Parent hash&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Processing status&lt;/li&gt;
&lt;li&gt;Confirmation or finality state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose the indexer stored:&lt;/p&gt;

&lt;p&gt;`Block 105&lt;br&gt;
Hash: AAA&lt;/p&gt;

&lt;p&gt;Block 106&lt;br&gt;
Hash: BBB&lt;br&gt;
Parent: AAA&lt;br&gt;
The node later reports:&lt;br&gt;
Block 105&lt;br&gt;
Hash: AAA&lt;/p&gt;

&lt;p&gt;Block 106&lt;br&gt;
Hash: CCC&lt;br&gt;
Parent: AAA`&lt;/p&gt;

&lt;p&gt;The block number is the same, but the hash has changed.&lt;br&gt;
That is a strong indication that the chain has reorganized.&lt;/p&gt;

&lt;p&gt;The indexer can then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect the block-hash mismatch.&lt;/li&gt;
&lt;li&gt;Find the latest common ancestor.&lt;/li&gt;
&lt;li&gt;Identify blocks that are no longer canonical.&lt;/li&gt;
&lt;li&gt;Roll back data generated from those blocks.&lt;/li&gt;
&lt;li&gt;Process the replacement branch.&lt;/li&gt;
&lt;li&gt;Rebuild affected derived state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Parent-hash validation is particularly important because it allows the indexer to verify whether the block it is processing actually follows the chain it previously stored.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rollbacks: Reverting Data After a Reorg
&lt;/h2&gt;

&lt;p&gt;A rollback means reversing application state that was derived from blocks that are no longer canonical.&lt;/p&gt;

&lt;p&gt;Imagine an indexer records:&lt;/p&gt;

&lt;p&gt;Alice balance = 5 ETH&lt;/p&gt;

&lt;p&gt;Deposit event:&lt;br&gt;
Alice +2 ETH&lt;/p&gt;

&lt;p&gt;Indexed balance = 7 ETH&lt;/p&gt;

&lt;p&gt;If the block containing that deposit is removed during a reorg, simply deleting the block record is not enough.&lt;/p&gt;

&lt;p&gt;The derived balance may still show 7 ETH.&lt;br&gt;
The indexer must reverse the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remove deposit
        ↓
Reverse affected event
        ↓
Restore Alice's balance
        ↓
Process replacement chain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same principle applies to token transfers, NFT ownership, contract events, and other derived data.&lt;/p&gt;

&lt;p&gt;This is why reorg-aware indexing requires a clear relationship between &lt;strong&gt;raw blockchain data and derived application state&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing an Indexer That Can Safely Roll Back
&lt;/h2&gt;

&lt;p&gt;A reliable indexer should be designed with reprocessing in mind from the beginning.&lt;/p&gt;

&lt;p&gt;Store Block Metadata&lt;/p&gt;

&lt;p&gt;A blocks table might contain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  blocks
&lt;/h2&gt;

&lt;p&gt;height&lt;br&gt;
hash&lt;br&gt;
parent_hash&lt;br&gt;
timestamp&lt;br&gt;
status&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Transactions and events can then reference the block that produced them:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  transactions
&lt;/h2&gt;

&lt;p&gt;tx_hash&lt;br&gt;
block_hash&lt;br&gt;
block_height&lt;/p&gt;
&lt;h2&gt;
  
  
  events
&lt;/h2&gt;

&lt;p&gt;event_id&lt;br&gt;
tx_hash&lt;br&gt;
block_hash&lt;br&gt;
contract_address&lt;br&gt;
event_type&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This makes it easier to identify which records belong to a discarded branch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Checkpoints
&lt;/h2&gt;

&lt;p&gt;Checkpoints allow an indexer to remember where processing safely stopped.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;strong&gt;Last checkpoint: Block 10,000&lt;br&gt;
Current block:   Block 10,250&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the indexer crashes or encounters a reorg, it does not necessarily need to rebuild the entire chain.&lt;/p&gt;

&lt;p&gt;It can roll back to an appropriate checkpoint or common ancestor and continue processing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Make Processing Idempotent
&lt;/h2&gt;

&lt;p&gt;An operation is idempotent when running it multiple times does not produce incorrect duplicate state.&lt;/p&gt;

&lt;p&gt;This matters because replacement blocks may require transactions or events to be processed again.&lt;/p&gt;

&lt;p&gt;A robust indexer should be able to safely:&lt;br&gt;
Read block&lt;/p&gt;

&lt;p&gt;→ Process events&lt;br&gt;
→ Roll back&lt;br&gt;
→ Read replacement block&lt;br&gt;
→ Process events again&lt;/p&gt;

&lt;p&gt;without creating duplicate balances or duplicate records.&lt;/p&gt;

&lt;p&gt;Database transactions can also help ensure that block processing does not leave partially updated state if something fails.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Exchanges Protect User Funds During Reorgs
&lt;/h2&gt;

&lt;p&gt;Exchanges usually apply stricter rules than a simple blockchain explorer.&lt;br&gt;
A deposit may move through several states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Detected
   ↓
Pending
   ↓
Confirmed
   ↓
Credited
   ↓
Finalized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These states allow the exchange to separate &lt;strong&gt;seeing a transaction&lt;/strong&gt; from &lt;strong&gt;trusting that&lt;/strong&gt; transaction.&lt;/p&gt;

&lt;p&gt;For example, an exchange may detect a deposit immediately but wait for a predefined number of confirmations before making the funds available to the user.&lt;/p&gt;

&lt;p&gt;If a reorg occurs while the deposit is still pending, the system can reassess the transaction without necessarily affecting the user's available balance.&lt;/p&gt;

&lt;p&gt;For higher-value transactions, exchanges may also use stricter confirmation requirements or additional reconciliation checks.&lt;/p&gt;

&lt;p&gt;The key principle is that blockchain monitoring should not directly equal balance crediting.&lt;/p&gt;

&lt;p&gt;The application needs a risk-aware state machine between the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirmation Depth vs Finality
&lt;/h2&gt;

&lt;p&gt;Confirmation depth and finality are related, but they are not the same thing.&lt;/p&gt;

&lt;p&gt;Confirmation depth refers to how many blocks have been added after a transaction.&lt;/p&gt;

&lt;p&gt;Finality refers to the point at which a transaction is considered settled under the blockchain's consensus rules and is no longer realistically expected to be reversed.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Confirmation&lt;/strong&gt;   - A transaction is included in a block&lt;br&gt;
&lt;strong&gt;Confirmation depth&lt;/strong&gt; - Additional blocks have been built afterward&lt;br&gt;
&lt;strong&gt;Rollback&lt;/strong&gt; - Application state is reversed after a chain change&lt;br&gt;
&lt;strong&gt;Finality&lt;/strong&gt; - The transaction reaches a sufficiently irreversible state&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The distinction becomes particularly important when building systems that operate across different blockchain networks.&lt;/p&gt;

&lt;p&gt;An application should understand the finality model of the chain it supports rather than applying the same confirmation policy everywhere.&lt;/p&gt;

&lt;p&gt;A network with probabilistic finality may require applications to wait for additional confirmations. A network with explicit consensus finality may provide a different signal that applications can use.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Reorg-Handling Workflow
&lt;/h2&gt;

&lt;p&gt;A production indexer or exchange can think about reorg handling as a sequence of checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New block detected
       ↓
Validate parent hash
       ↓
Compare with stored chain
       ↓
Reorg detected?
     /     \
   No       Yes
   ↓         ↓
Process    Find common ancestor
             ↓
        Roll back affected state
             ↓
      Process replacement blocks
             ↓
       Update confirmations
             ↓
          Finality
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact implementation will vary, but the architecture should always answer three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this block part of the canonical chain?&lt;/li&gt;
&lt;li&gt;What application state depends on this block?&lt;/li&gt;
&lt;li&gt;When can that state be considered final?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping these questions separate makes the system easier to reason about and test.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Test Reorg Handling
&lt;/h2&gt;

&lt;p&gt;Reorg handling should not be tested only when a production incident occurs.&lt;/p&gt;

&lt;p&gt;Development teams can simulate scenarios such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Competing blocks&lt;/li&gt;
&lt;li&gt;Removed blocks&lt;/li&gt;
&lt;li&gt;Replacement blocks&lt;/li&gt;
&lt;li&gt;Transactions disappearing from a branch&lt;/li&gt;
&lt;li&gt;Transactions appearing again in a replacement branch&lt;/li&gt;
&lt;li&gt;Duplicate events&lt;/li&gt;
&lt;li&gt;Indexer crashes during rollback&lt;/li&gt;
&lt;li&gt;Rebuilding state from checkpoints&lt;/li&gt;
&lt;li&gt;Deeper-than-expected reorganizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One useful test is deliberately simple:&lt;/p&gt;

&lt;p&gt;If the last several blocks disappeared right now, could the system reconstruct the correct state automatically?&lt;/p&gt;

&lt;p&gt;If the answer is no, the system may be relying too heavily on the assumption that blockchain history is permanently append-only.&lt;/p&gt;

&lt;p&gt;A reorg-aware architecture should be able to detect the change, identify affected state, roll it back, process the canonical replacement chain, and continue without manual database correction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.coinsclone.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt; reorganizations are a normal engineering consideration for applications that depend on recent blockchain state.&lt;/p&gt;

&lt;p&gt;For exchanges, reorgs can affect deposits, withdrawals, and internal balances. For indexers, they can invalidate blocks, transactions, events, and derived state.&lt;/p&gt;

&lt;p&gt;The solution is not simply to wait for a few confirmations. Robust systems combine &lt;strong&gt;confirmation depth, block-hash tracking, parent validation, rollback mechanisms, checkpoints, idempotent processing, and finality-aware state management&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The most important distinction is between data that has been observed and data that can safely be treated as final.&lt;/p&gt;

&lt;p&gt;A blockchain application should always be prepared for the possibility that recent state can change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the latest few blocks were reorganized today, could your exchange or indexer recover the correct state without manual intervention?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>How Blockchain Indexers Rebuild Smart Contract State from Raw Event Logs</title>
      <dc:creator>Sundarapandy</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:27:28 +0000</pubDate>
      <link>https://dev.to/sundarapandy/how-blockchain-indexers-rebuild-smart-contract-state-from-raw-event-logs-mii</link>
      <guid>https://dev.to/sundarapandy/how-blockchain-indexers-rebuild-smart-contract-state-from-raw-event-logs-mii</guid>
      <description>&lt;p&gt;A blockchain node can tell you what happened on-chain, but it does not automatically give your application the exact data model it needs.&lt;br&gt;
Suppose a DeFi protocol has processed millions of deposits, withdrawals, borrows, repayments, and liquidations. A frontend may need to answer a simple question:&lt;br&gt;
&lt;strong&gt;What is this user's current position?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reading the blockchain directly for every request would mean repeatedly scanning blocks, transactions, contract storage, and event logs. That approach quickly becomes expensive and difficult to scale.&lt;/p&gt;

&lt;p&gt;Blockchain indexers solve this problem by creating a data-processing layer between the blockchain and the application.&lt;br&gt;
They consume blocks through RPC endpoints, identify relevant contract events, decode ABI-encoded data, process events in canonical order, apply state transitions, and persist the resulting state in a queryable database.&lt;br&gt;
At a high level:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Blockchain&lt;br&gt;
    ↓&lt;br&gt;
RPC&lt;br&gt;
    ↓&lt;br&gt;
Blocks &amp;amp; Logs&lt;br&gt;
    ↓&lt;br&gt;
Event Decoder&lt;br&gt;
    ↓&lt;br&gt;
State Processor&lt;br&gt;
    ↓&lt;br&gt;
Database&lt;br&gt;
    ↓&lt;br&gt;
API&lt;br&gt;
    ↓&lt;br&gt;
Application&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The interesting engineering problem is not simply collecting events. It is reconstructing a consistent application state from an ordered stream of blockchain activity.&lt;br&gt;
What Smart Contract State Means to an Indexer&lt;br&gt;
A smart contract's state is the collection of values stored by the contract at a particular point in the blockchain.&lt;/p&gt;

&lt;p&gt;For example, a lending protocol may maintain values such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;user → collateral&lt;br&gt;
user → debt&lt;br&gt;
user → interest index&lt;br&gt;
market → liquidity&lt;br&gt;
market → borrow rate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blockchain contains the transactions and contract execution that modify these values.&lt;br&gt;
An indexer can build an application-oriented representation such as:&lt;/p&gt;
&lt;h2&gt;
  
  
  UserPosition
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;user_address&lt;br&gt;
market_address&lt;br&gt;
collateral_amount&lt;br&gt;
borrow_amount&lt;br&gt;
last_updated_block&lt;/code&gt;&lt;br&gt;
The important distinction is that events are historical facts, while indexed state is a derived representation.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Deposit(user, 5 ETH)&lt;br&gt;
Borrow(user, 2000 USDC)&lt;br&gt;
Repay(user, 500 USDC)&lt;br&gt;
can result in:&lt;br&gt;
Collateral = 5 ETH&lt;br&gt;
Debt       = 1500 USDC&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The indexer derives that representation by processing events in sequence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Blockchain Data Through RPC
&lt;/h2&gt;

&lt;p&gt;The indexing pipeline usually begins with an RPC connection to a blockchain node or provider.&lt;br&gt;
Depending on the chain and node implementation, an indexer may use JSON-RPC methods such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eth_blockNumber&lt;br&gt;
eth_getBlockByNumber&lt;br&gt;
eth_getLogs&lt;br&gt;
eth_getTransactionReceipt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example, an indexer can request logs for a contract over a block range:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq6dnpzkhjzhlk0kvuted.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq6dnpzkhjzhlk0kvuted.jpeg" alt=" " width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node returns matching logs from that range.&lt;br&gt;
A production indexer normally does not request an unlimited block range. Instead, it processes blocks in manageable chunks.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;strong&gt;100000 → 100500&lt;br&gt;
100501 → 101000&lt;br&gt;
101001 → 101500&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Identifying the Events That Matter
&lt;/h2&gt;

&lt;p&gt;An indexer rarely needs every event emitted by every contract.&lt;br&gt;
Instead, it defines the contracts and event signatures it cares about.&lt;/p&gt;

&lt;p&gt;For example, a token indexer may monitor:&lt;br&gt;
&lt;strong&gt;Transfer(address,address,uint256)&lt;br&gt;
Approval(address,address,uint256)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The event signature is represented by a topic hash.&lt;br&gt;
Conceptually:&lt;br&gt;
&lt;strong&gt;Event Definition&lt;br&gt;
      ↓&lt;br&gt;
Keccak/Event Signature&lt;br&gt;
      ↓&lt;br&gt;
Topic0&lt;br&gt;
      ↓&lt;br&gt;
Matching Blockchain Logs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an &lt;strong&gt;eth_getLogs&lt;/strong&gt; response contains a matching topic, the indexer knows that the log corresponds to an event it understands.&lt;br&gt;
This filtering is important for performance. Processing irrelevant logs at scale can create unnecessary RPC traffic, CPU work, and database writes.&lt;/p&gt;

&lt;p&gt;The exact batch size depends on the chain, RPC provider, response size, rate limits, and event density.&lt;br&gt;
This is the first major engineering consideration: the indexer must reliably move through the chain without losing its position or overwhelming the RPC layer.&lt;/p&gt;

&lt;p&gt;ABI Decoding: Turning Logs into Structured Events&lt;br&gt;
Raw logs are not usually returned as convenient application objects.&lt;/p&gt;

&lt;p&gt;A typical log contains fields such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;address&lt;br&gt;
topics[]&lt;br&gt;
data&lt;br&gt;
blockNumber&lt;br&gt;
transactionHash&lt;br&gt;
transactionIndex&lt;br&gt;
logIndex&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The indexer uses the contract ABI to understand how those values should be interpreted.&lt;br&gt;
Consider:&lt;br&gt;
&lt;code&gt;event Transfer(&lt;br&gt;
    address indexed from,&lt;br&gt;
    address indexed to,&lt;br&gt;
    uint256 value&lt;br&gt;
);&lt;/code&gt;&lt;br&gt;
The two indexed addresses are represented in topics, while value is encoded in the event data.&lt;br&gt;
A decoder transforms the raw representation into something like:&lt;br&gt;
&lt;code&gt;{&lt;br&gt;
  "event": "Transfer",&lt;br&gt;
  "from": "0xAlice",&lt;br&gt;
  "to": "0xBob",&lt;br&gt;
  "value": "50000000000000000000"&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The indexer can then convert the raw integer into the appropriate application representation, such as:&lt;/p&gt;

&lt;p&gt;50 tokens&lt;/p&gt;

&lt;p&gt;depending on the token's decimals.&lt;br&gt;
This ABI-decoding stage is critical because incorrectly interpreting event data can result in incorrect application state even when the blockchain data itself is perfectly valid.&lt;/p&gt;
&lt;h2&gt;
  
  
  Event Ordering Is Part of State Reconstruction
&lt;/h2&gt;

&lt;p&gt;An indexer cannot simply process events in the order they arrive from different workers.&lt;br&gt;
State transitions must follow blockchain ordering.&lt;/p&gt;

&lt;p&gt;A useful ordering key is:&lt;br&gt;
&lt;strong&gt;(block_number, transaction_index, log_index)&lt;/strong&gt;&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Block 500
 ├── Transaction 0
 │    ├── Log 0
 │    └── Log 1
 │
 ├── Transaction 1
 │    └── Log 0
 │
 └── Transaction 2
        └── Log 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The indexer should process these in their canonical sequence.&lt;br&gt;
This becomes particularly important when several events affect the same entity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider:&lt;br&gt;
Block 500:&lt;br&gt;
Deposit +100&lt;br&gt;
Transfer -40&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the indexer applies the transfer before the deposit, it could temporarily derive an impossible balance.&lt;br&gt;
Therefore, parallel ingestion and sequential state application often need to be separated architecturally.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reconstructing State from Events
&lt;/h2&gt;

&lt;p&gt;Once events have been decoded and ordered, the indexer applies them as state transitions.&lt;br&gt;
A simplified model is:&lt;br&gt;
&lt;strong&gt;new_state = apply(previous_state, event)&lt;/strong&gt;&lt;br&gt;
For example:&lt;br&gt;
&lt;strong&gt;Initial balance = 0&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mint 100&lt;br&gt;
→ balance = 100&lt;br&gt;
Transfer 30&lt;br&gt;
→ balance = 70&lt;br&gt;
Transfer 20&lt;br&gt;
→ balance = 50&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simple pseudocode implementation could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def process_event(event, state):
    if event.type == "Mint":
        state.balance += event.amount

    elif event.type == "Transfer":
        state.balance -= event.amount

    return state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A production indexer obviously needs considerably more logic, including address validation, token decimals, multiple accounts, transaction boundaries, database transactions, rollback handling, and failure recovery.&lt;/p&gt;

&lt;p&gt;But the underlying principle remains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Previous State + Event → New State&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Schema for Indexed State
&lt;/h2&gt;

&lt;p&gt;After processing events, the indexer needs to persist the resulting data.&lt;br&gt;
A simplified schema for a token indexer might look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frxasmcke4vd06ssrxjfn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frxasmcke4vd06ssrxjfn.jpeg" alt=" " width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keeping both raw normalized events and derived state can be useful.&lt;br&gt;
The event table provides an audit trail and makes reprocessing possible, while the derived tables provide fast application queries.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;code&gt;SELECT balance&lt;br&gt;
FROM token_balances&lt;br&gt;
WHERE wallet_address = '0x...';&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;is considerably more practical for an application than scanning historical blockchain logs for every request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Checkpoints: Knowing Where the Indexer Stopped
&lt;/h2&gt;

&lt;p&gt;A production indexer also needs a checkpoint system.&lt;br&gt;
Imagine the indexer has successfully processed block &lt;strong&gt;2,500,000&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It can store:&lt;br&gt;
&lt;code&gt;last_processed_block = 2500000&lt;br&gt;
last_processed_hash  = 0xabc...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the process crashes, it can resume from a known position instead of starting from the beginning.&lt;/p&gt;

&lt;p&gt;A checkpoint table might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE indexer_checkpoint (
    id              INT PRIMARY KEY,
    block_number    BIGINT,
    block_hash      VARCHAR(66),
    updated_at      TIMESTAMP
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checkpoint should generally be updated only after the corresponding data has been successfully persisted.&lt;/p&gt;

&lt;p&gt;Otherwise, the indexer could record a block as processed even though some of its state changes were never committed.&lt;br&gt;
This creates an important consistency rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persist the state and advance the checkpoint as one logical unit of progress.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Handling Reorganizations
&lt;/h2&gt;

&lt;p&gt;One of the most important challenges is blockchain reorganization.&lt;br&gt;
Suppose the indexer processes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Block 100&lt;br&gt;
    ↓&lt;br&gt;
Block 101A&lt;br&gt;
    ↓&lt;br&gt;
Block 102A&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It derives application state from those blocks.&lt;br&gt;
Later, the canonical chain becomes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Block 100&lt;br&gt;
    ↓&lt;br&gt;
Block 101B&lt;br&gt;
    ↓&lt;br&gt;
Block 102B&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The events from &lt;strong&gt;101A&lt;/strong&gt; and &lt;strong&gt;102A&lt;/strong&gt; may no longer belong to the canonical chain.&lt;/p&gt;

&lt;p&gt;A robust indexer needs to detect this situation.&lt;br&gt;
That is why storing the block hash alongside the checkpoint is useful.&lt;br&gt;
The indexer can compare:&lt;/p&gt;

&lt;p&gt;Stored block hash&lt;br&gt;
        vs&lt;br&gt;
Current canonical block hash&lt;/p&gt;

&lt;p&gt;If they differ, the indexer knows that its local view needs reconciliation.&lt;br&gt;
A simplified rollback process could be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Detect mismatch&lt;br&gt;
 ↓&lt;br&gt;
Find common ancestor&lt;br&gt;
 ↓&lt;br&gt;
Rollback affected indexed state&lt;br&gt;
 ↓&lt;br&gt;
Remove/reverse affected events&lt;br&gt;
 ↓&lt;br&gt;
Process canonical blocks again&lt;br&gt;
  ↓&lt;br&gt;
Update checkpoint&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some architectures avoid immediately exposing very recent blocks as final application state and instead wait for additional confirmations. Others implement explicit rollback support.&lt;/p&gt;

&lt;p&gt;The correct strategy depends on the blockchain and the application's consistency requirements.&lt;/p&gt;
&lt;h2&gt;
  
  
  Handling Missed Events and RPC Failures
&lt;/h2&gt;

&lt;p&gt;RPC infrastructure introduces another failure surface.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Indexer → RPC Provider
             ↓
         Timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the indexer simply continues without verifying the response, it could silently skip blockchain data.&lt;/p&gt;

&lt;p&gt;Production systems commonly use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retry policies&lt;/li&gt;
&lt;li&gt;Request timeouts&lt;/li&gt;
&lt;li&gt;Block-range backfilling&lt;/li&gt;
&lt;li&gt;Multiple RPC providers&lt;/li&gt;
&lt;li&gt;Rate-limit handling&lt;/li&gt;
&lt;li&gt;Failed-job queues&lt;/li&gt;
&lt;li&gt;Data consistency checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose the indexer successfully processed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;100000 → 101000&lt;br&gt;
but the RPC request for:&lt;br&gt;
101001 → 101500&lt;/code&gt;&lt;br&gt;
failed.&lt;/p&gt;

&lt;p&gt;The indexer should not advance its checkpoint to &lt;strong&gt;101500&lt;/strong&gt;.&lt;br&gt;
Instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Checkpoint: 101000&lt;br&gt;
        ↓&lt;br&gt;
Retry 101001 → 101500&lt;br&gt;
        ↓&lt;br&gt;
Process successfully&lt;br&gt;
        ↓&lt;br&gt;
Advance checkpoint&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This simple rule prevents gaps in the indexed chain history.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Simplified Indexer Loop
&lt;/h2&gt;

&lt;p&gt;The entire process can be represented with pseudocode:&lt;br&gt;
while True:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;latest_block = get_latest_block()

    while checkpoint &amp;lt; latest_block:
        from_block = checkpoint + 1
        to_block = min(from_block + BATCH_SIZE - 1, latest_block)

        logs = get_logs(from_block, to_block)

        events = decode_logs(logs)

        events.sort(
            key=lambda e: (
                e.block_number,
                e.transaction_index,
                e.log_index
            )
        )

        begin_transaction()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    for event in events:

        `apply_state_change(event)
        store_event(event)

    save_checkpoint(to_block)

    commit_transaction()

    checkpoint = to_block`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A real implementation would need additional handling for RPC failures, duplicate events, reorgs, finality, concurrent workers, database failures, and chain-specific behavior.&lt;/p&gt;

&lt;p&gt;Still, this loop captures the core indexing model:&lt;br&gt;
&lt;strong&gt;Fetch → Decode → Order → Apply → Persist → Checkpoint&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Architecture Matters for Web3 Applications
&lt;/h2&gt;

&lt;p&gt;Once the indexing layer is reliable, applications can query structured data rather than repeatedly interpreting raw blockchain activity.&lt;/p&gt;

&lt;p&gt;A DeFi frontend can request:&lt;br&gt;
&lt;strong&gt;GET /users/0x123/positions&lt;/strong&gt;&lt;br&gt;
and receive:&lt;br&gt;
&lt;code&gt;{&lt;br&gt;
  "collateral": "5 ETH",&lt;br&gt;
  "debt": "1500 USDC"&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;An NFT application can request the current owner of a token.&lt;br&gt;
A trading application can query historical trades.&lt;br&gt;
A blockchain explorer can provide transaction and contract activity through indexed tables.&lt;/p&gt;

&lt;p&gt;In each case, the application is consuming a derived data layer, while the blockchain remains the underlying source of truth.&lt;br&gt;
This separation also makes it easier to scale application APIs independently from blockchain RPC infrastructure.&lt;br&gt;
The Complete Reconstruction Pipeline&lt;br&gt;
Putting everything together:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Blockchain&lt;br&gt;
                 ↓&lt;br&gt;
                  RPC Node&lt;br&gt;
                 ↓&lt;br&gt;
             Block / Log Fetching&lt;br&gt;
                 ↓&lt;br&gt;
                Event Filtering&lt;br&gt;
                 ↓&lt;br&gt;
                 ABI Decoding&lt;br&gt;
                 ↓&lt;br&gt;
              Canonical Ordering&lt;br&gt;
                 ↓&lt;br&gt;
              State Transitions&lt;br&gt;
                 ↓&lt;br&gt;
             Database Persistence&lt;br&gt;
                 ↓&lt;br&gt;
               Checkpoint Update&lt;br&gt;
                 ↓&lt;br&gt;
                    API&lt;br&gt;
                 ↓&lt;br&gt;
                Web3 Application&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The indexer's job is therefore more sophisticated than simply copying blockchain data into a database.&lt;br&gt;
It is maintaining a continuously updated, application-oriented representation of blockchain activity while accounting for ordering, failures, missed blocks, and chain reorganizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.coinsclone.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt; indexers provide the infrastructure that turns low-level blockchain activity into data applications can actually use.&lt;/p&gt;

&lt;p&gt;The process starts with RPC calls that retrieve blocks and logs. The indexer filters relevant events, decodes ABI-encoded values, orders them according to their position in the chain, and applies each event as a state transition.&lt;/p&gt;

&lt;p&gt;The resulting state is persisted in database tables optimized for application queries. Checkpoints allow the indexer to resume safely after failures, while block hashes and rollback mechanisms help it recover from chain reorganizations.&lt;/p&gt;

&lt;p&gt;The fundamental model is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Raw Logs&lt;br&gt;
 ↓&lt;br&gt;
RPC Ingestion&lt;br&gt;
 ↓&lt;br&gt;
ABI Decoding&lt;br&gt;
 ↓&lt;br&gt;
Canonical Ordering&lt;br&gt;
 ↓&lt;br&gt;
State Reconstruction&lt;br&gt;
 ↓&lt;br&gt;
Database&lt;br&gt;
 ↓&lt;br&gt;
API&lt;br&gt;
 ↓&lt;br&gt;
Application&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For developers and CTOs building blockchain applications, the key architectural question is not simply “How do we read blockchain data?”&lt;/p&gt;

&lt;p&gt;It is:&lt;br&gt;
&lt;strong&gt;“How do we maintain a reliable, queryable representation of blockchain state as the chain continuously changes?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the core problem blockchain indexing is designed to solve.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>What Happens When an API Call Fails? A Practical Guide to Timeouts, Retries, and Circuit Breakers</title>
      <dc:creator>Sundarapandy</dc:creator>
      <pubDate>Mon, 21 Sep 2026 13:23:07 +0000</pubDate>
      <link>https://dev.to/sundarapandy/what-happens-when-an-api-call-fails-a-practical-guide-to-timeouts-retries-and-circuit-breakers-297i</link>
      <guid>https://dev.to/sundarapandy/what-happens-when-an-api-call-fails-a-practical-guide-to-timeouts-retries-and-circuit-breakers-297i</guid>
      <description>&lt;p&gt;Your app submits a request to an external API.&lt;br&gt;
Usually, everything works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Application → External API → Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But what happens when the external API becomes slow?&lt;/p&gt;

&lt;p&gt;Or stops responding completely?&lt;/p&gt;

&lt;p&gt;Your application may keep waiting, requests may start piling up, and eventually other parts of your system can become affected.&lt;br&gt;
This is why production applications need to be designed not only for successful requests, but also for failures.&lt;/p&gt;

&lt;p&gt;In this post, we'll look at three important mechanisms for handling unreliable dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Circuit breakers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also look at exponential backoff and graceful degradation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why API Failures Are Different in Production
&lt;/h2&gt;

&lt;p&gt;When developing locally, an API call often looks simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application&lt;br&gt;
    ↓&lt;br&gt;
API Request&lt;br&gt;
    ↓&lt;br&gt;
External Service&lt;br&gt;
    ↓&lt;br&gt;
Response&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In production, there are many things that can go wrong.&lt;br&gt;
An external service might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take too long to respond&lt;/li&gt;
&lt;li&gt;Return a 500 error&lt;/li&gt;
&lt;li&gt;Become temporarily unavailable&lt;/li&gt;
&lt;li&gt;Rate-limit your requests&lt;/li&gt;
&lt;li&gt;Experience network problems&lt;/li&gt;
&lt;li&gt;Return an unexpected response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, imagine an e-commerce application that calls a recommendation service:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User&lt;br&gt;
  ↓&lt;br&gt;
E-commerce API&lt;br&gt;
  ↓&lt;br&gt;
Recommendation Service&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the recommendation service becomes unavailable, should the entire application stop working?&lt;br&gt;
Usually, no.&lt;br&gt;
The application should have a strategy for handling that failure.&lt;br&gt;
That's where resilience patterns become important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeouts: Don't Wait Forever
&lt;/h2&gt;

&lt;p&gt;The first rule is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never allow an external request to wait indefinitely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose your application sends a request to a payment service:&lt;br&gt;
&lt;strong&gt;Application → Payment API&lt;/strong&gt;&lt;br&gt;
If the payment API doesn't respond, your application might continue waiting.&lt;br&gt;
If enough requests do this simultaneously, you can end up with many requests consuming connections and resources.&lt;/p&gt;

&lt;p&gt;A timeout places a limit on how long the application will wait.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application&lt;br&gt;
     ↓&lt;br&gt;
Payment API&lt;br&gt;
     ↓&lt;br&gt;
No response&lt;br&gt;
     ↓&lt;br&gt;
Timeout&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;response = requests.get(&lt;br&gt;
    "https://api.example.com/payment",&lt;br&gt;
    timeout=5 &lt;br&gt;
)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The exact timeout value depends on the operation and system requirements, but the important principle is:&lt;br&gt;
&lt;strong&gt;Every external dependency should have an intentional timeout.&lt;/strong&gt;&lt;br&gt;
A timeout doesn't fix the underlying failure.&lt;br&gt;
It prevents one slow dependency from holding your application hostage indefinitely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retries: Temporary Failures Can Recover
&lt;/h2&gt;

&lt;p&gt;Some failures are temporary.&lt;br&gt;
A network request might fail once and succeed a moment later.&lt;br&gt;
In those cases, retrying can be useful.&lt;br&gt;
A basic retry flow looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Request&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;br&gt;
   ↓&lt;br&gt;
Success&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
Attempt 1 → Failed&lt;br&gt;
Attempt 2 → Failed&lt;br&gt;
Attempt 3 → Success&lt;/p&gt;

&lt;p&gt;This can be useful for transient problems such as temporary network failures or certain server errors.&lt;/p&gt;

&lt;p&gt;However, retries should not be added blindly.&lt;br&gt;
Imagine 1,000 requests reach a service that is already struggling.&lt;br&gt;
If every request immediately retries three times, the service could suddenly receive thousands of additional requests. Instead of recovering, the situation can become worse. This is sometimes referred to as a retry storm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exponential Backoff
&lt;/h2&gt;

&lt;p&gt;One way to make retries safer is to introduce a delay between attempts.&lt;br&gt;
Instead of:&lt;br&gt;
&lt;strong&gt;Retry → Retry → Retry&lt;/strong&gt;&lt;br&gt;
you can use:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Request&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Wait&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Wait longer&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is called &lt;strong&gt;exponential backoff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A simplified example might look like:&lt;br&gt;
&lt;strong&gt;Attempt 1&lt;/strong&gt; → Failure&lt;br&gt;
Wait 1 second&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 2&lt;/strong&gt; → Failure&lt;br&gt;
Wait 2 seconds&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 3&lt;/strong&gt; → Failure&lt;br&gt;
Wait 4 seconds&lt;/p&gt;

&lt;p&gt;The delay increases after each failed attempt.&lt;br&gt;
In distributed systems, jitter is often added to these delays as well. This prevents many clients from retrying at exactly the same time.&lt;/p&gt;

&lt;p&gt;A practical retry strategy therefore usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A maximum number of retries&lt;/li&gt;
&lt;li&gt;Exponential backoff&lt;/li&gt;
&lt;li&gt;Jitter&lt;/li&gt;
&lt;li&gt;Appropriate timeout values&lt;/li&gt;
&lt;li&gt;Rules for which errors are retryable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Don't Retry Every Request
&lt;/h2&gt;

&lt;p&gt;This is an important distinction. Not every failed request should automatically be retried.&lt;br&gt;
For example, retrying a read operation may be relatively safe in many cases.&lt;br&gt;
But consider a request that creates a payment or places an order.&lt;br&gt;
If the server processed the request successfully but the response was lost, your client may not know whether the operation actually happened.&lt;br&gt;
Automatically sending the same request again could potentially create a duplicate operation.&lt;br&gt;
This is why &lt;strong&gt;idempotency&lt;/strong&gt; matters.&lt;/p&gt;

&lt;p&gt;For operations that may be retried, systems often use idempotency keys or other mechanisms to ensure that repeating the request doesn't accidentally repeat the business operation.&lt;br&gt;
The key question isn't:&lt;br&gt;
“&lt;strong&gt;Can we retry this request&lt;/strong&gt;?”&lt;br&gt;
It's:&lt;br&gt;
“&lt;strong&gt;What happens if we retry this request?&lt;/strong&gt;”&lt;/p&gt;

&lt;h2&gt;
  
  
  Circuit Breakers: Stop Calling a Failing Service
&lt;/h2&gt;

&lt;p&gt;Now consider a different situation.&lt;br&gt;
An external service isn't failing once or twice.&lt;br&gt;
It's been failing continuously for several minutes.&lt;br&gt;
Should your application keep sending requests?&lt;br&gt;
Probably not.&lt;br&gt;
This is where a &lt;strong&gt;circuit breaker&lt;/strong&gt; can help.&lt;br&gt;
A circuit breaker generally has three states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Success&lt;br&gt;
      ┌───────────┐&lt;br&gt;
      │           ↓&lt;br&gt;
   CLOSED → OPEN → HALF-OPEN&lt;br&gt;
      ↑                 │&lt;br&gt;
      └─────────────────┘&lt;br&gt;
          Recovery&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Closed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is operating normally.&lt;br&gt;
Requests are allowed through.&lt;br&gt;
Application → External Service&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dependency is consistently failing.&lt;br&gt;
The circuit opens and stops sending requests to that dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application → Circuit Breaker → Fallback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prevents your application from repeatedly calling a service that is already unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After some time, the circuit breaker allows a limited request through to check whether the dependency has recovered.&lt;/p&gt;

&lt;p&gt;If the request succeeds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open → Closed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it fails again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open → Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This gives the failing service time to recover while protecting your application from repeated failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graceful Degradation
&lt;/h2&gt;

&lt;p&gt;Sometimes the best solution isn't retrying at all.&lt;br&gt;
Instead, your application can provide a reduced version of the functionality.&lt;br&gt;
Consider an online shopping application:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Product Page&lt;br&gt;
     ↓&lt;br&gt;
Recommendation Service&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The recommendation service goes down.&lt;br&gt;
You could make the entire product page fail.&lt;br&gt;
Or you could simply hide the recommendations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Product Page&lt;br&gt;
     ↓&lt;br&gt;
Recommendation Service ❌&lt;br&gt;
     ↓&lt;br&gt;
Show Product Without Recommendations&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user can still view the product and continue shopping.&lt;br&gt;
This is called graceful degradation.&lt;br&gt;
The idea is simple:&lt;br&gt;
When one feature fails, don't necessarily let the entire system fail with it.&lt;br&gt;
Putting the Patterns Together&lt;br&gt;
These mechanisms work particularly well when combined.&lt;br&gt;
A simplified architecture could look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            ┌───────────────┐
            │ Your Service  │
            └───────┬───────┘
                    ↓
             ┌─────────────┐
             │  Timeout    │
             └──────┬──────┘
                    ↓
             ┌─────────────┐
             │    Retry    │
             └──────┬──────┘
                    ↓
          ┌──────────────────┐
          │  Circuit Breaker │
          └─────────-────────┘
                    ↓
             External Service
                    ↓
              ┌─────────┐
              │Fallback │
              └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The exact implementation depends on your architecture, but the concepts work together:&lt;br&gt;
&lt;strong&gt;Timeouts&lt;/strong&gt; prevent requests from waiting indefinitely.&lt;br&gt;
&lt;strong&gt;Retries&lt;/strong&gt; handle certain temporary failures.&lt;br&gt;
&lt;strong&gt;Backoff&lt;/strong&gt; prevents retries from overwhelming a failing service.&lt;br&gt;
&lt;strong&gt;Circuit breakers&lt;/strong&gt; interrupt repeated calls to an unhealthy Service.&lt;br&gt;
&lt;strong&gt;Fallbacks&lt;/strong&gt; allow your application to continue operating when possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should You Monitor?
&lt;/h2&gt;

&lt;p&gt;Adding resilience mechanisms isn't enough.&lt;br&gt;
You also need to know when they're being triggered.&lt;br&gt;
Useful metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request failure rate&lt;/li&gt;
&lt;li&gt;Timeout rate&lt;/li&gt;
&lt;li&gt;Retry count&lt;/li&gt;
&lt;li&gt;Retry success rate&lt;/li&gt;
&lt;li&gt;Circuit breaker state&lt;/li&gt;
&lt;li&gt;External API latency&lt;/li&gt;
&lt;li&gt;Fallback frequency&lt;/li&gt;
&lt;li&gt;Dependency availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if your retry success rate suddenly drops from 90% to 10%, that could indicate that the dependency has a larger problem.&lt;br&gt;
Without monitoring, your system may appear to be working while silently relying on retries and fallbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Checklist
&lt;/h2&gt;

&lt;p&gt;When your application depends on external services, consider the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;br&gt;
Set a reasonable timeout for every external request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry&lt;/strong&gt;&lt;br&gt;
Retry only failures that are safe and potentially temporary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backoff&lt;/strong&gt;&lt;br&gt;
Avoid immediate repeated retries. Use exponential backoff and, where appropriate, jitter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency&lt;/strong&gt;&lt;br&gt;
Make sure retrying an operation doesn't accidentally duplicate a business action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circuit Breaker&lt;/strong&gt;&lt;br&gt;
Stop repeatedly calling a dependency that is consistently failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fallback&lt;/strong&gt;&lt;br&gt;
Decide whether your application can continue with reduced functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;br&gt;
Track failures, retries, latency, and dependency health.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;External services will eventually become slow, unavailable, or unpredictable.&lt;br&gt;
The objective is not to create a system that will never fail.&lt;br&gt;
That's unrealistic.&lt;br&gt;
The goal is to build a system that fails predictably, limits the impact of failures, and recovers when dependencies become healthy again.&lt;br&gt;
Timeouts, retries, exponential backoff, circuit breakers, and graceful degradation are some of the building blocks that make that possible.&lt;/p&gt;

&lt;p&gt;The next time you add an external API to your application, don't ask only:&lt;br&gt;
&lt;strong&gt;“What happens when the API works?”&lt;/strong&gt;&lt;br&gt;
Also ask:&lt;br&gt;
&lt;strong&gt;“What happens when it doesn't?”&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
