<?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: Mindinu Ariyawansha</title>
    <description>The latest articles on DEV Community by Mindinu Ariyawansha (@mindinu).</description>
    <link>https://dev.to/mindinu</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%2F3755924%2F2fb5d34f-cd8c-4042-afeb-0a8775a5ce2e.jpg</url>
      <title>DEV Community: Mindinu Ariyawansha</title>
      <link>https://dev.to/mindinu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mindinu"/>
    <language>en</language>
    <item>
      <title>Safely Running AI-Generated Code in Node.js: `vm` vs `worker_threads` vs V8 Isolates</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Wed, 07 Oct 2026 15:57:02 +0000</pubDate>
      <link>https://dev.to/mindinu/safely-running-ai-generated-code-in-nodejs-vm-vs-workerthreads-vs-v8-isolates-2im9</link>
      <guid>https://dev.to/mindinu/safely-running-ai-generated-code-in-nodejs-vm-vs-workerthreads-vs-v8-isolates-2im9</guid>
      <description>&lt;p&gt;With the rise of autonomous AI agents and code-interpreting LLM features, Node.js backends are increasingly required to execute code generated on the fly. &lt;/p&gt;

&lt;p&gt;However, running arbitrary user or AI code inside a Node.js process is dangerous. A single malicious or hallucinated script can read environment variables (&lt;code&gt;process.env&lt;/code&gt;), spawn OS processes (&lt;code&gt;child_process&lt;/code&gt;), freeze the event loop with infinite loops, or crash your entire server by exhausting heap memory.&lt;/p&gt;

&lt;p&gt;In this post, we’ll analyze the three primary methods for executing isolated code in Node.js, why built-in modules fail at security, and how to implement true V8 heap sandboxing.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The &lt;code&gt;vm&lt;/code&gt; Module: Great for Context, Terrible for Security
&lt;/h2&gt;

&lt;p&gt;Node.js ships with a built-in &lt;code&gt;vm&lt;/code&gt; module that allows compiling and running code within a synthetic context.&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;import&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:vm&lt;/span&gt;&lt;span class="dl"&gt;'&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&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="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Looks sandboxed, but IS NOT SECURE!&lt;/span&gt;
&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x += 40;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Security Trap
&lt;/h3&gt;

&lt;p&gt;The Node.js documentation explicitly states: &lt;strong&gt;"The &lt;code&gt;vm&lt;/code&gt; module is not a security mechanism. Do not use it to run untrusted code."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because the context objects inside &lt;code&gt;vm&lt;/code&gt; still inherit from the host process's standard prototypes, untrusted code can escape the sandbox via constructor chain navigation:&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="c1"&gt;// Exploiting Node.js vm to execute host OS commands&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami');
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Executed on your host machine!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. &lt;code&gt;worker_threads&lt;/code&gt;: Good for Offloading, Weak for Isolation
&lt;/h2&gt;

&lt;p&gt;Node.js &lt;code&gt;worker_threads&lt;/code&gt; move execution off the main event loop onto a separate thread.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&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;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./worker-script.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;workerData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;console.log("Hello from worker");&lt;/span&gt;&lt;span class="dl"&gt;'&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;h3&gt;
  
  
  Pros &amp;amp; Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Non-blocking. If the guest script runs an infinite loop, the main event loop remains responsive. You can also destroy the thread via &lt;code&gt;worker.terminate()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; High memory footprint (each worker instantiates an entire Node.js runtime state). More importantly, workers still share access to Node's internal APIs (&lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;net&lt;/code&gt;, &lt;code&gt;process&lt;/code&gt;), meaning malicious code can still read disk files or make outgoing requests unless restricted by external containers like Docker.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. V8 Isolates (&lt;code&gt;isolated-vm&lt;/code&gt;): True Heap &amp;amp; Execution Sandboxing
&lt;/h2&gt;

&lt;p&gt;To safely execute untrusted code without running heavy Docker containers, you need &lt;strong&gt;V8 Isolates&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;An isolate is a distinct instance of the V8 engine with its own heap, garbage collector, and execution state. V8 isolates allow running multiple execution contexts inside a single Node.js process with zero cross-leakage and strict physical memory ceilings.&lt;/p&gt;

&lt;p&gt;Using libraries like &lt;code&gt;isolated-vm&lt;/code&gt;, you can instantiate pure V8 isolates directly in Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ivm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isolated-vm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runUntrustedCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Create a new V8 isolate with a hard 64MB memory cap&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isolate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ivm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Isolate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;memoryLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Create an execution context inside the isolate&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Set standard global primitives&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derefInto&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Compile and run the code with a strict 1-second timeout&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compileScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Execution terminated or timed out:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 5. Clean up V8 heap resources immediately&lt;/span&gt;
    &lt;span class="nx"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&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;h3&gt;
  
  
  Why V8 Isolates Win for AI Runtimes:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Host Leaks:&lt;/strong&gt; Prototype chains inside the isolate do not lead back to the host Node.js &lt;code&gt;process&lt;/code&gt; or &lt;code&gt;require&lt;/code&gt; functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard Memory Ceilings:&lt;/strong&gt; Exceeding &lt;code&gt;memoryLimit&lt;/code&gt; immediately terminates the isolate without crashing the parent process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Millisecond Startup:&lt;/strong&gt; Spawning a V8 isolate takes under $1\text{ms}$, compared to $100\text{ms}+$ for OS containers or heavy VMs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Technical Comparison Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;vm&lt;/code&gt; Module&lt;/th&gt;
&lt;th&gt;&lt;code&gt;worker_threads&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;V8 Isolates (&lt;code&gt;isolated-vm&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Docker Containers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;td&gt;🟢 High&lt;/td&gt;
&lt;td&gt;🟢 Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$&amp;lt; 1\text{ms}$&lt;/td&gt;
&lt;td&gt;$\sim 20\text{ms}$–$50\text{ms}$&lt;/td&gt;
&lt;td&gt;$&amp;lt; 1\text{ms}$&lt;/td&gt;
&lt;td&gt;$500\text{ms}$–$2000\text{ms}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared&lt;/td&gt;
&lt;td&gt;Separate Thread&lt;/td&gt;
&lt;td&gt;Hard Ceiling Per Isolate&lt;/td&gt;
&lt;td&gt;Full OS Container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host Process Risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Arbitrary Code Execution&lt;/td&gt;
&lt;td&gt;Shared System Resources&lt;/td&gt;
&lt;td&gt;Isolated V8 Heap&lt;/td&gt;
&lt;td&gt;Completely Sandboxed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trusted Internal Plugins&lt;/td&gt;
&lt;td&gt;CPU-bound background math&lt;/td&gt;
&lt;td&gt;Untrusted AI / User Scripts&lt;/td&gt;
&lt;td&gt;Multi-tenant untrusted apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Best Practices for AI Code Execution
&lt;/h2&gt;

&lt;p&gt;If your application evaluates LLM-generated code or executes dynamic tool calls:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never pass Node system modules into the execution scope.&lt;/strong&gt; Strip &lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;child_process&lt;/code&gt;, and &lt;code&gt;net&lt;/code&gt; entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce hard execution timeouts.&lt;/strong&gt; Always wrap code execution in a deadline timer (e.g., $1000\text{ms}$) to prevent event loop locking via &lt;code&gt;while(true)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit memory allocations.&lt;/strong&gt; Cap isolate memory at 32MB–128MB depending on the workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitize return payloads.&lt;/strong&gt; Pass return values through Data Loss Prevention (DLP) scanners or schema validators before returning them to your LLM's context window.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Avoid &lt;code&gt;node:vm&lt;/code&gt; for untrusted inputs—it is easily escaped.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;worker_threads&lt;/code&gt; for heavy concurrency, but not as a security barrier.&lt;/li&gt;
&lt;li&gt;Adopt &lt;strong&gt;V8 Isolates&lt;/strong&gt; when you need fast, local, sub-millisecond sandboxing for untrusted dynamic scripts.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Stop Trusting Autonomous AI Agents with Unchecked Tool Access: Introducing Vark</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Sun, 04 Oct 2026 07:42:50 +0000</pubDate>
      <link>https://dev.to/mindinu/stop-trusting-autonomous-ai-agents-with-unchecked-tool-access-introducing-vark-3f5h</link>
      <guid>https://dev.to/mindinu/stop-trusting-autonomous-ai-agents-with-unchecked-tool-access-introducing-vark-3f5h</guid>
      <description>&lt;p&gt;As AI agents transition from text completion interfaces to autonomous execution loops, we’re giving them unprecedented capability. Modern agent frameworks like Saturn AI, LangChain, Vercel AI SDK, and LlamaIndex now run shell commands, query production databases, and dynamically register external tools via the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Giving autonomous agents direct access to infrastructure introduces massive security risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud guardrails are too slow:&lt;/strong&gt; External API guardrail endpoints introduce $100\text{ms}$–$300\text{ms}$ of network latency per tool call, destroying the responsiveness of real-time loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data privacy risk:&lt;/strong&gt; Sending internal tool arguments, SQL query parameters, and system state to third-party guardrail APIs leaks sensitive intellectual property and credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No OS-level visibility:&lt;/strong&gt; API-level filters evaluate text prompts, but they lack visibility into V8 memory heaps, file descriptors, child process spawning, or stream contents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this, &lt;strong&gt;Vark&lt;/strong&gt; is an open-source, local-first, sub-millisecond execution firewall and runtime guardrail engine designed explicitly for autonomous AI agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Vark?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/luveo-technologies/vark" rel="noopener noreferrer"&gt;Vark&lt;/a&gt; runs &lt;strong&gt;100% local-first&lt;/strong&gt; inside your application process or edge runtime (Node.js, V8 Isolates, WASM). Operating inline between your agent orchestrator and system capabilities, Vark evaluates every tool execution request through an &lt;strong&gt;8-gate inspection pipeline&lt;/strong&gt; in under &lt;strong&gt;$1\text{ms}$&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;┌─────────────────┐
│   AI Agent /    │
│ Orchestrator    │
└────────┬────────┘
         │
         ▼
┌────────────────────────────────────────────────────────────────────────┐
│                          VARK SECURITY ENGINE                          │
│                                                                        │
│  ┌────────────────────┐   ┌────────────────────┐   ┌────────────────┐ │
│  │ 1. Normalization   ├──►│ 2. Schema Check    ├──►│ 3. Breaker Gate│ │
│  └────────────────────┘   └────────────────────┘   └───────┬────────┘ │
│                                                            │          │
│  ┌────────────────────┐   ┌────────────────────┐   ┌───────▼────────┐ │
│  │ 6. Isolation       │◄──│ 5. HITL Gate       │◄──│ 4. Policy Gate │ │
│  └────────┬───────────┘   └────────────────────┘   └────────────────┘ │
│           │                                                           │
│  ┌────────▼───────────┐   ┌────────────────────┐                      │
│  │ 7. Output DLP      ├──►│ 8. Crypto Audit    │                      │
│  └────────────────────┘   └────────┬───────────┘                      │
└────────────────────────────────────┼──────────────────────────────────┘
                                     │
                                     ▼
                            ┌────────────────┐
                            │ Target System  │
                            │ (FS, API, DB)  │
                            └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Core Technical Pillars
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Security Benefit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sub-Millisecond Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monomorphic V8 shapes, zero heavy external dependencies, linear-time regex arrays&lt;/td&gt;
&lt;td&gt;Adds $&amp;lt; 1\text{ms}$ overhead, preserving real-time agent execution performance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Local-First &amp;amp; Offline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100% pure TypeScript running in-process or inside local V8 isolates&lt;/td&gt;
&lt;td&gt;Zero network egress; no source code or credentials leave your network.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deterministic Sandboxing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;isolated-vm&lt;/code&gt; memory caps, Copy-on-Write virtual filesystems (&lt;code&gt;memfs&lt;/code&gt;), AST shell parsing&lt;/td&gt;
&lt;td&gt;Physical memory isolation and disk safety preventing privilege escalation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cryptographic Auditing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Append-only HMAC-SHA256 / Ed25519 hash-chained logs&lt;/td&gt;
&lt;td&gt;Tamper-proof, audit-ready execution traces for enterprise compliance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP Supply-Chain Defense&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamic SHA-256 tool descriptor pinning &amp;amp; taint tracking&lt;/td&gt;
&lt;td&gt;Eliminates Model Context Protocol "rug pull" exploits and prompt-injection schema mutations.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The 8-Gate Security Pipeline
&lt;/h2&gt;

&lt;p&gt;Every tool request dispatched by an agent routes sequentially through Vark's 8 inspection gates. If any gate detects a threat or policy violation, the pipeline &lt;strong&gt;fails closed instantly&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input Normalization Gate:&lt;/strong&gt; Normalizes string payloads to Unicode NFKC, strips zero-width characters and bidi direction overrides, and recursively decodes Base64, Hex, URL, and HTML obfuscation layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Schema Gate:&lt;/strong&gt; Strictly validates and coerces argument types against a JSON Schema (Draft-07 subset) before tool invocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Millisecond Circuit Breaker Gate:&lt;/strong&gt; Tracks real-time error rates per tool identity, tripping instantly to halt runaway agent infinite retry loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative Policy &amp;amp; Taint Tracking Gate:&lt;/strong&gt; Evaluates fine-grained role-based policies (&lt;code&gt;.vark.yaml&lt;/code&gt;) and marks data retrieved from untrusted external sources as tainted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-In-The-Loop (HITL) Gate:&lt;/strong&gt; Intercepts high-risk operations (e.g., destructive database queries, financial transfers) and pauses execution until approved via webhook or digital signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolated Sandbox Gate:&lt;/strong&gt; Spawns dedicated V8 isolates (&lt;code&gt;isolated-vm&lt;/code&gt; / QuickJS WASM), worker threads with memory caps, or ephemeral Copy-on-Write virtual filesystems (&lt;code&gt;memfs&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output DLP &amp;amp; Stream Scanner Gate:&lt;/strong&gt; Inspects return values, binary buffers, and &lt;code&gt;ReadableStream&lt;/code&gt; instances for exposed PII, API tokens, and private keys (using Luhn validation and Shannon Entropy analysis) without blocking stream consumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cryptographic Audit Gate:&lt;/strong&gt; Signs execution payloads using HMAC-SHA256 or Ed25519 and appends the entry to an immutable hash chain.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Deep Dive: Hardening Model Context Protocol (MCP)
&lt;/h2&gt;

&lt;p&gt;As MCP adoption expands, agents dynamically pull tool definitions from remote MCP servers. This creates a dangerous attack vector: a compromised or malicious MCP server could perform a &lt;strong&gt;schema rug pull&lt;/strong&gt; by mutating tool descriptions mid-session to trick an LLM into running unintended commands.&lt;/p&gt;

&lt;p&gt;Vark stops this via &lt;strong&gt;Dynamic SHA-256 Descriptor Pinning&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upon tool registration, Vark generates a cryptographic hash digest of advertised MCP tool schemas, names, and description strings.&lt;/li&gt;
&lt;li&gt;If a remote MCP server attempts to alter schema definitions mid-session, Vark detects the modification and immediately blocks execution.&lt;/li&gt;
&lt;li&gt;Context retrieved via external MCP servers is automatically flagged as &lt;strong&gt;tainted&lt;/strong&gt;, preventing untrusted external inputs from reaching high-privilege execution sinks.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Integration Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VarkEngine&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@saturn/vark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IsolatedSandbox&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@saturn/vark/security&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize Vark Security Engine&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VarkEngine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;policyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./policies/agent-policy.yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;auditLogPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logs/vark-audit.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;enableDLP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Intercept an autonomous agent tool execution request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;execute_script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;agent-worker-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;console.log("Processing batch job");&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&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;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolRequest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Execute safely inside a memory-capped V8 isolate (64MB cap, 1s timeout)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IsolatedSandbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
    &lt;span class="na"&gt;memoryLimitMb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;executionTimeoutMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&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;rawOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Pass return payload through DLP scanner before returning to agent context&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scanOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Result:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cleanOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Blocked by Gate [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failedGate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Vark is open-source under the dual &lt;strong&gt;Apache-2.0 / MIT&lt;/strong&gt; license and maintained by LUVEO Technologies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/luveo-technologies/vark" rel="noopener noreferrer"&gt;github.com/luveo-technologies/vark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm Package:&lt;/strong&gt; &lt;code&gt;@saturn/vark&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the repository, try integrating it into your agent pipeline, and let us know what features or security gates you'd like to see next!&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Stop Mocking Everything in Integration Tests: Why Testcontainers Changed My CI Strategy</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Thu, 24 Sep 2026 16:13:43 +0000</pubDate>
      <link>https://dev.to/mindinu/stop-mocking-everything-in-integration-tests-why-testcontainers-changed-my-ci-strategy-2akl</link>
      <guid>https://dev.to/mindinu/stop-mocking-everything-in-integration-tests-why-testcontainers-changed-my-ci-strategy-2akl</guid>
      <description>&lt;p&gt;Every backend developer knows the classic trade-off when writing tests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests with mocks are lightning fast&lt;/strong&gt;, but they lie to you about database constraint violations, subtle SQL dialect quirks, and ORM mapping bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-end (E2E) tests catch real bugs&lt;/strong&gt;, but they are notoriously flaky, require complex staging environments, and slow down CI pipelines.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For years, the standard compromise was using lightweight, in-memory alternatives for testing—like running &lt;strong&gt;H2 or SQLite&lt;/strong&gt; in your test suite while production runs on &lt;strong&gt;PostgreSQL&lt;/strong&gt; or &lt;strong&gt;MySQL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This approach creates a false sense of security.&lt;/p&gt;




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

&lt;p&gt;Running an in-memory substitute in tests while using a real relational database in production frequently introduces subtle bugs that bypass CI entirely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON/JSONB Operators:&lt;/strong&gt; Native JSON querying behaves completely differently across engine dialects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency &amp;amp; Locking:&lt;/strong&gt; Row-level locking (&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;), transaction isolation levels, and deadlocks cannot be reliably reproduced on in-memory databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL Extensions:&lt;/strong&gt; Features like &lt;code&gt;pgvector&lt;/code&gt;, PostGIS, or custom full-text search indexes don't exist in H2 or SQLite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mocking your persistence layer doesn't test your database interaction—it tests &lt;strong&gt;your assumptions about how the database works&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Testcontainers: Ephemeral Infrastructure in Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt; flips this paradigm. Instead of relying on mocks or shared staging databases, it allows you to spin up real, disposable Docker containers directly from your test code (available in Node.js, Go, Python, Java, .NET, and more).&lt;/p&gt;

&lt;p&gt;Here is what a true integration test looks like in TypeScript using Testcontainers and PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PostgreSqlContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StartedPostgreSqlContainer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testcontainers/postgresql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User Repository Integration Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StartedPostgreSqlContainer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;dbPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Spin up a real PostgreSQL 16 container before tests run&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSqlContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres:16-alpine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_pass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;dbPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnectionUri&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Run database migrations against the real engine&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runMigrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbPool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Tear down the ephemeral container after tests complete&lt;/span&gt;
  &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should enforce unique email constraint on user insertion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT INTO users(email, name) VALUES($1, $2)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Test real PostgreSQL duplicate key error handling&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;dbPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT INTO users(email, name) VALUES($1, $2)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/duplicate key value violates unique constraint/&lt;/span&gt;&lt;span class="p"&gt;);&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;h2&gt;
  
  
  Why This Pattern Scales
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Zero Staging Pollution
&lt;/h3&gt;

&lt;p&gt;Because every test run provisions its own ephemeral container on a random host port, tests run in complete isolation. You no longer suffer from dynamic test order dependencies or leftover dirty state in shared staging databases.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Multi-Service Integration
&lt;/h3&gt;

&lt;p&gt;Testcontainers isn't limited to SQL. You can orchestrate multi-container setups inside a single test file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spin up &lt;strong&gt;Redis&lt;/strong&gt; to verify cache invalidation logic.&lt;/li&gt;
&lt;li&gt;Spin up &lt;strong&gt;Kafka/RabbitMQ&lt;/strong&gt; to verify async event-driven consumers.&lt;/li&gt;
&lt;li&gt;Spin up &lt;strong&gt;LocalStack&lt;/strong&gt; to test AWS S3 uploads locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Deterministic CI Environments
&lt;/h3&gt;

&lt;p&gt;Since the container image tag is explicitly defined in your test code (&lt;code&gt;postgres:16-alpine&lt;/code&gt;), your local dev environment and your GitHub Actions / GitLab CI runner run on the &lt;strong&gt;exact same byte-for-byte engine&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for CI Performance
&lt;/h2&gt;

&lt;p&gt;Running real containers in CI comes with performance considerations. To keep build times fast:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reuse Containers Across Test Suites:&lt;/strong&gt; Enable container reuse (&lt;code&gt;withReuse(true)&lt;/code&gt;) during local development so you don't pay the startup penalty on every code change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Lightweight Base Images:&lt;/strong&gt; Stick to minimal tags like &lt;code&gt;alpine&lt;/code&gt; or &lt;code&gt;slim&lt;/code&gt; variants to keep Docker image pull times down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run on Ramdisk (tmpfs):&lt;/strong&gt; Mount the database data directory inside the container to tmpfs (&lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;) to bypass physical disk I/O bottlenecks during test runs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In-memory database substitutes and heavy mocking were necessary compromises a decade ago. Today, containerized testing makes it seamless to validate your code against the exact technologies running in production.&lt;/p&gt;

&lt;p&gt;If you aren't testing against real infrastructure, you aren't really integration testing!&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Your Production API Needs Idempotency Keys (And How to Build an Engine in Node.js &amp; Redis)</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Sun, 20 Sep 2026 09:02:45 +0000</pubDate>
      <link>https://dev.to/mindinu/why-your-production-api-needs-idempotency-keys-and-how-to-build-an-engine-in-nodejs-redis-30n6</link>
      <guid>https://dev.to/mindinu/why-your-production-api-needs-idempotency-keys-and-how-to-build-an-engine-in-nodejs-redis-30n6</guid>
      <description>&lt;p&gt;In a perfect network, every HTTP request arrives exactly once. In the real world, mobile clients drop connection mid-flight, timeouts hit load balancers, and frontend retry logic triggers duplicate operations.&lt;/p&gt;

&lt;p&gt;If a user clicks "Pay Now" on a $100 checkout, their connection drops, and their app automatically retries the request 3 seconds later, what happens?&lt;/p&gt;

&lt;p&gt;Without an &lt;strong&gt;Idempotency Engine&lt;/strong&gt;, your API risks processing two charges for a single intent. &lt;/p&gt;

&lt;p&gt;While &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; methods are naturally idempotent by HTTP spec, &lt;code&gt;POST&lt;/code&gt; endpoints (creating charges, generating invoices, triggering AI workflows) are inherently non-idempotent.&lt;/p&gt;

&lt;p&gt;Here is how production systems guarantee &lt;strong&gt;exact-once execution semantics&lt;/strong&gt; using Idempotency Keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  How an Idempotency Engine Works
&lt;/h2&gt;

&lt;p&gt;An idempotency key is a unique, client-generated identifier (usually a v4 UUID) sent in the HTTP header:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Idempotency-Key: 7b9e1d84-2a3c-4e89-9102-1a4f52e39a01&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When the server receives a request with an idempotency key, it enters a state machine execution loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  +-----------------------------------+
                  |   Incoming Request + Header Key   |
                  +-----------------------------------+
                                    |
                                    v
                       [ Check Redis Cache for Key ]
                                    |
                     +--------------+--------------+
                     |                             |
             (Key Exists?)                   (Key Missing?)
                     |                             |
          +----------+----------+                  v
          |                     |       [ Set Key State: "PROCESSING" ]
   (State: COMPLETE)   (State: PROCESSING)         |
          |                     |                  v
          v                     v       [ Execute Business Logic ]
   [ Return Saved ]      [ Return 409 ]            |
   [ HTTP Response ]     [ Conflict ]              v
                                        [ Save Response + State: "COMPLETE" ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First Request:&lt;/strong&gt; The server checks Redis for the key. Missing. It stores &lt;code&gt;key: "PROCESSING"&lt;/code&gt; with a short TTL (e.g., 30 seconds) and executes the logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Duplicate:&lt;/strong&gt; If a second request arrives with the same key while the first is still processing, the server immediately rejects it with &lt;code&gt;409 Conflict&lt;/code&gt; or &lt;code&gt;429 Too Many Requests&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completed Duplicate:&lt;/strong&gt; Once the first request succeeds, the server caches the HTTP status code and response body in Redis under that key. Subsequent retries return the &lt;strong&gt;cached response instantly&lt;/strong&gt; without re-executing any logic.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Production Middleware Implementation (Express + Redis)
&lt;/h2&gt;

&lt;p&gt;Here is a clean, dependency-light middleware implementation in Node.js using Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextFunction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&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;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_URL&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;IDEMPOTENCY_TTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 24 Hours in seconds&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idempotencyMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextFunction&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Idempotency-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Skip if client didn't supply a key (or enforce it for critical routes)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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;redisKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`idempotency:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Atomically set key if it doesn't exist (NX) with a lock timeout&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acquired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PROCESSING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;acquired&lt;/span&gt;&lt;span class="p"&gt;)&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;cachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&lt;/span&gt;&lt;span class="p"&gt;)&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;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PROCESSING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Concurrent request in progress. Please wait.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Return previously cached execution response&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Intercept res.json to capture response payload before sending to client&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Save execution result to Redis for future retries&lt;/span&gt;
      &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;redisKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COMPLETE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; 
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;IDEMPOTENCY_TTL&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;originalJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&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;h2&gt;
  
  
  Crucial Edge Cases to Consider
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Request Payload Validation
&lt;/h3&gt;

&lt;p&gt;What if a malicious actor sends the same &lt;code&gt;Idempotency-Key&lt;/code&gt; with a completely &lt;em&gt;different&lt;/em&gt; JSON payload?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Hash the request body alongside the key (&lt;code&gt;SHA256(Key + Payload)&lt;/code&gt;). If the key matches but the payload hash differs, return &lt;code&gt;400 Bad Request&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Handling Hard Failures (5xx Server Errors)
&lt;/h3&gt;

&lt;p&gt;If your database or downstream service crashes while processing a request, &lt;strong&gt;do not save a 500 error as a permanent idempotent response&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delete the Redis key if the request throws an unhandled exception so the client can safely retry after your system recovers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Distributed Lock Leaks
&lt;/h3&gt;

&lt;p&gt;Always set an expiration TTL (e.g., 30 seconds) on the initial &lt;code&gt;PROCESSING&lt;/code&gt; lock. If your API worker dies mid-execution, the key will naturally expire rather than permanently locking out the user from retrying.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Handling retries cleanly is the difference between a brittle prototype and enterprise-ready API infrastructure. By pushing idempotency tracking to a fast Redis layer, you protect your database from duplicate writes and give your client applications a bulletproof retry strategy.&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>node</category>
      <category>redis</category>
    </item>
    <item>
      <title>How Eventual Consistency Breaks System Logic (And How to Handle It in Distributed Systems)</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Sat, 19 Sep 2026 12:31:59 +0000</pubDate>
      <link>https://dev.to/mindinu/how-eventual-consistency-breaks-system-logic-and-how-to-handle-it-in-distributed-systems-15cg</link>
      <guid>https://dev.to/mindinu/how-eventual-consistency-breaks-system-logic-and-how-to-handle-it-in-distributed-systems-15cg</guid>
      <description>&lt;p&gt;When building distributed microservices, moving away from monolithic ACID transactions to eventual consistency is often touted as the ultimate cure for scalability bottlenecks. But eventual consistency introduces a silent killer: &lt;strong&gt;race conditions between data propagation and business rules.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a single, unified database, a standard transaction guarantees immediate consistency. Once a write commits, every subsequent read sees that data. &lt;/p&gt;

&lt;p&gt;In an eventually consistent, asynchronous architecture (e.g., using Kafka, DynamoDB, or PostgreSQL logical replication), &lt;strong&gt;there is a time window where different parts of your system hold conflicting truths.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is how eventual consistency breaks application logic—and the patterns senior engineers use to prevent data corruption.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Phantom Inventory Problem (When Consistency Delays Kill UX)
&lt;/h2&gt;

&lt;p&gt;Consider an e-commerce checkout pipeline split into two services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Order Service:&lt;/strong&gt; Creates a pending order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory Service:&lt;/strong&gt; Decrements stock and confirms availability.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------+     1. Write Order     +----------------------+
| Order Service | ---------------------&amp;gt; | Primary DB (Master)  |
+---------------+                        +----------------------+
        |                                           |
        | 2. Emit "OrderCreated" Event              | Async Replication
        v                                           v
+------------------+                     +----------------------+
| Event Message Bus|                     | Read Replica DB      |
+------------------+                     +----------------------+
        |                                           ^
        | 3. Process Async                          | 4. User refreshes
        v                                           |    page (Reads old state!)
+-------------------+                       +------------------+
| Inventory Service |                       | Web Client / App |
+-------------------+                       +------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Goes Wrong:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A user buys the last remaining item in stock.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Order Service&lt;/code&gt; writes to the primary database and emits an &lt;code&gt;OrderCreated&lt;/code&gt; event to a message queue.&lt;/li&gt;
&lt;li&gt;The user is redirected to their "Order Confirmation" page.&lt;/li&gt;
&lt;li&gt;The web client queries a &lt;strong&gt;Read Replica&lt;/strong&gt; for order status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Trap:&lt;/strong&gt; Because database replication has a 200ms lag, the Read Replica still shows $0$ orders and stock available. The UI shows "Order Failed" or allows a second user to purchase the same item.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't a database crash—it's a &lt;strong&gt;consistency window failure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Design Patterns to Fix Eventual Consistency Bugs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Read-Your-Own-Writes Consistency (Client-Side State Tracking)
&lt;/h3&gt;

&lt;p&gt;Instead of relying on the backend read replicas immediately after a write operation, the host application maintains a short-lived local state or passes a session token (like a monotonic sequence ID or vector clock).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; When the client performs a write, the API returns a version marker (&lt;code&gt;version_id: 1042&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When fetching data, the client sends &lt;code&gt;If-Match-Version: 1042&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;If the read replica hasn't caught up to version 1042 yet, the API gateway routes the read request directly to the &lt;strong&gt;Primary/Leader Database&lt;/strong&gt; instead of the lagging replica.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Saga Pattern (Orchestration vs. Choreography)
&lt;/h3&gt;

&lt;p&gt;Because you cannot execute a distributed transaction spanning multiple databases using traditional 2-Phase Commit (2PC) without crippling performance, you use a &lt;strong&gt;Saga&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Saga executes a sequence of local transactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transaction 1:&lt;/strong&gt; Reserve inventory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction 2:&lt;/strong&gt; Process payment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction 3:&lt;/strong&gt; Confirm order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;strong&gt;Transaction 2 (Payment)&lt;/strong&gt; fails, the Saga Orchestrator triggers explicit &lt;strong&gt;Compensating Transactions&lt;/strong&gt; in reverse order (e.g., executing &lt;code&gt;Unreserve Inventory&lt;/code&gt;) to return the system to a clean, balanced state.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Idempotent Event Handlers
&lt;/h3&gt;

&lt;p&gt;In asynchronous networks, messages get delayed, retried, and delivered out of order. An eventual consistency pipeline MUST assume every event will be delivered &lt;strong&gt;at least once&lt;/strong&gt; and potentially &lt;strong&gt;out of sequence&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Bad: Non-idempotent update (Running twice doubles the discount)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;user_balances&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Good: Idempotent state machine with explicit event tracking&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;processed_events&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processed_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'evt_9921'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;user_balances&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;last_event_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'evt_9921'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary Checklist for Distributed System State
&lt;/h2&gt;

&lt;p&gt;When designing async or multi-database features, ask these three questions before hitting production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What happens if this message arrives 5 seconds late?&lt;/strong&gt; (Will it overwrite newer data?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What happens if this event executes twice?&lt;/strong&gt; (Is the consumer strictly idempotent?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the UI reflect speculative execution or read-replica lag?&lt;/strong&gt; (Are you shielding the user from replica latency windows?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eventual consistency is necessary for high scale, but treating it like immediate consistency is the root cause of subtle, hard-to-reproduce production bugs!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>microservices</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Why Your Production Microservices Should Use Circuit Breakers (And How to Implement One in 50 Lines)</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Sat, 19 Sep 2026 05:59:46 +0000</pubDate>
      <link>https://dev.to/mindinu/why-your-production-microservices-should-use-circuit-breakers-and-how-to-implement-one-in-50-lines-42n2</link>
      <guid>https://dev.to/mindinu/why-your-production-microservices-should-use-circuit-breakers-and-how-to-implement-one-in-50-lines-42n2</guid>
      <description>&lt;p&gt;If you are building distributed systems, network calls will fail. It’s not a matter of &lt;em&gt;if&lt;/em&gt;, but &lt;em&gt;when&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Whether it's an API rate limit, a transient database spike, or a downstream service deploying buggy code, external network calls are non-deterministic. The real problem isn't the single failed request—it's &lt;strong&gt;cascading failures&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When Service A synchronously calls Service B, and Service B begins hanging due to high latency, Service A runs out of available threads or memory waiting for responses. Suddenly, Service A crashes, taking down Service C, and dragging your entire infrastructure into a full outage.&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;Circuit Breaker Pattern&lt;/strong&gt; becomes vital.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a Circuit Breaker?
&lt;/h2&gt;

&lt;p&gt;Inspired by the electrical circuit breaker in your home that cuts power during a current overload, a software circuit breaker wraps an expensive or network-bound call and monitors for failures.&lt;/p&gt;

&lt;p&gt;A circuit breaker operates in three distinct states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         +--------------------------------------------+
         |                                            |
         v                                            |
   +-----------+   Failures &amp;gt; Threshold   +----------+ |
---|   CLOSED  |-------------------------&amp;gt;|   OPEN   | | Success
   +-----------+                          +----------+ |
         ^                                     |       |
         |         Timeout Expired             |       |
         |      +-------------------+          |       |
         +------|     HALF-OPEN     |&amp;lt;---------+       |
                +-------------------+                  |
                          |                            |
                          +----------------------------+
                                   Failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CLOSED (Normal Operation):&lt;/strong&gt; All requests pass through to the downstream service. The breaker records successes and failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OPEN (Failing Fast):&lt;/strong&gt; If the failure rate crosses a specified threshold within a time window, the circuit trips &lt;strong&gt;OPEN&lt;/strong&gt;. All incoming requests immediately return a fallback error or cached data &lt;strong&gt;without actually making a network call&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HALF-OPEN (Testing the Waters):&lt;/strong&gt; After a cooldown period, the breaker allows a limited number of test requests through. If they succeed, the circuit resets to &lt;strong&gt;CLOSED&lt;/strong&gt;. If they fail, it trips back to &lt;strong&gt;OPEN&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Implementing a Minimal Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;Here is a lightweight, dependency-free implementation of a Circuit Breaker in Go (the exact same pattern applies in TypeScript, Python, or Java).&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
go
package main

import (
    "errors"
    "sync"
    "time"
)

type State int

const (
    StateClosed State = iota
    StateOpen
    StateHalfOpen
)

type CircuitBreaker struct {
    mu               sync.Mutex
    state            State
    failureThreshold int
    cooldown         time.Duration
    failures         int
    lastFailureTime  time.Time
}

func NewCircuitBreaker(threshold int, cooldown time.Duration) *CircuitBreaker {
    return &amp;amp;CircuitBreaker{
        state:            StateClosed,
        failureThreshold: threshold,
        cooldown:         cooldown,
    }
}

func (cb *CircuitBreaker) Execute(req func() error) error {
    cb.mu.Lock()

    // Check if OPEN circuit cooldown period has expired
    if cb.state == StateOpen {
        if time.Since(cb.lastFailureTime) &amp;gt; cb.cooldown {
            cb.state = StateHalfOpen
        } else {
            cb.mu.Unlock()
            return errors.New("circuit breaker is OPEN: fast-failing request")
        }
    }

    cb.mu.Unlock()

    // Execute actual network request
    err := req()

    cb.mu.Lock()
    defer cb.mu.Unlock()

    if err != nil {
        cb.failures++
        cb.lastFailureTime = time.Now()

        if cb.failures &amp;gt;= cb.failureThreshold {
            cb.state = StateOpen
        }
        return err
    }

    // Reset state on successful request
    if cb.state == StateHalfOpen || cb.state == StateClosed {
        cb.failures = 0
        cb.state = StateClosed
    }

    return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>WebAssembly Beyond the Browser: Building a Sandboxed Plugin System in Node.js &amp; Go</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Fri, 18 Sep 2026 16:35:15 +0000</pubDate>
      <link>https://dev.to/mindinu/webassembly-beyond-the-browser-building-a-sandboxed-plugin-system-in-nodejs-go-eo0</link>
      <guid>https://dev.to/mindinu/webassembly-beyond-the-browser-building-a-sandboxed-plugin-system-in-nodejs-go-eo0</guid>
      <description>&lt;p&gt;For years, WebAssembly (WASM) was pitched primarily as a way to bring high-performance C++ or Rust graphics to the web browser. But in modern backend engineering, WASM’s most compelling application has shifted entirely: &lt;strong&gt;safe, near-native sandboxed plugin execution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are building a system where third-party developers (or internal teams) need to execute custom logic—like webhook transformers, custom authorization rules, or pipeline data formatters—running untrusted code safely is a nightmare. &lt;/p&gt;

&lt;p&gt;Traditional approaches come with heavy trade-offs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eval()&lt;/code&gt; or Node &lt;code&gt;vm&lt;/code&gt; module:&lt;/strong&gt; Insecure. Process isolation is weak, memory leak-prone, and susceptible to prototype pollution or host system access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Containers / Micro-VMs (Firecracker):&lt;/strong&gt; Maximum isolation, but massive overhead. Spin-up latency takes tens to hundreds of milliseconds, and memory consumption scales poorly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded JS Interpreters (V8 Isolate / QuickJS):&lt;/strong&gt; Better, but locks your plugin authors into a single language ecosystem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enter &lt;strong&gt;WebAssembly on the Server&lt;/strong&gt;. By embedding a lightweight WASM runtime (like Wasmtime or Extism) into your host application, you get isolated execution with near-zero cold starts (&amp;lt; 1ms) and predictable memory boundaries.&lt;/p&gt;

&lt;p&gt;Here is a practical look at how server-side WASM sandboxing works and how to design a safe plugin host.&lt;/p&gt;




&lt;h2&gt;
  
  
  The WASM Sandbox Architecture
&lt;/h2&gt;

&lt;p&gt;When executing a WASM plugin inside a host process, the WASM runtime creates an isolated instance with explicit memory bounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------------------------------------+
| HOST APPLICATION (Node.js / Go / Rust)                      |
|                                                             |
|   +-----------------------------------------------------+   |
|   | WASM RUNTIME INSTANCE (e.g., Wasmtime / Extism)     |   |
|   |                                                     |   |
|   |   - Linear Memory: Fixed Max Allocation (e.g. 16MB) |   |
|   |   - System Calls: DENIED by default                 |   |
|   |   - Disk / Network: Isolated / Whitelisted Host ABI |   |
|   |                                                     |   |
|   |   [ Plugin Code (Compiled from Rust/Go/Zig) ]       |   |
|   +-----------------------------------------------------+   |
|                                                             |
+-------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, a WASM module is &lt;strong&gt;deny-by-default&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It cannot read or write to the host file system.&lt;/li&gt;
&lt;li&gt;It cannot open network sockets.&lt;/li&gt;
&lt;li&gt;It cannot access the host machine’s memory outside its allocated linear memory block.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Designing the Host-Plugin Contract (ABI)
&lt;/h2&gt;

&lt;p&gt;Because WASM natively only understands basic numeric types (&lt;code&gt;i32&lt;/code&gt;, &lt;code&gt;i64&lt;/code&gt;, &lt;code&gt;f32&lt;/code&gt;, &lt;code&gt;f64&lt;/code&gt;), passing complex data (like JSON strings or binary payloads) across the host-guest boundary requires an Application Binary Interface (ABI) convention.&lt;/p&gt;

&lt;p&gt;Here is how data flows across the boundary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Host Allocates Memory:&lt;/strong&gt; The host writes the input payload (e.g., JSON bytes) directly into a segment of the WASM instance's linear memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host Passes Pointers:&lt;/strong&gt; The host calls the WASM exported function, passing the &lt;strong&gt;memory pointer&lt;/strong&gt; and &lt;strong&gt;length&lt;/strong&gt; as integer arguments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guest Processes Data:&lt;/strong&gt; The plugin reads the memory segment, executes its transformation logic, and writes the output payload to another memory segment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guest Returns Pointer:&lt;/strong&gt; The WASM function returns an integer pointer pointing to the result location in memory for the host to consume.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Example: Embedding a WASM Plugin Host in Go
&lt;/h2&gt;

&lt;p&gt;Using open-source frameworks like &lt;strong&gt;Extism&lt;/strong&gt; or &lt;strong&gt;Wazero&lt;/strong&gt;, setting up a host runtime takes less than 20 lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"[github.com/extism/go-sdk](https://github.com/extism/go-sdk)"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 1. Configure memory bounds and plugin source&lt;/span&gt;
    &lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Manifest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Wasm&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wasm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WasmFile&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"./plugins/transform_user_payload.wasm"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManifestMemory&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;MaxPages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// Cap total memory at 2MB (64KB per page)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Instantiate the sandboxed plugin&lt;/span&gt;
    &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extism&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginConfig&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Call exported function with raw JSON payload&lt;/span&gt;
    &lt;span class="n"&gt;inputJSON&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`{"user_id": 1042, "raw_role": "admin_v2"}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exitCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transform_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputJSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Plugin execution failed with exit code: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exitCode&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="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Plugin Output: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&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;h2&gt;
  
  
  The Trade-Offs You Must Consider
&lt;/h2&gt;

&lt;p&gt;While WASM-based plugin systems offer incredible performance and security benefits, they aren't a silver bullet:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Garbage Collection Boundary
&lt;/h3&gt;

&lt;p&gt;Languages with runtime GC (like Go or AssemblyScript) embed their GC engine into the compiled &lt;code&gt;.wasm&lt;/code&gt; binary, increasing binary size. Languages like &lt;strong&gt;Rust&lt;/strong&gt;, &lt;strong&gt;Zig&lt;/strong&gt;, or &lt;strong&gt;C&lt;/strong&gt; compile down to minimal WASM binaries (often under 100KB) and are far better suited for writing lightweight plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fuel &amp;amp; Execution Limits
&lt;/h3&gt;

&lt;p&gt;A malicious or buggy plugin could contain an infinite loop: &lt;code&gt;while(true) {}&lt;/code&gt;. To prevent CPU starvation, WASM runtimes use &lt;strong&gt;Fuel Consumption&lt;/strong&gt; algorithms. The host assigns a fixed number of "fuel units" to a invocation. Every WASM instruction consumes fuel—when fuel runs out, the runtime instantly terminates the instance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxing:&lt;/strong&gt; WASM provides hardware-level memory boundaries without requiring full containerization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold Start Speed:&lt;/strong&gt; Instantiating a compiled WASM module takes microseconds compared to milliseconds/seconds for containers or V8 isolates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Flexibility:&lt;/strong&gt; Plugin authors can write in Rust, C, Go, Zig, or TypeScript (via AssemblyScript) as long as it targets &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; or &lt;code&gt;wasm32-wasi&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're architecting developer tools, workflow automation engines, or multi-tenant API gateways, embedding a WASM engine is one of the cleanest patterns available today for safe, high-throughput extensibility.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>security</category>
      <category>webassembly</category>
    </item>
    <item>
      <title>The Anatomy of a Slow Database Query: How to Diagnose, Deconstruct, and Fix It</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Mon, 14 Sep 2026 10:53:56 +0000</pubDate>
      <link>https://dev.to/mindinu/the-anatomy-of-a-slow-database-query-how-to-diagnose-deconstruct-and-fix-it-3ndj</link>
      <guid>https://dev.to/mindinu/the-anatomy-of-a-slow-database-query-how-to-diagnose-deconstruct-and-fix-it-3ndj</guid>
      <description>&lt;p&gt;Every backend developer has experienced that sinking feeling: a user reports that a dashboard is loading slowly, you check the application logs, and you find a database query taking upwards of 3,000 milliseconds. &lt;/p&gt;

&lt;p&gt;In a local environment with three rows of test data, everything ran instantaneously. But in production, with millions of rows, that single unoptimized query brings the entire application to its knees.&lt;/p&gt;

&lt;p&gt;In this deep dive, we are going to dissect the anatomy of a slow database query. We will look at how relational databases process queries, how to read an execution plan, spot missing indexes, and optimize JOIN operations to get your response times back into the single-digit milliseconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Lifecycle of a Query
&lt;/h2&gt;

&lt;p&gt;When your application sends a SQL query to a database, it doesn't just scan the hard drive blindly. The database engine goes through a rigorous pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parser &amp;amp; Translator:&lt;/strong&gt; Validates syntax and checks if the tables and columns exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rewriter:&lt;/strong&gt; Optimizes the query structure logically (e.g., rewriting subqueries into joins).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Optimizer (The Brain):&lt;/strong&gt; This is where the magic (or tragedy) happens. The optimizer analyzes statistical data about your tables and indexes to generate the &lt;strong&gt;Execution Plan&lt;/strong&gt;—the cheapest, fastest path to retrieve the requested data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Engine:&lt;/strong&gt; Executes the plan, fetching data from disk or buffer pool memory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a query is slow, 99% of the time it’s because &lt;strong&gt;the Query Optimizer chose a suboptimal execution plan&lt;/strong&gt;, usually forced into doing so by missing indexes, outdated statistics, or poor query design.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Reading the Execution Plan (&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Never guess why a query is slow. Always ask the database. &lt;/p&gt;

&lt;p&gt;In PostgreSQL, MySQL, and SQLite, prefixing your query with &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; (or &lt;code&gt;EXPLAIN&lt;/code&gt; depending on the flavor) tells the database to run the query and output the execution steps it took along with actual timing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt; 
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you look at the output, you want to hunt down two major red flags:&lt;/p&gt;

&lt;h3&gt;
  
  
  Red Flag A: The Sequential Scan (&lt;code&gt;Seq Scan&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;If you see a &lt;code&gt;Seq Scan&lt;/code&gt; on a massive table, the database is reading &lt;strong&gt;every single row&lt;/strong&gt; from disk from start to finish to check if it matches your &lt;code&gt;WHERE&lt;/code&gt; clause. If your &lt;code&gt;users&lt;/code&gt; table has 10 million rows, a sequential scan means reading 10 million rows, regardless of whether you only wanted 5 of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Red Flag B: High Cost and Actual Time Discrepancies
&lt;/h3&gt;

&lt;p&gt;Execution plans show estimated costs and actual time. If the optimizer estimated 5 rows and got 500,000 rows, your table statistics are stale, leading the optimizer to choose a disastrously bad execution strategy (like a Nested Loop join instead of a Hash join).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Culprit: Missing or Inefficient Indexes
&lt;/h2&gt;

&lt;p&gt;Indexes are the primary weapon against slow queries. Think of an index like the index at the back of a textbook: instead of reading every page to find a keyword, you look up the word alphabetically and jump straight to the correct page.&lt;/p&gt;

&lt;h3&gt;
  
  
  The B-Tree Index Structure
&lt;/h3&gt;

&lt;p&gt;Most databases use &lt;strong&gt;B-Trees&lt;/strong&gt; by default. A B-Tree keeps data sorted and allows logarithmic time searches (O(log n)) rather than linear searches (O(n)).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trap: Leftmost Prefix Rule
&lt;/h3&gt;

&lt;p&gt;Composite indexes (indexes on multiple columns) require care. If you create an index on &lt;code&gt;(status, created_at, user_id)&lt;/code&gt;, the database can use it if you query by &lt;code&gt;status&lt;/code&gt;, or &lt;code&gt;status AND created_at&lt;/code&gt;. However, if you query &lt;em&gt;only&lt;/em&gt; by &lt;code&gt;created_at&lt;/code&gt;, &lt;strong&gt;the index is completely ignored&lt;/strong&gt; because the B-Tree requires the leftmost column to navigate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- GOOD: Uses the composite index on (status, created_at)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- BAD: Bypasses the index because 'status' is skipped&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. The Trap of Hidden Operations (Functions on Columns)
&lt;/h2&gt;

&lt;p&gt;Look at this seemingly harmless query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;YEAR&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this slow? Because you wrapped the column &lt;code&gt;created_at&lt;/code&gt; in a function (&lt;code&gt;EXTRACT&lt;/code&gt;). The database can no longer use a standard B-Tree index built on &lt;code&gt;created_at&lt;/code&gt; because it doesn't store the extracted year value—it stores the raw timestamp. To evaluate this, the database must evaluate the function on &lt;em&gt;every single row&lt;/em&gt; (causing a Sequential Scan).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix: SARGable Queries
&lt;/h3&gt;

&lt;p&gt;Make your queries &lt;strong&gt;SARGable&lt;/strong&gt; (Search Argument Able) by keeping columns bare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- GOOD: Allows the database to use an index on created_at&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01 00:00:00'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01 00:00:00'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Anatomy of a Bad JOIN
&lt;/h2&gt;

&lt;p&gt;Joins are notorious for performance bottlenecks when scaling. Consider this query connecting &lt;code&gt;orders&lt;/code&gt;, &lt;code&gt;items&lt;/code&gt;, and &lt;code&gt;customers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How Databases Handle Joins
&lt;/h3&gt;

&lt;p&gt;Databases typically choose between three join algorithms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Nested Loop:&lt;/strong&gt; Good for small datasets or when the inner table is indexed on the join key. Terrible when both tables are massive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash Join:&lt;/strong&gt; The database builds an in-memory hash table of the smaller relation and probes it with the larger relation. Highly efficient for large unindexed sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge Join:&lt;/strong&gt; Sorts both relations on the join key and merges them. Requires sorted inputs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your query is slow here, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Foreign Key Indexes:&lt;/strong&gt; Are &lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;order_id&lt;/code&gt;, and &lt;code&gt;item_id&lt;/code&gt; indexed on their respective child tables? If not, the database may resort to nested loops with full table scans for &lt;em&gt;every single row&lt;/em&gt; matched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter Placement:&lt;/strong&gt; Are you filtering (&lt;code&gt;WHERE&lt;/code&gt;) after the join, or can you filter the dataset &lt;em&gt;before&lt;/em&gt; performing the join to minimize rows flowing through memory?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary Checklist for Optimizing Queries
&lt;/h2&gt;

&lt;p&gt;When you encounter a slow query in production, run through this quick mental checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;&lt;/strong&gt; to see what the database is actually doing (look for &lt;code&gt;Seq Scan&lt;/code&gt; and high execution times).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for missing indexes&lt;/strong&gt; on columns used in &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN ON&lt;/code&gt;, and &lt;code&gt;ORDER BY&lt;/code&gt; clauses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensure queries are SARGable&lt;/strong&gt;—avoid wrapping columns in functions or operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect table statistics&lt;/strong&gt; (&lt;code&gt;ANALYZE&lt;/code&gt; in Postgres/MySQL) to ensure the query optimizer isn't working with outdated assumptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit payload size&lt;/strong&gt;—never use &lt;code&gt;SELECT *&lt;/code&gt; if you only need two columns; reducing data width reduces disk I/O and network overhead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Database performance optimization is an iterative engineering discipline. Master your execution plans, understand your indexes, and treat your database engine as a cooperative partner rather than a black box!&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>performance</category>
      <category>sql</category>
    </item>
    <item>
      <title>⚡ Stop Defaulting to WebSockets: Why Server-Sent Events (SSE) are Usually Better</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Fri, 04 Sep 2026 04:56:51 +0000</pubDate>
      <link>https://dev.to/mindinu/stop-defaulting-to-websockets-why-server-sent-events-sse-are-usually-better-3k2g</link>
      <guid>https://dev.to/mindinu/stop-defaulting-to-websockets-why-server-sent-events-sse-are-usually-better-3k2g</guid>
      <description>&lt;p&gt;There is a moment in every web developer's career when a client asks: &lt;em&gt;"Can we make this update in real time?"&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Your mind immediately jumps to WebSockets. It is the industry buzzword. It sounds fast. You spin up &lt;code&gt;socket.io&lt;/code&gt; or &lt;code&gt;Reverb&lt;/code&gt;, spend two days fighting with your load balancer, and finally get it working.&lt;/p&gt;

&lt;p&gt;But here is the harsh truth: for about 90% of modern web applications—including AI chat streaming, live dashboards, and notification feeds—&lt;strong&gt;WebSockets are massive overkill&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Instead, you should probably be using &lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt;. Here is why SSE is often the cleaner, cheaper, and more pragmatic choice.&lt;/p&gt;

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

&lt;p&gt;Both WebSockets and SSE exist to push data from the server to the client without the client needing to constantly poll the server. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;WebSockets&lt;/strong&gt; create a full-duplex, persistent TCP connection. Both the client and the server can shout at each other simultaneously.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSE&lt;/strong&gt; is a unidirectional, HTTP-based stream. The server keeps a standard HTTP connection open and pushes text-based events down to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why WebSockets Are a Headache in Production
&lt;/h2&gt;

&lt;p&gt;WebSockets are amazing for multiplayer games or collaborative tools like Google Docs where clients are constantly sending high-frequency data &lt;em&gt;back&lt;/em&gt; to the server. But that power comes with a heavy infrastructure tax.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Stateful Scaling:&lt;/strong&gt; WebSockets are stateful. If you scale horizontally, your load balancer needs connection-aware routing (sticky sessions) to ensure a client's subsequent messages go to the specific server holding their connection.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Proxy Nightmares:&lt;/strong&gt; Aggressive corporate proxies and firewalls frequently drop WebSocket protocol upgrades, leaving connections in failure modes that are notoriously hard to debug. &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Memory Hogs:&lt;/strong&gt; Maintaining bidirectional frame buffers and tracking protocol state means every single WebSocket connection consumes significantly more server memory than an equivalent HTTP connection.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No Native Reconnect:&lt;/strong&gt; If a WebSocket connection drops (and it will), the browser does not care. You have to write all the custom logic to detect the drop, backoff, retry, and resynchronize state.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why SSE is the Underdog You Need
&lt;/h2&gt;

&lt;p&gt;SSE leans on the mature, battle-tested HTTP ecosystem. It doesn't require a protocol upgrade, it doesn't need a custom server, and it works flawlessly with standard load balancers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Native Auto-Reconnect:&lt;/strong&gt; The &lt;code&gt;EventSource&lt;/code&gt; API in the browser is brilliant. If the connection drops, the browser automatically attempts to reconnect on its own. It even sends a &lt;code&gt;Last-Event-ID&lt;/code&gt; header so your server knows exactly where to resume the stream.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Standard HTTP Routing:&lt;/strong&gt; Because SSE is just a long-lived HTTP request, it scales like any other HTTP endpoint. &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Perfect for AI and Dashboards:&lt;/strong&gt; If you are streaming an LLM response or pushing live price feeds to a dashboard, the client isn't sending data &lt;em&gt;back&lt;/em&gt; through that channel (they just make a standard POST request to trigger the event). SSE perfectly models this server-push architecture.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Talk is Cheap. Look at the Code.
&lt;/h2&gt;

&lt;p&gt;Here is how simple it is to implement SSE. No massive libraries, no custom protocols. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend (Node/Express):&lt;/strong&gt;&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Set the headers to keep the connection open&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Push data whenever you want&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intervalId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Clean up on disconnect&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalId&lt;/span&gt;&lt;span class="p"&gt;);&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;&lt;strong&gt;Frontend (Vanilla JS):&lt;/strong&gt;&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="c1"&gt;// The browser handles connection, streaming, and auto-reconnecting!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New update:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&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;h2&gt;
  
  
  The Decision Framework
&lt;/h2&gt;

&lt;p&gt;WebSockets and SSE aren't competitors; they solve different shapes of problems. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose WebSockets if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You are building a chat app, multiplayer game, or real-time collaborative canvas.&lt;/li&gt;
&lt;li&gt;  The client needs to push data to the server at high frequencies (10+ times per second).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose SSE if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You are streaming AI responses, live notifications, news feeds, or financial tickers.&lt;/li&gt;
&lt;li&gt;  The communication is primarily one-way (Server → Client).&lt;/li&gt;
&lt;li&gt;  You want to avoid managing custom reconnections and complex load balancing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next time someone asks for real-time updates, don't immediately reach for the heaviest tool in the box. Give SSE a try. &lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you struggled with WebSocket scaling in production? Let's talk about it in the comments! 👇&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
    <item>
      <title>🚨 3 PostgreSQL Anti-Patterns That Are Silently Killing Your App's Performance</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Thu, 03 Sep 2026 17:40:23 +0000</pubDate>
      <link>https://dev.to/mindinu/3-postgresql-anti-patterns-that-are-silently-killing-your-apps-performance-1e5k</link>
      <guid>https://dev.to/mindinu/3-postgresql-anti-patterns-that-are-silently-killing-your-apps-performance-1e5k</guid>
      <description>&lt;p&gt;PostgreSQL is one of the most powerful relational databases on the planet. Out of the box, it can handle massive workloads. But as your application scales, the way you write queries and structure your schema matters more than the database engine itself.&lt;/p&gt;

&lt;p&gt;If your app is starting to feel sluggish, it might not be a lack of resources. You might be falling into one of these three common PostgreSQL anti-patterns. &lt;/p&gt;

&lt;p&gt;Here is how to spot them and how to fix them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The &lt;code&gt;SELECT *&lt;/code&gt; Trap (and why it ruins memory)
&lt;/h2&gt;

&lt;p&gt;When we are iterating quickly, it is incredibly tempting to just write &lt;code&gt;SELECT * FROM users&lt;/code&gt; and let the backend filter out the fields it doesn't need. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it’s an anti-pattern:&lt;/strong&gt;&lt;br&gt;
PostgreSQL has to read the data from the disk, load it into memory, and send it over the network to your application. If your &lt;code&gt;users&lt;/code&gt; table has 30 columns (including heavy &lt;code&gt;JSONB&lt;/code&gt; blobs or large &lt;code&gt;TEXT&lt;/code&gt; fields) and you only need the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt;, you are forcing the database to do 10x the I/O work for no reason. &lt;/p&gt;

&lt;p&gt;Furthermore, &lt;code&gt;SELECT *&lt;/code&gt; breaks index-only scans. If you have an index on &lt;code&gt;email&lt;/code&gt;, a query like &lt;code&gt;SELECT email FROM users WHERE email = 'x'&lt;/code&gt; can be resolved purely from the index without even touching the main table. &lt;code&gt;SELECT *&lt;/code&gt; forces Postgres to fetch the whole row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Always explicitly define your columns, even if you are using an ORM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ❌ Bad&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- ✅ Good&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_amount&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Over-Indexing (The "Just Add an Index" Fallacy)
&lt;/h2&gt;

&lt;p&gt;When a query is slow, the immediate reaction is usually: &lt;em&gt;"Let's just throw an index on that column!"&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it’s an anti-pattern:&lt;/strong&gt;&lt;br&gt;
Indexes are not free. Every time you &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, or &lt;code&gt;DELETE&lt;/code&gt; a row, PostgreSQL has to update the main table &lt;em&gt;and&lt;/em&gt; every single index associated with that table. If you have a write-heavy table (like an event logger or analytics tracker) with 10 different indexes, your write latency will skyrocket.&lt;/p&gt;

&lt;p&gt;Additionally, Postgres query planners are smart. If an index isn't highly selective (e.g., a boolean column like &lt;code&gt;is_active&lt;/code&gt; where 95% of users are active), Postgres will likely ignore the index entirely and do a sequential scan anyway. You are paying the write penalty for an index that never gets used!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Periodically check for unused indexes. Postgres tracks this for you! You can run this query to find indexes that the database is ignoring:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexrelname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_stat_user_indexes&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Drop unused indexes and lean into &lt;strong&gt;composite indexes&lt;/strong&gt; for queries that frequently filter by the same multiple columns.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  3. The ORM N+1 Query Disaster
&lt;/h2&gt;

&lt;p&gt;If you are using Prisma, TypeORM, Hibernate, or Eloquent, you have probably written this exact bug without realizing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it’s an anti-pattern:&lt;/strong&gt;&lt;br&gt;
The N+1 problem occurs when your code fetches a list of records, and then loops through that list to fetch related data for &lt;em&gt;each&lt;/em&gt; record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ The N+1 Disaster in action&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 1 query&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;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This runs a NEW query for every single user! (N queries)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;authorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have 1,000 users, you just hit your database 1,001 times over the network for something that should have been a single round trip. This is the #1 cause of API latency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;JOIN&lt;/code&gt;s or rely on your ORM's eager-loading capabilities to fetch everything in a single optimized query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good: Fetches users and their posts in a single round trip&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersWithPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;&lt;em&gt;Note: Under the hood, this translates to either a &lt;code&gt;LEFT JOIN&lt;/code&gt; or exactly two queries (one for users, one for all posts matching those user IDs via an &lt;code&gt;IN&lt;/code&gt; clause).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Scaling a database isn't just about throwing more RAM and CPU at your cloud provider. It is about respecting the network boundary, understanding your indexes, and keeping a close eye on the SQL your ORM is actually generating. &lt;/p&gt;




&lt;p&gt;&lt;em&gt;What is the worst database performance bug you've ever had to debug? Let me know in the comments! 👇&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>sql</category>
    </item>
    <item>
      <title>🛑 Stop Wasting API Calls: How to Build a Dead-Simple Caching Layer for AI Apps</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Thu, 03 Sep 2026 07:02:59 +0000</pubDate>
      <link>https://dev.to/mindinu/stop-wasting-api-calls-how-to-build-a-dead-simple-caching-layer-for-ai-apps-ngi</link>
      <guid>https://dev.to/mindinu/stop-wasting-api-calls-how-to-build-a-dead-simple-caching-layer-for-ai-apps-ngi</guid>
      <description>&lt;p&gt;Building AI applications is incredibly fun right up until you check your API dashboard and realize you've been burning through credits by sending the exact same prompts during development and testing. &lt;/p&gt;

&lt;p&gt;Whether you are using Claude, Gemini, or OpenAI, rate limits and latency are real bottlenecks. If you are building wrappers, agents, or generation tools, &lt;strong&gt;you need a caching layer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a highly effective, zero-dependency caching wrapper in TypeScript that you can drop into any project today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;Instead of calling the LLM directly, we pass the prompt through a caching function. We hash the prompt (or use it as a key) and check if we already have a response stored. If yes, we return the cached string instantly. If no, we make the expensive network call, save the result, and return it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (TypeScript)
&lt;/h2&gt;

&lt;p&gt;This uses a simple in-memory &lt;code&gt;Map&lt;/code&gt;, which is perfect for local development or single-instance edge functions.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
typescript
// Define an in-memory cache
const responseCache = new Map&amp;lt;string, string&amp;gt;();

async function fetchWithCache(prompt: string): Promise&amp;lt;string&amp;gt; {
  // 1. Check if we already have the exact prompt cached
  if (responseCache.has(prompt)) {
    console.log("⚡ Returning from Cache (0ms)");
    return responseCache.get(prompt)!;
  }

  // 2. If not, make the actual API call (using a generic fetch as an example)
  console.log("☁️ Fetching from API...");
  const response = await fetch("[https://api.your-llm-provider.com/v1/generate](https://api.your-llm-provider.com/v1/generate)", {
    method: "POST",
    headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
    body: JSON.stringify({ prompt })
  });

  const data = await response.json();
  const textResult = data.choices[0].text;

  // 3. Store the result in the cache for next time
  responseCache.set(prompt, textResult);

  return textResult;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>typescript</category>
    </item>
    <item>
      <title>🔌 The 'USB-C of AI': Make Your Own MCP</title>
      <dc:creator>Mindinu Ariyawansha</dc:creator>
      <pubDate>Wed, 02 Sep 2026 14:41:48 +0000</pubDate>
      <link>https://dev.to/mindinu/the-usb-c-of-ai-make-your-own-mcp-568i</link>
      <guid>https://dev.to/mindinu/the-usb-c-of-ai-make-your-own-mcp-568i</guid>
      <description>&lt;p&gt;If you've been building AI-integrated apps recently, you know the pain: every single LLM, agent, and coding assistant needs a custom integration to read your database, check your GitHub repo, or pull Jira tickets. It's an endless cycle of writing custom API glue code.&lt;/p&gt;

&lt;p&gt;Enter the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Originally open-sourced by Anthropic, MCP is rapidly becoming the universal standard for how AI agents talk to data sources. It is quite literally the USB-C of the AI world. &lt;/p&gt;

&lt;p&gt;Here is why MCP is completely changing the modern developer stack—and how you can start using it to turbocharge your workflow today.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 What exactly is MCP?
&lt;/h2&gt;

&lt;p&gt;In simple terms, MCP is an open standard that standardizes how AI models access external context. Instead of building a custom plugin for Claude, a different one for Cursor, and another for your custom Python agent, you build &lt;strong&gt;one MCP Server&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Any MCP-compatible client (like Claude Desktop, Cursor, or your own app) can instantly connect to that server and understand what tools and data are available.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture is simple:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MCP Hosts:&lt;/strong&gt; The application the user interacts with (e.g., Claude Desktop, Cursor IDE).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Clients:&lt;/strong&gt; The protocol layer inside the host application that manages connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Servers:&lt;/strong&gt; Lightweight programs you run locally or in the cloud that expose your data (e.g., a local SQLite database, a GitHub repo, a Slack workspace).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🛠️ The 3 Core Primitives of MCP
&lt;/h2&gt;

&lt;p&gt;When an AI connects to an MCP server, it gets access to three core primitives:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Resources (Read-only data)
&lt;/h3&gt;

&lt;p&gt;Resources are like file systems for AI. They allow the LLM to read data without modifying it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Example:&lt;/em&gt; Giving the AI read access to your local API documentation, system logs, or a Notion workspace. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Tools (Executable actions)
&lt;/h3&gt;

&lt;p&gt;Tools are functions the LLM can call to actually &lt;em&gt;do&lt;/em&gt; things. The server defines the required arguments, and the client prompts the user for permission before executing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Example:&lt;/em&gt; &lt;code&gt;execute_sql_query&lt;/code&gt;, &lt;code&gt;create_github_issue&lt;/code&gt;, or &lt;code&gt;restart_docker_container&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Prompts (Reusable templates)
&lt;/h3&gt;

&lt;p&gt;Pre-defined prompt templates that help users get the most out of the connected data. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Example:&lt;/em&gt; A "Code Review" prompt that automatically pulls the current git diff and asks the LLM to review it against your company's style guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Why this matters for your workflow &lt;em&gt;right now&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;You don't need to be building an AI startup to benefit from MCP. You can use it today to make your local dev environment incredibly powerful.&lt;/p&gt;

&lt;p&gt;Imagine this workflow:&lt;br&gt;
You are debugging an issue in Cursor. Instead of copying and pasting logs from your terminal, you spin up a local &lt;strong&gt;Postgres MCP Server&lt;/strong&gt; and a &lt;strong&gt;Datadog MCP Server&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;You simply ask your AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Look at the recent 500 errors in Datadog, query the users table in my local Postgres to see if their accounts are active, and find the bug in my codebase."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the AI is connected to those MCP servers, it can autonomously fetch the logs, run the SQL query, and fix the code in one seamless interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 Building your first MCP Server
&lt;/h2&gt;

&lt;p&gt;Building a server is surprisingly easy. You can write them in TypeScript or Python. Here is the conceptual skeleton of exposing a simple database tool in TypeScript:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// 1. Initialize the server
const server = new McpServer({
  name: "Local-DB-Server",
  version: "1.0.0"
});

// 2. Add a Tool for the AI to use
server.tool(
  "query_users",
  "Run a search query against the local users database",
  { searchTerm: z.string() },
  async ({ searchTerm }) =&amp;gt; {
    // Run your actual DB logic here
    const results = await mockDbSearch(searchTerm);
    return {
      content: [{ type: "text", text: JSON.stringify(results) }]
    };
  }
);

// 3. Start listening over standard I/O
const transport = new StdioServerTransport();
await server.connect(transport);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>mcp</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
