<?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: Debjit Dey</title>
    <description>The latest articles on Forem by Debjit Dey (@debjit450).</description>
    <link>https://forem.com/debjit450</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%2F3579460%2Fbd6fc51d-2749-4bb9-9ba8-46d8bce33f74.jpg</url>
      <title>Forem: Debjit Dey</title>
      <link>https://forem.com/debjit450</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/debjit450"/>
    <language>en</language>
    <item>
      <title>Adaptive Rate Limiting with Redis and Lua</title>
      <dc:creator>Debjit Dey</dc:creator>
      <pubDate>Tue, 28 Apr 2026 17:36:33 +0000</pubDate>
      <link>https://forem.com/debjit450/adaptive-rate-limiting-with-redis-and-lua-32fe</link>
      <guid>https://forem.com/debjit450/adaptive-rate-limiting-with-redis-and-lua-32fe</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Ft88bw7nx2suua8v7j1d4.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.amazonaws.com%2Fuploads%2Farticles%2Ft88bw7nx2suua8v7j1d4.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Rate Limiting Correct Under Concurrency
&lt;/h2&gt;

&lt;p&gt;Most rate limiting tutorials stop at the single-instance case.&lt;br&gt;
That’s fine for learning, but it breaks quickly in production.&lt;/p&gt;

&lt;p&gt;Once you have multiple instances and real traffic patterns, the problem changes.&lt;br&gt;
It’s no longer just about picking an algorithm — it’s about &lt;strong&gt;correctness under concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article walks through what actually goes wrong and how to fix it.&lt;/p&gt;


&lt;h2&gt;
  
  
  The In-Memory Trap
&lt;/h2&gt;

&lt;p&gt;The first implementation most people write looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep a counter in memory&lt;/li&gt;
&lt;li&gt;increment on each request&lt;/li&gt;
&lt;li&gt;reject when the limit is reached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works perfectly in a single instance.&lt;/p&gt;

&lt;p&gt;Now deploy two instances.&lt;/p&gt;

&lt;p&gt;Each instance has its own counter. A client can exceed your intended limit just by hitting different instances.&lt;/p&gt;

&lt;p&gt;At that point, you don’t have a rate limiter anymore.&lt;br&gt;
You have a suggestion.&lt;/p&gt;


&lt;h2&gt;
  
  
  Redis Fixes Distribution, Not Concurrency
&lt;/h2&gt;

&lt;p&gt;The next step is moving state to Redis.&lt;/p&gt;

&lt;p&gt;Now all instances share the same counters. Good.&lt;/p&gt;

&lt;p&gt;A typical implementation looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read current count from Redis&lt;/li&gt;
&lt;li&gt;Check against limit&lt;/li&gt;
&lt;li&gt;Increment and write back&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This seems correct, but it isn’t.&lt;/p&gt;

&lt;p&gt;These are separate operations. Under concurrent load:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two requests read the same value&lt;/li&gt;
&lt;li&gt;both pass the check&lt;/li&gt;
&lt;li&gt;both increment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your limit is no longer strict. It’s approximate.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Real Problem: Atomicity
&lt;/h2&gt;

&lt;p&gt;The issue isn’t Redis.&lt;/p&gt;

&lt;p&gt;It’s that the decision is split across multiple steps.&lt;/p&gt;

&lt;p&gt;What you need is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a single, atomic operation that reads state, applies logic, and updates state&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Fix: Lua Scripts in Redis
&lt;/h2&gt;

&lt;p&gt;Redis supports Lua scripts that execute atomically.&lt;/p&gt;

&lt;p&gt;No other command runs between the start and end of the script.&lt;/p&gt;

&lt;p&gt;Instead of multiple round trips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read state&lt;/li&gt;
&lt;li&gt;apply limiter logic&lt;/li&gt;
&lt;li&gt;update state&lt;/li&gt;
&lt;li&gt;return decision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do everything inside one script.&lt;/p&gt;

&lt;p&gt;Example (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"INCR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"EXPIRE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no race conditions&lt;/li&gt;
&lt;li&gt;consistent decisions across instances&lt;/li&gt;
&lt;li&gt;predictable behavior under load&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where Algorithms Fit In
&lt;/h2&gt;

&lt;p&gt;At this point, you can plug in different strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token Bucket → allows bursts, smooths over time&lt;/li&gt;
&lt;li&gt;Sliding Window → more accurate but heavier&lt;/li&gt;
&lt;li&gt;Leaky Bucket → enforces steady flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the key point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The algorithm matters less than where the decision happens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your logic isn’t atomic, the algorithm won’t save you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static Limits Miss Real Traffic Behavior
&lt;/h2&gt;

&lt;p&gt;Even with correct enforcement, static limits are too rigid.&lt;/p&gt;

&lt;p&gt;Real traffic looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;legitimate bursts&lt;/li&gt;
&lt;li&gt;scrapers probing endpoints&lt;/li&gt;
&lt;li&gt;repeated identical requests&lt;/li&gt;
&lt;li&gt;denial loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fixed limit treats all of these the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding a Behavior Layer
&lt;/h2&gt;

&lt;p&gt;A simple improvement is to track short-term behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request volume over a short window (burst detection)&lt;/li&gt;
&lt;li&gt;repeated request fingerprints&lt;/li&gt;
&lt;li&gt;number of unique routes hit (scan detection)&lt;/li&gt;
&lt;li&gt;repeated denials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This produces a basic risk score.&lt;/p&gt;

&lt;p&gt;That score maps to tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;normal&lt;/li&gt;
&lt;li&gt;elevated&lt;/li&gt;
&lt;li&gt;suspicious&lt;/li&gt;
&lt;li&gt;blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is separation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limiter&lt;/strong&gt; → enforces limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy&lt;/strong&gt; → decides how strict to be&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the system easier to reason about and tune.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tradeoffs
&lt;/h2&gt;

&lt;p&gt;This approach is not free.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lua scripts add complexity&lt;/li&gt;
&lt;li&gt;debugging moves closer to Redis&lt;/li&gt;
&lt;li&gt;Redis becomes a critical dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for systems that need consistency under concurrency, the tradeoff is worth it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;The biggest lesson is not about token buckets or sliding windows.&lt;/p&gt;

&lt;p&gt;It’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Correctness in rate limiting comes from atomic decision-making.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a single source of truth&lt;/li&gt;
&lt;li&gt;atomic execution&lt;/li&gt;
&lt;li&gt;consistent state across instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the rest becomes much easier.&lt;/p&gt;




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

&lt;p&gt;I built this approach into a small system to explore the problem end-to-end.&lt;/p&gt;

&lt;p&gt;If you’re interested in seeing a full implementation (TypeScript + Redis + Lua), you can check it out here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/debjit450/arce" rel="noopener noreferrer"&gt;https://github.com/debjit450/arce&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you’ve dealt with this problem in production, I’d be interested to hear how you approached it.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>StableJSON: A Practical Workspace for Serious JSON Work</title>
      <dc:creator>Debjit Dey</dc:creator>
      <pubDate>Wed, 31 Dec 2025 06:14:37 +0000</pubDate>
      <link>https://forem.com/debjit450/stablejson-a-practical-workspace-for-serious-json-work-2cal</link>
      <guid>https://forem.com/debjit450/stablejson-a-practical-workspace-for-serious-json-work-2cal</guid>
      <description>&lt;p&gt;JSON is everywhere — APIs, configs, logs, exports, payloads, diffs.&lt;br&gt;
If you build software, you already spend a non-trivial part of your life working with it.&lt;/p&gt;

&lt;p&gt;Most JSON tools fall into two camps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too basic&lt;/strong&gt; — format and validate, then you’re on your own&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overengineered&lt;/strong&gt; — cluttered UIs, half-useful features, slow workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;StableJSON sits in between.&lt;br&gt;
It’s a focused workspace for &lt;em&gt;actually working with JSON&lt;/em&gt;, not just prettifying it.&lt;/p&gt;

&lt;p&gt;Live app: &lt;a href="https://stablejson.vercel.app" rel="noopener noreferrer"&gt;https://stablejson.vercel.app&lt;/a&gt;&lt;br&gt;
Repo: &lt;a href="https://github.com/debjit450/stablejson" rel="noopener noreferrer"&gt;https://github.com/debjit450/stablejson&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Formatting Isn’t the Hard Part
&lt;/h2&gt;

&lt;p&gt;Pretty-printing JSON is solved. Every editor can do that.&lt;/p&gt;

&lt;p&gt;The real problems show up when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare large API responses&lt;/li&gt;
&lt;li&gt;Debug payload mismatches&lt;/li&gt;
&lt;li&gt;Clean data before storage or hashing&lt;/li&gt;
&lt;li&gt;Generate types from unknown or evolving structures&lt;/li&gt;
&lt;li&gt;Ensure identical data always produces identical output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where things usually break down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key order changes&lt;/li&gt;
&lt;li&gt;Nulls and empty values add noise&lt;/li&gt;
&lt;li&gt;Diffs become unreadable&lt;/li&gt;
&lt;li&gt;Hashes change unexpectedly&lt;/li&gt;
&lt;li&gt;Generated types drift from reality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;StableJSON is built specifically around these failure points.&lt;/p&gt;




&lt;h2&gt;
  
  
  What StableJSON Actually Gives You
&lt;/h2&gt;

&lt;p&gt;StableJSON is a browser-based JSON workspace with tools that cover the &lt;em&gt;entire&lt;/em&gt; JSON workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate &amp;amp; format&lt;/strong&gt; JSON with proper indentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean null, undefined, and empty values&lt;/strong&gt; in one step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side-by-side JSON diff&lt;/strong&gt; with clear highlighted changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path inspection &amp;amp; extraction&lt;/strong&gt; for deeply nested data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic TypeScript interface and Zod schema generation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical JSON output&lt;/strong&gt; for consistent hashing and comparison&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSONPath queries&lt;/strong&gt; to slice complex objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure analysis&lt;/strong&gt; (depth, size, data types)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transform, flatten, and reshape&lt;/strong&gt; JSON safely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom validation rules&lt;/strong&gt; when default checks aren’t enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s not a collection of random utilities — it’s one coherent workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Canonical JSON Is the Quiet Power Feature
&lt;/h2&gt;

&lt;p&gt;One of the most important features in StableJSON is canonical output.&lt;/p&gt;

&lt;p&gt;When JSON is deterministic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hashes stop changing randomly&lt;/li&gt;
&lt;li&gt;Cache keys become reliable&lt;/li&gt;
&lt;li&gt;Diffs stay readable&lt;/li&gt;
&lt;li&gt;CI checks stop failing for no apparent reason&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve ever had two JSON objects that &lt;em&gt;look&lt;/em&gt; identical but serialize differently, you already know why this matters.&lt;/p&gt;

&lt;p&gt;Canonical output isn’t flashy — but it prevents entire classes of bugs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who This Is Built For
&lt;/h2&gt;

&lt;p&gt;StableJSON is for developers who:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug APIs regularly&lt;/li&gt;
&lt;li&gt;Care about correctness, not just visuals&lt;/li&gt;
&lt;li&gt;Work with large or dynamic JSON structures&lt;/li&gt;
&lt;li&gt;Need reliable diffs, hashes, and generated types&lt;/li&gt;
&lt;li&gt;Want one tool instead of five scattered ones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If JSON is part of your daily workflow, this tool earns its keep very quickly.&lt;/p&gt;




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

&lt;p&gt;Most JSON tools stop at “looks nice”.&lt;/p&gt;

&lt;p&gt;StableJSON goes further — it helps you &lt;strong&gt;understand&lt;/strong&gt;, &lt;strong&gt;compare&lt;/strong&gt;, &lt;strong&gt;clean&lt;/strong&gt;, and &lt;strong&gt;stabilize&lt;/strong&gt; your data.&lt;/p&gt;

&lt;p&gt;When you need JSON to be &lt;em&gt;right&lt;/em&gt;, not just readable, this is the tool you reach for.&lt;/p&gt;

&lt;p&gt;Try it: &lt;a href="https://stablejson.vercel.app" rel="noopener noreferrer"&gt;https://stablejson.vercel.app&lt;/a&gt;&lt;br&gt;
Explore the code: &lt;a href="https://github.com/debjit450/stablejson" rel="noopener noreferrer"&gt;https://github.com/debjit450/stablejson&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>devtool</category>
      <category>json</category>
    </item>
  </channel>
</rss>
