<?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: Mitesh Vasoya</title>
    <description>The latest articles on DEV Community by Mitesh Vasoya (@miteshvasoya).</description>
    <link>https://dev.to/miteshvasoya</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3911754%2F3e6f30b1-ea8a-4d63-982d-7f9ed4316e4c.jpg</url>
      <title>DEV Community: Mitesh Vasoya</title>
      <link>https://dev.to/miteshvasoya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/miteshvasoya"/>
    <language>en</language>
    <item>
      <title>From a For-Loop to a Fault-Tolerant Payout System (₹70L/month, 0 Duplicate Payments)</title>
      <dc:creator>Mitesh Vasoya</dc:creator>
      <pubDate>Mon, 04 May 2026 10:35:56 +0000</pubDate>
      <link>https://dev.to/miteshvasoya/from-a-for-loop-to-a-fault-tolerant-payout-system-70lmonth-0-duplicate-payments-4cfp</link>
      <guid>https://dev.to/miteshvasoya/from-a-for-loop-to-a-fault-tolerant-payout-system-70lmonth-0-duplicate-payments-4cfp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How we moved from a fragile loop-based payout system to a reliable, idempotent, and traceable architecture.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Paying Money Is Easy. Paying It Correctly Is Not.
&lt;/h2&gt;

&lt;p&gt;On paper, payouts sound simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer places an order&lt;/li&gt;
&lt;li&gt;Platform collects payment&lt;/li&gt;
&lt;li&gt;Platform pays the seller&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;Until you try to do it &lt;strong&gt;at scale&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Problem Behind "Simple" Payouts
&lt;/h2&gt;

&lt;p&gt;In any marketplace or fintech system, money flows across multiple parties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sellers / vendors&lt;/li&gt;
&lt;li&gt;Delivery partners&lt;/li&gt;
&lt;li&gt;Platform fees&lt;/li&gt;
&lt;li&gt;Discounts, vouchers, wallet adjustments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you're not just "sending money" — you're managing a &lt;strong&gt;financial system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ No duplicate payouts&lt;/li&gt;
&lt;li&gt;✅ No missing payouts&lt;/li&gt;
&lt;li&gt;✅ Full auditability&lt;/li&gt;
&lt;li&gt;✅ Strong failure recovery&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Scale Changes Everything
&lt;/h2&gt;

&lt;p&gt;What starts small:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;₹5L/month&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quickly becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;₹70L+/month · Hundreds of payouts per cycle · Multiple failure points&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this stage, &lt;strong&gt;mistakes are expensive&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  I Started with a Simple For-Loop
&lt;/h2&gt;

&lt;p&gt;Like most systems, I started simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initiatePayouts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entityIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entityIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;insertPaymentRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// DB write&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callBankAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// Transfer call&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updatePaymentStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// DB write&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked — until it didn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Broke
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;❌ No Visibility&lt;/strong&gt;&lt;br&gt;
We couldn't tell what succeeded vs failed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Duplicate Payouts&lt;/strong&gt;&lt;br&gt;
Retries caused double payments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Data Inconsistencies&lt;/strong&gt;&lt;br&gt;
No transactional guarantees → manual fixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ No Crash Recovery&lt;/strong&gt;&lt;br&gt;
Server restart = lost progress.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Core Problem
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A for-loop has no memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What it already processed&lt;/li&gt;
&lt;li&gt;What failed&lt;/li&gt;
&lt;li&gt;Where to resume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For financial systems, that's a &lt;strong&gt;deal-breaker&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Rethinking Payouts as a System
&lt;/h2&gt;

&lt;p&gt;Redesigned payouts as a &lt;strong&gt;lifecycle with checkpoints&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1] Pre-Payout Reconciliation
[2] Request Creation  (Maker)
[3] Approval          (Checker)
[4] Execution Pipeline
[5] Status Tracking
[6] Post-Payout Reconciliation
[7] Exception Handling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1. Pre-Payout Reconciliation — Trust Nothing
&lt;/h2&gt;

&lt;p&gt;Before moving money, everything is validated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ledger entries exist&lt;/li&gt;
&lt;li&gt;Discounts &amp;amp; adjustments match&lt;/li&gt;
&lt;li&gt;Wallet usage is correct&lt;/li&gt;
&lt;li&gt;Final payout amount is accurate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mismatch → block payout.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fixing a wrong payout is harder than delaying a correct one.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Ledger — Foundation of Everything
&lt;/h2&gt;

&lt;p&gt;Implemented a &lt;strong&gt;double-entry ledger system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each financial event creates two entries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entry&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liability created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liability settled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Outstanding Payable = Total Debits - Total Credits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Guarantees:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No debit → no payout&lt;/li&gt;
&lt;li&gt;No overpayment&lt;/li&gt;
&lt;li&gt;No duplicate payouts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2–3. Maker–Checker Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One person  →  Creates payout batch
Another     →  Approves it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prevents costly human errors &lt;strong&gt;before&lt;/strong&gt; execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Execution Pipeline — The Real Shift
&lt;/h2&gt;

&lt;p&gt;Before execution, we validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No pending transactions&lt;/li&gt;
&lt;li&gt;No ongoing transfers&lt;/li&gt;
&lt;li&gt;Valid bank details&lt;/li&gt;
&lt;li&gt;Ledger consistency&lt;/li&gt;
&lt;li&gt;No previous payouts for this cycle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Moving to a Queue (Game Changer)
&lt;/h3&gt;

&lt;p&gt;I replaced the for-loop with a &lt;strong&gt;queue-based system&lt;/strong&gt; (Redis + BullMQ).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Insert payment record → pending
          ↓
    Push job to queue
          ↓
    Worker picks job
          ↓
  Re-check status (idempotency)
          ↓
    Execute transfer
          ↓
      Update status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What This Solved
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;For-Loop&lt;/th&gt;
&lt;th&gt;Queue System&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Visibility&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Full tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicates&lt;/td&gt;
&lt;td&gt;❌ High risk&lt;/td&gt;
&lt;td&gt;✅ Idempotent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB Integrity&lt;/td&gt;
&lt;td&gt;❌ Weak&lt;/td&gt;
&lt;td&gt;✅ Transaction-safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crash Recovery&lt;/td&gt;
&lt;td&gt;❌ Lost jobs&lt;/td&gt;
&lt;td&gt;✅ Persistent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Retries &amp;amp; Failure Handling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automatic retries with exponential backoff&lt;/li&gt;
&lt;li&gt;Permanent failures → alerts + support tickets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No silent failures&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Status Tracking — No Unknown States
&lt;/h2&gt;

&lt;p&gt;We track every payout using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks&lt;/strong&gt; — real-time bank updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polling fallback&lt;/strong&gt; — for missed webhook events&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;No payout is ever left in an unknown state.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Post-Payout Reconciliation
&lt;/h2&gt;

&lt;p&gt;We verify across three sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internal system  ←→  Bank records  ←→  Ledger entries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System Initiated = Bank Debited = Ledger Credited
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mismatch → alert + investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily Audit
&lt;/h3&gt;

&lt;p&gt;Generate daily reports covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total initiated&lt;/li&gt;
&lt;li&gt;Total debited&lt;/li&gt;
&lt;li&gt;Total credited&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues are caught &lt;strong&gt;before&lt;/strong&gt; the next payout cycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Exception Handling
&lt;/h2&gt;

&lt;p&gt;Some cases require manual intervention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalid bank details&lt;/li&gt;
&lt;li&gt;External gateway failures&lt;/li&gt;
&lt;li&gt;Ledger mismatches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Handled via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ticketing system&lt;/li&gt;
&lt;li&gt;Auto-retry after resolution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Design Trade-offs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Safety vs Speed&lt;/td&gt;
&lt;td&gt;Manual approval checks&lt;/td&gt;
&lt;td&gt;Slower to execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simplicity vs Reliability&lt;/td&gt;
&lt;td&gt;Queue-based system&lt;/td&gt;
&lt;td&gt;More infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automation vs Control&lt;/td&gt;
&lt;td&gt;Human approvals&lt;/td&gt;
&lt;td&gt;Less fully automated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build the ledger first&lt;/strong&gt; — everything else depends on it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconciliation is not optional&lt;/strong&gt; — it's the safety net&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For-loops fail at scale&lt;/strong&gt; — queues are the answer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability is critical&lt;/strong&gt; — if you can't see it, you can't fix it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human oversight still matters&lt;/strong&gt; — especially at approval gates&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;We didn't start with a complex system.&lt;/p&gt;

&lt;p&gt;We started with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A loop → Failures → Iteration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we scaled, the system evolved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution   →  Validation
Functions   →  Systems
Assumptions →  Guarantees
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today, it's not just about moving money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's about proving that every transaction is correct.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mitesh Vasoya&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Backend Engineer · Fintech Systems&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
