<?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>Forem: Aditya</title>
    <description>The latest articles on Forem by Aditya (@aaditya_efec6eedf3319cec3).</description>
    <link>https://forem.com/aaditya_efec6eedf3319cec3</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%2F3695380%2F5ead04af-b29c-4c72-88e5-c91a384096cd.png</url>
      <title>Forem: Aditya</title>
      <link>https://forem.com/aaditya_efec6eedf3319cec3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aaditya_efec6eedf3319cec3"/>
    <language>en</language>
    <item>
      <title>Don't Trust the Client: How I Hacked My Own Coupon System</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Tue, 06 Jan 2026 03:59:58 +0000</pubDate>
      <link>https://forem.com/aaditya_efec6eedf3319cec3/dont-trust-the-client-how-i-hacked-my-own-coupon-system-4ll</link>
      <guid>https://forem.com/aaditya_efec6eedf3319cec3/dont-trust-the-client-how-i-hacked-my-own-coupon-system-4ll</guid>
      <description>&lt;p&gt;I recently finished building a promotional engine for an e-commerce platform. The logic was simple: A "Flash Sale" coupon that expires in 2 hours.&lt;/p&gt;

&lt;p&gt;I wrote the logic, tested it, and deployed it. It worked perfectly. Then, I decided to switch hats. I stopped being the developer and started being the bad actor.&lt;/p&gt;

&lt;p&gt;The Hack I opened the application, applied the coupon, and saw the timer counting down. Then, I did something incredibly low-tech:&lt;/p&gt;

&lt;p&gt;I went to my laptop's system settings and changed the date back by one day.&lt;/p&gt;

&lt;p&gt;The Result? The coupon, which should have expired, became valid again. The "Expired" button turned back into "Apply." My backend accepted the order.&lt;/p&gt;

&lt;p&gt;I had accidentally committed the cardinal sin of web development: I trusted the client.&lt;/p&gt;

&lt;p&gt;The Technical Deep Dive In my initial code, I was validating the expiry logic partially on the client-side to save server resources (a "premature optimization"). I was comparing the coupon's expiry timestamp against new Date()—which pulls from the user's operating system, not the server.&lt;/p&gt;

&lt;p&gt;If the user controls the OS, the user controls the time.&lt;/p&gt;

&lt;p&gt;The Fix (Source of Truth) The solution wasn't just to move the check to the backend; it was to ensure the backend wasn't relying on its local clock either (which can drift in distributed systems).&lt;/p&gt;

&lt;p&gt;I refactored the architecture to use the database (in this case, MongoDB) as the single source of temporal truth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JavaScript

// The Bad Way (Vulnerable)
if (Date.now() &amp;lt; coupon.expiry) { ... }

// The Better Way (Database specific)
const validCoupon = await Coupon.findOne({
    code: "FLASH50",
    expiryDate: { $gt: new Date() } // Server time is strictly enforced
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Lesson "Frontend validation is for User Experience. Backend validation is for Security."&lt;/p&gt;

&lt;p&gt;It’s a cliché for a reason. As engineers, we often focus on the happy path—when the user does what they are supposed to do. But the real engineering happens when we account for the user who is trying to trick the system.&lt;/p&gt;

&lt;p&gt;Always assume your API is being called by a script, not a browser.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond if (stock &gt; 0): Handling Race Conditions in High-Traffic E-Commerce Systems</title>
      <dc:creator>Aditya</dc:creator>
      <pubDate>Tue, 06 Jan 2026 03:47:02 +0000</pubDate>
      <link>https://forem.com/aaditya_efec6eedf3319cec3/beyond-if-stock-0-handling-race-conditions-in-high-traffic-e-commerce-systems-481</link>
      <guid>https://forem.com/aaditya_efec6eedf3319cec3/beyond-if-stock-0-handling-race-conditions-in-high-traffic-e-commerce-systems-481</guid>
      <description>&lt;p&gt;Why basic logic fails at scale and how to implement Optimistic Concurrency Control in Node.js &amp;amp; MongoDB.&lt;/p&gt;

&lt;p&gt;In the early stages of building an e-commerce application, inventory management seems straightforward. Logic dictates: check if the item is in stock, and if it is, process the order and decrement the count.&lt;/p&gt;

&lt;p&gt;It usually looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The Naive Approach
const product = await Product.findById(productId);

if (product.stock &amp;gt; 0) {
  product.stock -= 1;
  await product.save();
  // Process payment...
} else {
  throw new Error('Out of stock');
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a development environment, this works perfectly. In a production environment with concurrent users, this is a disaster waiting to happen.&lt;/p&gt;

&lt;p&gt;The Problem: The Race Condition Imagine two users, Alice and Bob, try to buy the last iPhone at the exact same millisecond.&lt;/p&gt;

&lt;p&gt;Thread A (Alice) reads the DB: Stock = 1.&lt;/p&gt;

&lt;p&gt;Thread B (Bob) reads the DB: Stock = 1.&lt;/p&gt;

&lt;p&gt;Thread A passes the if check and decrements stock to 0.&lt;/p&gt;

&lt;p&gt;Thread B also passes the if check (because it read the stale data before Thread A finished) and decrements stock to -1.&lt;/p&gt;

&lt;p&gt;We have now sold two phones when we only had one. This leads to customer support nightmares and reconciliation issues.&lt;/p&gt;

&lt;p&gt;The Experienced Solution: Atomic Operations To solve this without locking the entire database (which kills performance), we leverage the atomicity of the database engine itself. Instead of a "Read-Modify-Write" pattern in the application layer, we push the logic to the database layer.&lt;/p&gt;

&lt;p&gt;In MongoDB (Mongoose), we can use findOneAndUpdate with a query filter that acts as our guard:&lt;/p&gt;

&lt;p&gt;JavaScript&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The Scalable Approach
const updatedProduct = await Product.findOneAndUpdate(
  { 
    _id: productId, 
    stock: { $gt: 0 } // The Atomic Guard
  },
  { 
    $inc: { stock: -1 } // The Atomic Update
  }, 
  { new: true }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if (!updatedProduct) {&lt;br&gt;
  throw new Error('Out of stock or race condition detected');&lt;br&gt;
}&lt;br&gt;
Why this works: The database process is linear. Even if two requests hit the database simultaneously, MongoDB will process one first. The first request matches the document because stock &amp;gt; 0. It decrements the stock. The second request immediately fails to match because stock is now 0.&lt;/p&gt;

&lt;p&gt;Conclusion Building for scale requires moving beyond happy-path logic. By utilizing atomic database operations, we ensure data consistency without the overhead of pessimistic locking or queues, ensuring our application remains robust even during flash sales.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>mongodb</category>
      <category>node</category>
    </item>
  </channel>
</rss>
