<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kartik Mehta]]></title><description><![CDATA[Engineering and Product Development harmoniously united.]]></description><link>https://writer.mrmehta.in</link><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 23:20:13 GMT</lastBuildDate><atom:link href="https://writer.mrmehta.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Day 15 Files Became One]]></title><description><![CDATA[Meet Sam. Four months ago, Sam joined a fast-growing document management startup as a backend developer. The company's product processes documents in various formats, JSON, XML, YAML, CSV, and more. Sam's job? Build a robust document parsing system.
...]]></description><link>https://writer.mrmehta.in/the-day-15-files-became-one</link><guid isPermaLink="true">https://writer.mrmehta.in/the-day-15-files-became-one</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[Factory Design Pattern]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sat, 10 Jan 2026 09:41:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768028338010/3de350ca-d3c3-40b3-a188-f6e3d1c1b331.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768029308229/f2d71e92-8b30-4db0-ad52-5e872fa9bfd9.png" alt class="image--center mx-auto" /></p>
<p>Meet <strong>Sam</strong>. Four months ago, Sam joined a fast-growing document management startup as a backend developer. The company's product processes documents in various formats, JSON, XML, YAML, CSV, and more. <strong>Sam's job? Build a robust document parsing system.</strong></p>
<p>Sounds straightforward, right? It was... until it wasn't.</p>
<p>It's Thursday, 4:23 PM. Sam is in a meeting with the product team.</p>
<p><strong>Product Manager (Emma)</strong>: "Great news! We just signed a huge enterprise client. They need support for TOML, INI, and PDF text extraction. Can you add those by Monday?"</p>
<p><strong>Sam</strong> <em>(internally screaming)</em>: "Sure, let me check the code..."</p>
<p>Sam opens the laptop. The color drains from Sam's face.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768029709706/b3e69559-4ed8-4e41-8ab9-a159deafb5e7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-current-code">The Current Code</h2>
<p>Here's what Sam has been dealing with:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// THE NIGHTMARE - Sam's current parser selection code.</span>

<span class="hljs-comment">// In document_service.rs.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_document</span></span>(content: &amp;<span class="hljs-built_in">str</span>, format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;ParsedDoc, Error&gt; {
    <span class="hljs-keyword">let</span> parser: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Parser&gt; = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
        <span class="hljs-built_in">Box</span>::new(JsonParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
        <span class="hljs-built_in">Box</span>::new(XmlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"yaml"</span> {
        <span class="hljs-built_in">Box</span>::new(YamlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"csv"</span> {
        <span class="hljs-built_in">Box</span>::new(CsvParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"markdown"</span> {
        <span class="hljs-built_in">Box</span>::new(MarkdownParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"html"</span> {
        <span class="hljs-built_in">Box</span>::new(HtmlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"toml"</span> {
        <span class="hljs-built_in">Box</span>::new(TomlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"ini"</span> {
        <span class="hljs-built_in">Box</span>::new(IniParser::new())
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(Error::UnsupportedFormat(format.to_string()));
    };

    parser.parse(content)
}

<span class="hljs-comment">// In file_processor.rs - SAME CODE AGAIN!</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process_file</span></span>(path: &amp;Path) -&gt; <span class="hljs-built_in">Result</span>&lt;ParsedDoc, Error&gt; {
    <span class="hljs-keyword">let</span> extension = path.extension()...;
    <span class="hljs-keyword">let</span> parser: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Parser&gt; = <span class="hljs-keyword">if</span> extension == <span class="hljs-string">"json"</span> {
        <span class="hljs-built_in">Box</span>::new(JsonParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> extension == <span class="hljs-string">"xml"</span> {
        <span class="hljs-built_in">Box</span>::new(XmlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> extension == <span class="hljs-string">"yaml"</span> {
        <span class="hljs-built_in">Box</span>::new(YamlParser::new())
    }
    <span class="hljs-comment">// ... DUPLICATED 8 MORE TIMES!</span>
}

<span class="hljs-comment">// In api_handler.rs - SAME CODE THIRD TIME!</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle_upload</span></span>(file: MultipartFile) -&gt; <span class="hljs-built_in">Result</span>&lt;Response&gt; {
    <span class="hljs-keyword">let</span> format = detect_format(&amp;file);
    <span class="hljs-keyword">let</span> parser: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Parser&gt; = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
        <span class="hljs-built_in">Box</span>::new(JsonParser::new())
    }
    <span class="hljs-comment">// ... DUPLICATED AGAIN!</span>
}

<span class="hljs-comment">// In batch_processor.rs - FOURTH TIME!</span>
<span class="hljs-comment">// In validator.rs - FIFTH TIME!</span>
<span class="hljs-comment">// In converter.rs - SIXTH TIME!</span>
<span class="hljs-comment">// ... This pattern repeats in 15+ FILES!</span>
</code></pre>
<p>Let me break down what's happening here for those new to Rust.</p>
<p>The code is trying to create a "parser", a piece of code that reads a document and makes sense of its structure. Since different file formats (JSON, XML, YAML) need different parsing logic, Sam created separate parser types for each one. <strong>The</strong> <code>Box&lt;dyn Parser&gt;</code> <strong>syntax is Rust's way of saying "give me any type that knows how to parse, and I don't care which specific one."</strong> That's actually a good idea!</p>
<p>The problem isn't the concept, it's the execution. Every single file that needs a parser has to include this massive decision tree. Want to parse a document in the API handler? Copy the if-else chain. Need to parse in the batch processor? Copy it again. File validator? You guessed it, copy it a third time. Now imagine adding a new format like TOML. You'd have to hunt down every single copy of this chain and add a new branch. Miss one? That's a bug waiting to happen in production.</p>
<p><em>If you've been coding for any length of time, you've probably felt that sinking feeling Sam is experiencing right now.</em> That moment when you realise the codebase has grown into something unwieldy, and every small change requires touching a dozen files.</p>
<p><strong>Sam's internal monologue:</strong></p>
<blockquote>
<p>"Every time we add a new format, I have to modify FIFTEEN different files. Each with the same giant if-else chain. Last time I added HTML parser, I missed updating it in two places and we had a production bug. How am I supposed to add THREE new formats by Monday?!"</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768030507650/0f61555d-c251-4635-8cf7-fe4d3afa870d.png" alt class="image--center mx-auto" /></p>
<p><strong>The specific horrors:</strong></p>
<ol>
<li><p><strong>Massive Duplication</strong>: Same if-else chain in 15+ files.</p>
</li>
<li><p><strong>Error-Prone</strong>: Easy to miss updating one place.</p>
</li>
<li><p><strong>No Single Source of Truth</strong>: Parser selection logic scattered everywhere.</p>
</li>
<li><p><strong>Hard to Test</strong>: Can't test parser creation independently.</p>
</li>
<li><p><strong>No Extensibility</strong>: Can't add parsers without recompiling.</p>
</li>
<li><p><strong>Maintenance Nightmare</strong>: Every change touches multiple files.</p>
</li>
</ol>
<h2 id="heading-the-parser-chaos">The Parser Chaos</h2>
<p>Let's break down exactly what's wrong with Sam's code. If you're new to software design, this section will help you recognise patterns in your own projects that might be heading toward the same cliff.</p>
<h3 id="heading-problem-1-the-if-else-chain">Problem 1: The If-Else Chain</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// This appears in 15 different files!</span>
<span class="hljs-keyword">let</span> parser: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Parser&gt; = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
    <span class="hljs-built_in">Box</span>::new(JsonParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
    <span class="hljs-built_in">Box</span>::new(XmlParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"yaml"</span> {
    <span class="hljs-built_in">Box</span>::new(YamlParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"csv"</span> {
    <span class="hljs-built_in">Box</span>::new(CsvParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"markdown"</span> {
    <span class="hljs-built_in">Box</span>::new(MarkdownParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"html"</span> {
    <span class="hljs-built_in">Box</span>::new(HtmlParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"toml"</span> {
    <span class="hljs-built_in">Box</span>::new(TomlParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"ini"</span> {
    <span class="hljs-built_in">Box</span>::new(IniParser::new())
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(Error::UnsupportedFormat(format.to_string()));
};
</code></pre>
<p><strong>Problems with this approach:</strong></p>
<ul>
<li><p><strong>Length</strong>: 18 lines just to create a parser.</p>
</li>
<li><p><strong>Duplication</strong>: Repeated 15+ times across the codebase.</p>
</li>
<li><p><strong>Fragility</strong>: Typo in one place equals a bug.</p>
</li>
<li><p><strong>Not Extensible</strong>: Can't add formats without modifying code</p>
</li>
</ul>
<h3 id="heading-problem-2-no-abstraction">Problem 2: No Abstraction</h3>
<p><strong>Every piece of code that needs a parser must know all possible parser types, how to construct each one, the exact string matching logic, and the mapping from format string to parser type.</strong> This violates something called the <strong>Dependency Inversion Principle</strong>, one of the fundamental rules of clean software design. In simple terms, high-level code (like your document service) shouldn't depend on low-level implementation details (like knowing about JsonParser, XmlParser, and every other concrete parser type).</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stackify.com/dependency-inversion-principle/">https://stackify.com/dependency-inversion-principle/</a></div>
<p> </p>
<p>Think of it like this: when you order coffee at a cafe, you don't need to know the exact temperature of the water, the grind size of the beans, or which brand of espresso machine they're using. You just say "I'll have a latte" and the barista handles the details. That's the abstraction we're missing here.</p>
<h3 id="heading-problem-3-testing">Problem 3: Testing</h3>
<pre><code class="lang-rust"><span class="hljs-meta">#[test]</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_parse_json</span></span>() {
    <span class="hljs-comment">// To test, must include full if-else chain.</span>
    <span class="hljs-keyword">let</span> parser = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
        <span class="hljs-built_in">Box</span>::new(JsonParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
        <span class="hljs-comment">// ... need entire chain!</span>
    };

    <span class="hljs-comment">// Can't easily mock or stub.</span>
    <span class="hljs-comment">// Can't test parser creation in isolation.</span>
}
</code></pre>
<p>When you can't test something easily, you tend not to test it at all. And untested code is a ticking time bomb waiting to explode in production.</p>
<h3 id="heading-the-real-world-impact">The Real-World Impact</h3>
<p>Sam keeps a log of incidents:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Week 1:</span> <span class="hljs-string">Added</span> <span class="hljs-string">HTML</span> <span class="hljs-string">parser,</span> <span class="hljs-string">forgot</span> <span class="hljs-string">to</span> <span class="hljs-string">update</span> <span class="hljs-string">batch_processor.rs</span>
        <span class="hljs-string">→</span> <span class="hljs-string">Batch</span> <span class="hljs-string">jobs</span> <span class="hljs-string">crashed,</span> <span class="hljs-number">2</span> <span class="hljs-string">hours</span> <span class="hljs-string">downtime</span>

<span class="hljs-attr">Week 2:</span> <span class="hljs-string">Typo</span> <span class="hljs-string">"yml"</span> <span class="hljs-string">vs</span> <span class="hljs-string">"yaml"</span> <span class="hljs-string">in</span> <span class="hljs-string">one</span> <span class="hljs-string">file</span> <span class="hljs-string">caused</span> <span class="hljs-string">inconsistent</span> <span class="hljs-string">behavior</span>
        <span class="hljs-string">→</span> <span class="hljs-string">Customer</span> <span class="hljs-string">complaints,</span> <span class="hljs-string">had</span> <span class="hljs-string">to</span> <span class="hljs-string">hotfix</span>

<span class="hljs-attr">Week 3:</span> <span class="hljs-string">Added</span> <span class="hljs-string">TOML</span> <span class="hljs-string">parser,</span> <span class="hljs-string">updated</span> <span class="hljs-number">14</span> <span class="hljs-string">files,</span> <span class="hljs-string">missed</span> <span class="hljs-number">1</span>
        <span class="hljs-string">→</span> <span class="hljs-string">CLI</span> <span class="hljs-string">tool</span> <span class="hljs-string">couldn't</span> <span class="hljs-string">parse</span> <span class="hljs-string">TOML,</span> <span class="hljs-string">support</span> <span class="hljs-string">tickets</span>

<span class="hljs-attr">Week 4:</span> <span class="hljs-string">Tried</span> <span class="hljs-string">to</span> <span class="hljs-string">write</span> <span class="hljs-string">unit</span> <span class="hljs-string">test,</span> <span class="hljs-string">realized</span> <span class="hljs-string">it's</span> <span class="hljs-string">impossible</span> <span class="hljs-string">to</span> <span class="hljs-string">test</span>
        <span class="hljs-string">→</span> <span class="hljs-literal">No</span> <span class="hljs-string">tests</span> <span class="hljs-string">=</span> <span class="hljs-string">more</span> <span class="hljs-string">bugs</span> <span class="hljs-string">in</span> <span class="hljs-string">production</span>

<span class="hljs-attr">Week 5:</span> <span class="hljs-string">Boss</span> <span class="hljs-string">wants</span> <span class="hljs-number">3</span> <span class="hljs-string">new</span> <span class="hljs-string">formats</span> <span class="hljs-string">by</span> <span class="hljs-string">Monday</span>
        <span class="hljs-string">→</span> <span class="hljs-string">Sam</span> <span class="hljs-string">considers</span> <span class="hljs-string">quitting</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768031382988/22d8d672-35be-48dc-8b25-435ba7eb9843.png" alt class="image--center mx-auto" /></p>
<p><strong>What's cyclomatic complexity?</strong> It's a fancy way of measuring how many different paths your code can take. Each <code>if</code> or <code>else</code> branch adds to the complexity. A cyclomatic complexity of 9 means there are 9 different paths through that function which means 9 different ways things can go wrong, and 9 different scenarios you need to test. That's way too high for what should be a simple "give me the right parser" operation.</p>
<blockquote>
<p>"I'm not solving problems anymore. I'm just copying and pasting if-else statements. There has to be a better way!"</p>
</blockquote>
<h2 id="heading-the-breaking-point">The Breaking Point</h2>
<p>Friday morning. Sam is called into a meeting with Emma (Product Manager) and the CTO, David.</p>
<p><strong>Emma</strong>: "Sam, I know we just talked about adding three new formats, but I have more news..."</p>
<p><strong>Sam</strong> <em>(nervously)</em>: "More?"</p>
<p><strong>Emma</strong>: "Our new enterprise client uses 12 different document formats. They need support for all of them. Can we…"</p>
<p><strong>Sam</strong> <em>(interrupting)</em>: "No. We can't. Not with the current architecture."</p>
<p><strong>David</strong>: "What do you mean?"</p>
<p><strong>Sam</strong> <em>(frustrated)</em>: "Every time we add a new format, I have to modify 15 different files. Each one has the same giant if-else chain. It takes hours, it's error-prone, and I've already introduced bugs three times this month."</p>
<p>Sam opens the laptop and shows them the code.</p>
<p><strong>Sam</strong>: "Look at this. This if-else chain is duplicated everywhere. Adding your 12 formats means updating it 15 times per format. That's 180 places I have to modify. And if I miss even ONE, we get production bugs."</p>
<p><strong>David</strong> <em>(understanding dawning)</em>: "Oh. That's... not good."</p>
<p><strong>Emma</strong>: "Is there a way to fix this?"</p>
<p><strong>David</strong>: "Yes. We need to refactor. Sam, have you heard of the <strong>Factory Pattern?</strong>"</p>
<p><strong>Sam</strong>: "Factory? Like a factory that makes things?"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768034428883/9de90c2b-1eec-4485-9bab-27a964a21227.png" alt class="image--center mx-auto" /></p>
<p>David draws on the whiteboard:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768034614427/2a7c45ca-9985-4993-9d17-e90ea86fbe10.png" alt class="image--center mx-auto" /></p>
<p><strong>Sam</strong>: "So instead of everyone knowing how to create parsers, they just ask the factory?"</p>
<p><strong>David</strong>: "Right! And we can make it even better with a registry. That way, you can add new parsers without even modifying the factory."</p>
<p><strong>Sam</strong> <em>(excited)</em>: "So adding the 12 new formats would just mean creating 12 new parser structs and registering them?"</p>
<p><strong>David</strong>: "Exactly. No changes to existing code."</p>
<p><strong>Emma</strong>: "How long would this refactoring take?"</p>
<p><strong>David</strong>: "A few days for the factory. But then adding new formats becomes trivial, <strong>maybe 30 minutes each.</strong>"</p>
<p><strong>Sam</strong>: "Let's do it. I can't keep maintaining this mess."</p>
<h2 id="heading-enter-the-factory-pattern">Enter the Factory Pattern</h2>
<p>Sam spends the weekend reading about the Factory Pattern. Monday morning, armed with coffee and determination, Sam is ready to refactor.</p>
<h3 id="heading-what-is-the-factory-pattern">What is the Factory Pattern?</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://refactoring.guru/design-patterns/factory-method">https://refactoring.guru/design-patterns/factory-method</a></div>
<p> </p>
<p>The Factory Pattern is what we call a <strong>creational design pattern</strong>. Design patterns are essentially battle-tested solutions to common programming problems, recipes that generations of developers have refined over decades. The Factory Pattern specifically deals with the problem of creating objects.</p>
<p><strong>Here's the core insight:</strong> instead of scattering object creation code throughout your application, you centralise it in one place. That "one place" is the factory.</p>
<h3 id="heading-the-core-idea">The Core Idea</h3>
<p>Instead of this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Client</span> <span class="hljs-string">Code</span> <span class="hljs-string">→</span> <span class="hljs-string">Directly</span> <span class="hljs-string">creates</span> <span class="hljs-string">object</span>
           <span class="hljs-string">→</span> <span class="hljs-string">Knows</span> <span class="hljs-string">about</span> <span class="hljs-string">all</span> <span class="hljs-string">concrete</span> <span class="hljs-string">types</span>
           <span class="hljs-string">→</span> <span class="hljs-string">Scattered</span> <span class="hljs-string">creation</span> <span class="hljs-string">logic</span>
</code></pre>
<p>We do this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Client</span> <span class="hljs-string">Code</span> <span class="hljs-string">→</span> <span class="hljs-string">Asks</span> <span class="hljs-string">factory</span> <span class="hljs-string">for</span> <span class="hljs-string">object</span>
<span class="hljs-string">Factory</span> <span class="hljs-string">→</span> <span class="hljs-string">Creates</span> <span class="hljs-string">the</span> <span class="hljs-string">right</span> <span class="hljs-string">type</span>
       <span class="hljs-string">→</span> <span class="hljs-string">Centralizes</span> <span class="hljs-string">creation</span> <span class="hljs-string">logic</span>
       <span class="hljs-string">→</span> <span class="hljs-string">Hides</span> <span class="hljs-string">concrete</span> <span class="hljs-string">types</span>
</code></pre>
<p>It's like the difference between cooking at home versus ordering from a restaurant. When you cook at home, you need to know all the recipes, have all the ingredients, and understand every technique. When you order from a restaurant, you just say what you want and the kitchen handles everything.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768034931896/f0f89e64-48d5-4081-b820-a6a14126e963.png" alt class="image--center mx-auto" /></p>
<p>Let's break down what each piece does:</p>
<ol>
<li><p><strong>Product Interface</strong> (DocumentParser): This is a <strong>trait</strong> in Rust (similar to an interface in other languages). It defines a contract, "here's what all parsers must be able to do." Every parser promises to implement <code>parse()</code>, <code>name()</code>, and <code>supported_extensions()</code>.</p>
</li>
<li><p><strong>Concrete Products</strong> (JsonParser, XmlParser, etc.): These are the actual implementations. Each one knows how to parse its specific format. JsonParser knows JSON, XmlParser knows XML, and so on.</p>
</li>
<li><p><strong>Factory</strong> (ParserFactory): This is the star of the show. <mark>It takes a format string like "json" and returns the appropriate parser.</mark> Client code never needs to know which concrete type it's getting, it just works with the trait.</p>
</li>
<li><p><strong>Client</strong>: This is any code that needs a parser. It asks the factory, gets a parser, and uses it. Simple.</p>
</li>
</ol>
<h3 id="heading-real-world-analogy">Real-World Analogy</h3>
<p><strong>David's explanation:</strong></p>
<blockquote>
<p>"Think of a car factory. You walk in and say 'I want a sedan.' You don't need to know:</p>
<ul>
<li><p>How to build a sedan</p>
</li>
<li><p>What parts it needs</p>
</li>
<li><p>The assembly process</p>
</li>
</ul>
<p>The factory handles all that. You just get your car.</p>
<p>Same with parser factory. You say 'I want a JSON parser.' You don't need to know:</p>
<ul>
<li><p>How to construct it</p>
</li>
<li><p>What dependencies it has</p>
</li>
<li><p>Implementation details</p>
</li>
</ul>
<p>The factory gives you a parser. You just use it."</p>
</blockquote>
<h2 id="heading-factory-pattern-amp-our-code">Factory Pattern &amp; Our Code</h2>
<p>Let's dive deeper into how the Factory Pattern works and why it solves Sam's problem.</p>
<h3 id="heading-before-vs-after">Before vs After</h3>
<p><strong>BEFORE (Without Factory):</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// In document_service.rs.</span>
<span class="hljs-keyword">let</span> parser = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
    <span class="hljs-built_in">Box</span>::new(JsonParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
    <span class="hljs-built_in">Box</span>::new(XmlParser::new())
}
<span class="hljs-comment">// ... 8 more branches.</span>

<span class="hljs-comment">// In file_processor.rs.</span>
<span class="hljs-keyword">let</span> parser = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {  <span class="hljs-comment">// DUPLICATED!</span>
    <span class="hljs-built_in">Box</span>::new(JsonParser::new())
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
    <span class="hljs-built_in">Box</span>::new(XmlParser::new())
}
<span class="hljs-comment">// ... 8 more branches.</span>

<span class="hljs-comment">// In api_handler.rs.</span>
<span class="hljs-keyword">let</span> parser = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {  <span class="hljs-comment">// DUPLICATED AGAIN!</span>
    <span class="hljs-built_in">Box</span>::new(JsonParser::new())
}
<span class="hljs-comment">// ... you get the idea.</span>
</code></pre>
<p><strong>AFTER (With Factory):</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// In document_service.rs</span>
<span class="hljs-keyword">let</span> parser = ParserFactory::create(format)?;

<span class="hljs-comment">// In file_processor.rs</span>
<span class="hljs-keyword">let</span> parser = ParserFactory::create(format)?;  <span class="hljs-comment">// Same call, but OK!</span>

<span class="hljs-comment">// In api_handler.rs</span>
<span class="hljs-keyword">let</span> parser = ParserFactory::create(format)?;  <span class="hljs-comment">// Consistent!</span>
</code></pre>
<p><strong>From 18 lines per file to 1 line per file!</strong></p>
<p>The magic here isn't just that the code is shorter, it's that every file now does the same thing in the same way. <mark>If you need to change how parsers are created, you change it in ONE place, and the change propagates everywhere automatically.</mark></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768035369210/a20d6ad2-1338-418d-a71f-407ccce092f1.png" alt class="image--center mx-auto" /></p>
<p>This diagram shows the dance that happens when you request a parser. The client never talks directly to JsonParser or XmlParser, it only talks to the Factory. The Factory is the matchmaker, connecting requests to the right implementations.</p>
<h3 id="heading-key-benefits">Key Benefits</h3>
<ol>
<li><p><strong>Centralised Creation</strong>: One place to modify when adding or changing parsers.</p>
</li>
<li><p><strong>Encapsulation</strong>: Hides construction complexity from client code.</p>
</li>
<li><p><strong>Flexibility</strong>: Easy to change what's created without affecting callers.</p>
</li>
<li><p><strong>Testability</strong>: Can mock the factory for unit tests.</p>
</li>
<li><p><strong>Maintainability</strong>: Changes don't ripple across the codebase.</p>
</li>
</ol>
<h3 id="heading-the-solid-principles">The SOLID Principles</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768035729154/3f99376b-68cd-4942-9d79-a59016f56ef2.png" alt class="image--center mx-auto" /></p>
<p>If you're not familiar with SOLID, these are five principles of object-oriented design that help create maintainable software. The Factory Pattern naturally supports several of them:</p>
<p><strong>1. Single Responsibility Principle</strong></p>
<p>Each piece of code should do one thing and do it well:</p>
<ul>
<li><p>Factory's only job: create parsers</p>
</li>
<li><p>Parser's only job: parse documents</p>
</li>
<li><p>Clear separation of concerns</p>
</li>
</ul>
<p><strong>2. Open/Closed Principle</strong></p>
<p>Software should be open for extension but closed for modification. With our factory:</p>
<ul>
<li><p>Open for extension: we can add new parsers</p>
</li>
<li><p>Closed for modification: we don't need to change existing client code</p>
</li>
<li><p>This is achieved through the registry pattern (coming up!)</p>
</li>
</ul>
<p><strong>3. Dependency Inversion Principle</strong></p>
<p>High-level code shouldn't depend on low-level details:</p>
<ul>
<li><p>Client depends on the Parser trait (abstraction)</p>
</li>
<li><p>Not on JsonParser, XmlParser (concrete types)</p>
</li>
<li><p>Factory bridges the gap</p>
</li>
</ul>
<h2 id="heading-the-solution">The Solution</h2>
<p>"Alright," Sam says, opening a fresh terminal. "Let's build this factory!"</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/document-parser-api">https://github.com/kartikmehta8/document-parser-api</a></div>
<p> </p>
<h3 id="heading-step-1-define-the-parser-trait">Step 1: Define the Parser Trait</h3>
<p>First, we need the interface that all parsers will implement. In Rust, we use a <strong>trait</strong> for this, it's like a contract that says "any type that wants to be a parser must provide these capabilities."</p>
<p>Create <code>src/parser_trait.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::error::Error;

<span class="hljs-comment">/// The core Parser trait - all parsers implement this.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// This is the "Product" interface in Factory Pattern terminology.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DocumentParser</span></span>: <span class="hljs-built_in">Send</span> + <span class="hljs-built_in">Sync</span> {
    <span class="hljs-comment">/// Parse the document content and return structured data.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(&amp;<span class="hljs-keyword">self</span>, content: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;serde_json::Value, <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Error&gt;&gt;;

    <span class="hljs-comment">/// Get the parser name. (e.g., "JSON", "XML")</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;

    <span class="hljs-comment">/// Get supported file extensions. (e.g., ["json", "jsonl"])</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">supported_extensions</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;&amp;<span class="hljs-built_in">str</span>&gt;;

    <span class="hljs-comment">/// Get a human-readable description.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;
}
</code></pre>
<p><strong>What's happening here?</strong></p>
<p>The <code>DocumentParser</code> trait defines four methods that every parser must implement. The <code>Send + Sync</code> bounds are Rust-specific, they tell the compiler that parsers can be safely shared between threads. This matters because our API might handle multiple requests simultaneously.</p>
<p>The <code>parse</code> method returns <code>Result&lt;serde_json::Value, Box&lt;dyn Error&gt;&gt;</code>. In Rust, <code>Result</code> is how we handle operations that might fail. It's either <code>Ok(value)</code> for success or <code>Err(error)</code> for failure. <code>serde_json::Value</code> is a flexible type that can represent any JSON structure, perfect for our parsed documents.</p>
<p><strong>Sam's note:</strong> "<mark>This trait defines what ALL parsers must do. Any parser can be used interchangeably because they all implement this interface.</mark> That's the power of abstraction!"</p>
<h3 id="heading-step-2-create-concrete-parsers">Step 2: Create Concrete Parsers</h3>
<p>Now let's build the actual parsers. Each one implements the trait in its own way.</p>
<p>Create <code>src/parsers/json_parser.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::parser_trait::DocumentParser;
<span class="hljs-keyword">use</span> std::error::Error;

<span class="hljs-comment">/// JSON document parser.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">JsonParser</span></span>;

<span class="hljs-keyword">impl</span> JsonParser {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span>
    }
}

<span class="hljs-keyword">impl</span> DocumentParser <span class="hljs-keyword">for</span> JsonParser {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(&amp;<span class="hljs-keyword">self</span>, content: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;serde_json::Value, <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Error&gt;&gt; {
        <span class="hljs-comment">// Parse JSON using serde_json.</span>
        <span class="hljs-keyword">let</span> value: serde_json::Value = serde_json::from_str(content)?;
        <span class="hljs-literal">Ok</span>(value)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"JSON"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">supported_extensions</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;&amp;<span class="hljs-built_in">str</span>&gt; {
        <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"json"</span>, <span class="hljs-string">"jsonl"</span>]
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Parses JSON (JavaScript Object Notation) documents"</span>
    }
}
</code></pre>
<p>Notice how simple this is! The <code>JsonParser</code> struct is actually empty, it's what Rust calls a "zero-sized type." It doesn't need to store any data; it just provides the parsing behaviour.</p>
<p>The <code>?</code> operator in <code>serde_json::from_str(content)?</code> is Rust's way of propagating errors. If parsing fails, the error automatically gets returned to the caller. No try-catch blocks needed.</p>
<p>Let's create a YAML parser too:</p>
<p>Create <code>src/parsers/yaml_parser.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::parser_trait::DocumentParser;
<span class="hljs-keyword">use</span> std::error::Error;

<span class="hljs-comment">/// YAML document parser.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">YamlParser</span></span>;

<span class="hljs-keyword">impl</span> YamlParser {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span>
    }
}

<span class="hljs-keyword">impl</span> DocumentParser <span class="hljs-keyword">for</span> YamlParser {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(&amp;<span class="hljs-keyword">self</span>, content: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;serde_json::Value, <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Error&gt;&gt; {
        <span class="hljs-comment">// Parse YAML and convert to JSON value.</span>
        <span class="hljs-keyword">let</span> value: serde_json::Value = serde_yaml::from_str(content)?;
        <span class="hljs-literal">Ok</span>(value)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"YAML"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">supported_extensions</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;&amp;<span class="hljs-built_in">str</span>&gt; {
        <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"yaml"</span>, <span class="hljs-string">"yml"</span>]
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Parses YAML (YAML Ain't Markup Language) documents"</span>
    }
}
</code></pre>
<p><strong>Sam:</strong> "Each parser is now a standalone module. They don't know about each other. Perfect separation of concerns!"</p>
<h3 id="heading-step-3-create-the-factory">Step 3: Create the Factory</h3>
<p>Now for the main event, the factory itself! This is where the magic happens.</p>
<p>Create <code>src/factory.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::parser_trait::DocumentParser;
<span class="hljs-keyword">use</span> crate::parsers::*;
<span class="hljs-keyword">use</span> std::sync::Arc;

<span class="hljs-comment">/// Parser Factory - Creates parsers based on format.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// This is the FACTORY in Factory Pattern terminology.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParserFactory</span></span>;

<span class="hljs-keyword">impl</span> ParserFactory {
    <span class="hljs-comment">/// Create a parser based on format string.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// This is a STATIC FACTORY METHOD - no instance needed.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">create</span></span>(format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;, <span class="hljs-built_in">String</span>&gt; {
        <span class="hljs-keyword">match</span> format.to_lowercase().as_str() {
            <span class="hljs-string">"json"</span> | <span class="hljs-string">"jsonl"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(JsonParser::new())),
            <span class="hljs-string">"xml"</span> | <span class="hljs-string">"xhtml"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(XmlParser::new())),
            <span class="hljs-string">"yaml"</span> | <span class="hljs-string">"yml"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(YamlParser::new())),
            <span class="hljs-string">"csv"</span> | <span class="hljs-string">"tsv"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(CsvParser::new())),
            <span class="hljs-string">"md"</span> | <span class="hljs-string">"markdown"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(MarkdownParser::new())),
            _ =&gt; <span class="hljs-literal">Err</span>(<span class="hljs-built_in">format!</span>(<span class="hljs-string">"Unsupported format: {}"</span>, format)),
        }
    }

    <span class="hljs-comment">/// Create parser from filename extension.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">create_from_filename</span></span>(filename: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;, <span class="hljs-built_in">String</span>&gt; {
        <span class="hljs-keyword">let</span> extension = filename
            .rsplit(<span class="hljs-string">'.'</span>)
            .next()
            .ok_or_else(|| <span class="hljs-string">"No file extension found"</span>.to_string())?;

        Self::create(extension)
    }
}
</code></pre>
<p><strong>What just happened?</strong></p>
<p>Let's break this down piece by piece:</p>
<ul>
<li><p><code>match</code> expression: This replaces the ugly if-else chain. Rust's <code>match</code> is exhaustive, the compiler ensures you handle all cases. The <code>_</code> arm catches anything not explicitly matched.</p>
</li>
<li><p><code>format.to_lowercase()</code>: We normalise the input so "JSON", "Json", and "json" all work the same way. Small detail, big usability improvement.</p>
</li>
<li><p><code>Arc&lt;dyn DocumentParser&gt;</code>: <code>Arc</code> stands for "<a target="_blank" href="https://doc.rust-lang.org/std/sync/struct.Arc.html">Atomic Reference Counted</a>." It's a smart pointer that allows multiple parts of your code to share ownership of the same parser safely. The <code>dyn DocumentParser</code> means "any type that implements DocumentParser", that's how we achieve polymorphism in Rust.</p>
</li>
<li><p><code>create_from_filename</code>: A convenience method. If someone has a file path like "data.json", this extracts "json" and creates the right parser.</p>
</li>
</ul>
<p><strong>The transformation:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// Before: 18 lines of if-else per file.</span>
<span class="hljs-comment">// After: 1 line.</span>
<span class="hljs-keyword">let</span> parser = ParserFactory::create(<span class="hljs-string">"json"</span>)?;
</code></pre>
<p><strong>Sam</strong> <em>(amazed)</em>: "That's it? That's the whole factory?"</p>
<p><strong>David</strong>: "Yep! Simple, clean, and now there's only ONE place to update when adding new formats."</p>
<h3 id="heading-step-4-using-the-factory">Step 4: Using the Factory</h3>
<p>Now let's update the document service to use our shiny new factory:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// BEFORE (the nightmare):</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_document</span></span>(content: &amp;<span class="hljs-built_in">str</span>, format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;ParsedDoc, Error&gt; {
    <span class="hljs-keyword">let</span> parser: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Parser&gt; = <span class="hljs-keyword">if</span> format == <span class="hljs-string">"json"</span> {
        <span class="hljs-built_in">Box</span>::new(JsonParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"xml"</span> {
        <span class="hljs-built_in">Box</span>::new(XmlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"yaml"</span> {
        <span class="hljs-built_in">Box</span>::new(YamlParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"csv"</span> {
        <span class="hljs-built_in">Box</span>::new(CsvParser::new())
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> format == <span class="hljs-string">"markdown"</span> {
        <span class="hljs-built_in">Box</span>::new(MarkdownParser::new())
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(Error::UnsupportedFormat(format.to_string()));
    };

    parser.parse(content)
}

<span class="hljs-comment">// AFTER (clean and beautiful):</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_document</span></span>(content: &amp;<span class="hljs-built_in">str</span>, format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;ParsedDoc, Error&gt; {
    <span class="hljs-keyword">let</span> parser = ParserFactory::create(format)
        .map_err(|e| Error::UnsupportedFormat(e))?;

    parser.parse(content).map_err(Error::from)
}
</code></pre>
<p><strong>From 18 lines to 5 lines!</strong> And this same transformation happens in ALL 15 files that needed parser creation.</p>
<h3 id="heading-step-5-testing-the-factory">Step 5: Testing the Factory</h3>
<p>One of the biggest wins with the Factory Pattern is testability. Now we can test parser creation independently:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[cfg(test)]</span>
<span class="hljs-keyword">mod</span> tests {
    <span class="hljs-keyword">use</span> super::*;

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_factory_creates_json_parser</span></span>() {
        <span class="hljs-keyword">let</span> parser = ParserFactory::create(<span class="hljs-string">"json"</span>).unwrap();
        <span class="hljs-built_in">assert_eq!</span>(parser.name(), <span class="hljs-string">"JSON"</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_factory_creates_yaml_parser</span></span>() {
        <span class="hljs-keyword">let</span> parser = ParserFactory::create(<span class="hljs-string">"yaml"</span>).unwrap();
        <span class="hljs-built_in">assert_eq!</span>(parser.name(), <span class="hljs-string">"YAML"</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_factory_handles_case_insensitivity</span></span>() {
        <span class="hljs-keyword">let</span> parser1 = ParserFactory::create(<span class="hljs-string">"JSON"</span>).unwrap();
        <span class="hljs-keyword">let</span> parser2 = ParserFactory::create(<span class="hljs-string">"json"</span>).unwrap();
        <span class="hljs-built_in">assert_eq!</span>(parser1.name(), parser2.name());
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_factory_from_filename</span></span>() {
        <span class="hljs-keyword">let</span> parser = ParserFactory::create_from_filename(<span class="hljs-string">"data.json"</span>).unwrap();
        <span class="hljs-built_in">assert_eq!</span>(parser.name(), <span class="hljs-string">"JSON"</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_factory_unsupported_format</span></span>() {
        <span class="hljs-keyword">let</span> result = ParserFactory::create(<span class="hljs-string">"unsupported"</span>);
        <span class="hljs-built_in">assert!</span>(result.is_err());
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_parse_json</span></span>() {
        <span class="hljs-keyword">let</span> parser = ParserFactory::create(<span class="hljs-string">"json"</span>).unwrap();
        <span class="hljs-keyword">let</span> content = <span class="hljs-string">r#"{"name": "test", "value": 42}"#</span>;
        <span class="hljs-keyword">let</span> result = parser.parse(content);
        <span class="hljs-built_in">assert!</span>(result.is_ok());
    }
}
</code></pre>
<p><strong>Sam:</strong> "Look! I can test the factory independently! And each parser independently! This was impossible before!"</p>
<h2 id="heading-the-registry-pattern">The Registry Pattern</h2>
<p><strong>David</strong>: "Sam, the factory is great. But we can make it even better with a registry."</p>
<p><strong>Sam</strong>: "A registry?"</p>
<p><strong>David</strong>: "Right now, to add a new parser, you still have to modify the factory's match statement. What if we could register parsers dynamically?"</p>
<h3 id="heading-the-problem-with-the-simple-factory">The Problem with the Simple Factory</h3>
<p>Even with our nice factory, adding a new parser requires changing the factory code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// To add TOML parser, must modify this:</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">create</span></span>(format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;, <span class="hljs-built_in">String</span>&gt; {
    <span class="hljs-keyword">match</span> format.to_lowercase().as_str() {
        <span class="hljs-string">"json"</span> | <span class="hljs-string">"jsonl"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(JsonParser::new())),
        <span class="hljs-string">"xml"</span> | <span class="hljs-string">"xhtml"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(XmlParser::new())),
        <span class="hljs-comment">// ... existing parsers ...</span>
        <span class="hljs-string">"toml"</span> =&gt; <span class="hljs-literal">Ok</span>(Arc::new(TomlParser::new())),  <span class="hljs-comment">// NEW! Must edit factory.</span>
        _ =&gt; <span class="hljs-literal">Err</span>(<span class="hljs-built_in">format!</span>(<span class="hljs-string">"Unsupported format: {}"</span>, format)),
    }
}
</code></pre>
<p>It's way better than before, but can we do even better?</p>
<h3 id="heading-the-registry-solution">The Registry Solution</h3>
<p>A registry is like a phone book for parsers. Instead of hardcoding which parsers exist, we maintain a dynamic collection that can be modified at runtime.</p>
<p>Create <code>src/registry.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::parser_trait::DocumentParser;
<span class="hljs-keyword">use</span> once_cell::sync::Lazy;
<span class="hljs-keyword">use</span> std::collections::HashMap;
<span class="hljs-keyword">use</span> std::sync::{Arc, RwLock};

<span class="hljs-comment">/// Parser Registry - Extensible parser registration system.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// This allows adding new parsers WITHOUT modifying the factory!</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParserRegistry</span></span> {
    parsers: RwLock&lt;HashMap&lt;<span class="hljs-built_in">String</span>, Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;&gt;&gt;,
}

<span class="hljs-keyword">impl</span> ParserRegistry {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            parsers: RwLock::new(HashMap::new()),
        }
    }

    <span class="hljs-comment">/// Register a new parser for a given format.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">register</span></span>(&amp;<span class="hljs-keyword">self</span>, format: <span class="hljs-built_in">String</span>, parser: Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;) {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> parsers = <span class="hljs-keyword">self</span>.parsers.write().unwrap();
        parsers.insert(format.to_lowercase(), parser);
    }

    <span class="hljs-comment">/// Get a parser by format name.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get</span></span>(&amp;<span class="hljs-keyword">self</span>, format: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;&gt; {
        <span class="hljs-keyword">let</span> parsers = <span class="hljs-keyword">self</span>.parsers.read().unwrap();
        parsers.get(&amp;format.to_lowercase()).cloned()
    }

    <span class="hljs-comment">/// List all registered parsers.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">list_all</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;(<span class="hljs-built_in">String</span>, Arc&lt;<span class="hljs-keyword">dyn</span> DocumentParser&gt;)&gt; {
        <span class="hljs-keyword">let</span> parsers = <span class="hljs-keyword">self</span>.parsers.read().unwrap();
        parsers
            .iter()
            .map(|(k, v)| (k.clone(), Arc::clone(v)))
            .collect()
    }
}

<span class="hljs-comment">/// Global parser registry singleton.</span>
<span class="hljs-keyword">static</span> PARSER_REGISTRY: Lazy&lt;ParserRegistry&gt; = Lazy::new(|| {
    <span class="hljs-keyword">let</span> registry = ParserRegistry::new();

    <span class="hljs-comment">// Pre-register default parsers.</span>
    <span class="hljs-keyword">use</span> crate::parsers::*;
    registry.register(<span class="hljs-string">"json"</span>.to_string(), Arc::new(JsonParser::new()));
    registry.register(<span class="hljs-string">"xml"</span>.to_string(), Arc::new(XmlParser::new()));
    registry.register(<span class="hljs-string">"yaml"</span>.to_string(), Arc::new(YamlParser::new()));
    registry.register(<span class="hljs-string">"csv"</span>.to_string(), Arc::new(CsvParser::new()));
    registry.register(<span class="hljs-string">"markdown"</span>.to_string(), Arc::new(MarkdownParser::new()));

    registry
});

<span class="hljs-comment">/// Get the global parser registry.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_registry</span></span>() -&gt; &amp;<span class="hljs-symbol">'static</span> ParserRegistry {
    &amp;PARSER_REGISTRY
}
</code></pre>
<p><strong>Let's unpack what's happening here:</strong></p>
<p>The <code>RwLock&lt;HashMap&lt;...&gt;&gt;</code> is a thread-safe way to store our parsers. <code>RwLock</code> allows multiple readers OR one writer at a time, perfect for a registry that's read often but written rarely.</p>
<p><code>Lazy</code> from the <code>once_cell</code> crate gives us lazy initialisation. The registry isn't created until the first time someone calls <code>get_registry()</code>. After that, the same instance is reused forever. This is the <strong>Singleton pattern</strong> working alongside our Factory pattern.</p>
<p>The default parsers are registered when the registry is first accessed. But here's the key insight: you can call <code>register()</code> at any time to add more parsers!</p>
<h3 id="heading-using-the-registry">Using the Registry</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// Get parser from registry.</span>
<span class="hljs-keyword">let</span> parser = get_registry()
    .get(<span class="hljs-string">"json"</span>)
    .ok_or(<span class="hljs-string">"Parser not found"</span>)?;

<span class="hljs-comment">// Add new parser WITHOUT modifying existing code!</span>
get_registry().register(
    <span class="hljs-string">"toml"</span>.to_string(),
    Arc::new(TomlParser::new())
);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768036647532/c8179385-d06d-48a0-9ad5-e903dea5bcb8.png" alt class="image--center mx-auto" /></p>
<p><strong>Sam</strong> <em>(excited)</em>: "So to add TOML, I just create TomlParser and register it? No changes to factory OR client code?"</p>
<p><strong>David</strong>: "Exactly! You could even load parsers from plugins at runtime if you wanted!"</p>
<h3 id="heading-adding-a-new-parser">Adding a New Parser</h3>
<p>Here's the complete process for adding TOML support:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Step 1: Create the parser. (new file: src/parsers/toml_parser.rs)</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TomlParser</span></span>;

<span class="hljs-keyword">impl</span> DocumentParser <span class="hljs-keyword">for</span> TomlParser {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(&amp;<span class="hljs-keyword">self</span>, content: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;serde_json::Value, <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> Error&gt;&gt; {
        <span class="hljs-keyword">let</span> value: toml::Value = toml::from_str(content)?;
        <span class="hljs-comment">// Convert TOML value to JSON value for consistency.</span>
        <span class="hljs-keyword">let</span> json = serde_json::to_value(value)?;
        <span class="hljs-literal">Ok</span>(json)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"TOML"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">supported_extensions</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;&amp;<span class="hljs-built_in">str</span>&gt; {
        <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"toml"</span>]
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Parses TOML (Tom's Obvious, Minimal Language) configuration files"</span>
    }
}

<span class="hljs-comment">// Step 2: Register it. (in main.rs or initialization code)</span>
get_registry().register(<span class="hljs-string">"toml"</span>.to_string(), Arc::new(TomlParser::new()));

<span class="hljs-comment">// Step 3: Done! No other changes needed!</span>
</code></pre>
<p><strong>That's it!</strong> No modifications to:</p>
<ul>
<li><p>Factory code</p>
</li>
<li><p>Client code</p>
</li>
<li><p>Other parsers</p>
</li>
<li><p>API handlers</p>
</li>
<li><p>Tests</p>
</li>
</ul>
<p><strong>Just:</strong></p>
<ul>
<li><p>Create new parser</p>
</li>
<li><p>Register it</p>
</li>
<li><p>It works everywhere!</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768036981167/94dffe6b-460a-4820-8bfa-63f15a21b40e.png" alt class="image--center mx-auto" /></p>
<p><strong>Sam</strong> <em>(amazed)</em>: "This is incredible! Emma wanted 12 new formats. With the registry, I can add all 12 without touching ANY existing code!"</p>
<h2 id="heading-the-resolution">The Resolution</h2>
<h3 id="heading-monday-morning-the-demo">Monday Morning - The Demo</h3>
<p>Sam calls a meeting with Emma and David to show the new system.</p>
<p><strong>Sam</strong>: "Alright, remember when you wanted support for 12 new formats? Watch this."</p>
<p>Sam opens the terminal:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new parser (takes 5 minutes)</span>
<span class="hljs-comment"># src/parsers/toml_parser.rs - 30 lines of code.</span>

<span class="hljs-comment"># Register it (takes 1 line)</span>
<span class="hljs-comment"># registry.register("toml".to_string(), Arc::new(TomlParser::new()));</span>

<span class="hljs-comment"># Test it immediately.</span>
curl -X POST http://localhost:3000/parse \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{"content": "name = \"test\"\nvalue = 42", "format": "toml"}'</span>

<span class="hljs-comment"># Response: Success!</span>
</code></pre>
<p><strong>Emma</strong> <em>(amazed)</em>: "Wait, that's it? You just added TOML support in 5 minutes?"</p>
<p><strong>Sam</strong>: "Yep. And I didn't touch ANY existing code. No changes to handlers, no changes to other parsers, no changes to tests."</p>
<p><strong>David</strong>: "That's the power of the Factory Pattern with Registry. Show them the before/after metrics."</p>
<h3 id="heading-the-metrics-that-matter">The Metrics That Matter</h3>
<p><strong>Before (If-Else Hell):</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Metric</td><td>Value</td></tr>
</thead>
<tbody>
<tr>
<td>Lines of duplicate code</td><td>270 lines (18 × 15 files)</td></tr>
<tr>
<td>Time to add new format</td><td>2-3 hours</td></tr>
<tr>
<td>Files to modify per format</td><td>15+ files</td></tr>
<tr>
<td>Bugs per new format</td><td>1-2 on average</td></tr>
<tr>
<td>Test coverage</td><td>12% (untestable)</td></tr>
<tr>
<td>Cyclomatic complexity</td><td>9 per chain</td></tr>
<tr>
<td>Developer happiness</td><td>2/10</td></tr>
</tbody>
</table>
</div><p><strong>After (Factory + Registry):</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Metric</td><td>Value</td><td>Improvement</td></tr>
</thead>
<tbody>
<tr>
<td>Lines of duplicate code</td><td>0 lines</td><td>100% elimination</td></tr>
<tr>
<td>Time to add new format</td><td>10-15 minutes</td><td>92% faster</td></tr>
<tr>
<td>Files to modify per format</td><td>1 file (new parser)</td><td>93% reduction</td></tr>
<tr>
<td>Bugs per new format</td><td>0 (isolated)</td><td>100% reduction</td></tr>
<tr>
<td>Test coverage</td><td>87% (testable!)</td><td>625% increase</td></tr>
<tr>
<td>Cyclomatic complexity</td><td>1-2 per function</td><td>78% reduction</td></tr>
<tr>
<td>Developer happiness</td><td>9/10</td><td>350% increase</td></tr>
</tbody>
</table>
</div><h3 id="heading-architecture-evolution">Architecture Evolution</h3>
<p><strong>Before:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">If-Else</span> <span class="hljs-string">Chains</span> <span class="hljs-string">Everywhere</span>
<span class="hljs-string">document_service.rs</span>    <span class="hljs-string">────┐</span>
<span class="hljs-string">file_processor.rs</span>      <span class="hljs-string">────┤</span>
<span class="hljs-string">api_handler.rs</span>         <span class="hljs-string">────┤</span>
<span class="hljs-string">batch_processor.rs</span>     <span class="hljs-string">────┤</span>  <span class="hljs-string">All</span> <span class="hljs-string">contain</span>
<span class="hljs-string">validator.rs</span>           <span class="hljs-string">────┼─</span> <span class="hljs-string">same</span> <span class="hljs-number">18</span><span class="hljs-string">-line</span>
<span class="hljs-string">converter.rs</span>           <span class="hljs-string">────┤</span>  <span class="hljs-string">if-else</span> <span class="hljs-string">chain</span>
<span class="hljs-string">cli_tool.rs</span>            <span class="hljs-string">────┤</span>
<span class="hljs-string">upload_handler.rs</span>      <span class="hljs-string">────┤</span>
<span class="hljs-string">...</span> <span class="hljs-number">8</span> <span class="hljs-string">more</span> <span class="hljs-string">files</span>       <span class="hljs-string">────┘</span>

<span class="hljs-string">Adding</span> <span class="hljs-string">format</span> <span class="hljs-string">=</span> <span class="hljs-string">Modify</span> <span class="hljs-number">15</span><span class="hljs-string">+</span> <span class="hljs-string">files</span>
</code></pre>
<p><strong>After:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">Clean</span> <span class="hljs-string">Factory</span> <span class="hljs-string">+</span> <span class="hljs-string">Registry</span>
                    <span class="hljs-string">┌──────────────┐</span>
<span class="hljs-string">All</span> <span class="hljs-string">Clients</span> <span class="hljs-string">───────▶│</span>   <span class="hljs-string">Registry</span>   <span class="hljs-string">│</span>
                    <span class="hljs-string">│</span>  <span class="hljs-string">(Singleton)</span> <span class="hljs-string">│</span>
                    <span class="hljs-string">└──────┬───────┘</span>
                           <span class="hljs-string">│</span>
            <span class="hljs-string">┌──────────────┼──────────────┐</span>
            <span class="hljs-string">▼</span>              <span class="hljs-string">▼</span>              <span class="hljs-string">▼</span>
        <span class="hljs-string">JsonParser</span>    <span class="hljs-string">YamlParser</span>    <span class="hljs-string">CsvParser</span>
            <span class="hljs-string">▼</span>              <span class="hljs-string">▼</span>              <span class="hljs-string">▼</span>
        <span class="hljs-string">XmlParser</span>    <span class="hljs-string">TomlParser</span>    <span class="hljs-string">...</span> <span class="hljs-string">more</span>

<span class="hljs-string">Adding</span> <span class="hljs-string">format</span> <span class="hljs-string">=</span> <span class="hljs-string">Create</span> <span class="hljs-number">1</span> <span class="hljs-string">file</span> <span class="hljs-string">+</span> <span class="hljs-number">1</span> <span class="hljs-string">line</span> <span class="hljs-string">to</span> <span class="hljs-string">register</span>
</code></pre>
<h3 id="heading-code-quality-transformation">Code Quality Transformation</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768037222815/d8300def-1ce2-409d-9e60-5ab918cab462.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-team-reaction">Team Reaction</h3>
<p><strong>Emma</strong>: "So you can add all 12 formats for the enterprise client?"</p>
<p><strong>Sam</strong>: "I can add them this week. Probably 2-3 hours total for all 12."</p>
<p><strong>Emma</strong> <em>(excited)</em>: "Last time you estimated 3 hours PER format!"</p>
<p><strong>Sam</strong>: "That was before the refactor. Now it's just:</p>
<ol>
<li><p>Create parser (15 min)</p>
</li>
<li><p>Write tests (10 min)</p>
</li>
<li><p>Register it (1 line)</p>
</li>
<li><p>Done!"</p>
</li>
</ol>
<p><strong>David</strong>: "And the best part? The code is now maintainable. When we hire new developers, they won't need to hunt through 15 files to understand parser selection. It's all in one place."</p>
<h3 id="heading-production-deployment">Production Deployment</h3>
<p>Sam deploys the new factory-based system to production.</p>
<p><strong>Deployment Log:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">All</span> <span class="hljs-string">existing</span> <span class="hljs-string">parsers</span> <span class="hljs-string">working</span>
<span class="hljs-string">Zero</span> <span class="hljs-string">breaking</span> <span class="hljs-string">changes</span>
<span class="hljs-string">Response</span> <span class="hljs-string">times</span> <span class="hljs-string">improved</span> <span class="hljs-string">(10%</span> <span class="hljs-string">faster)</span>
<span class="hljs-string">Memory</span> <span class="hljs-string">usage</span> <span class="hljs-string">down</span> <span class="hljs-string">(parsers</span> <span class="hljs-string">shared</span> <span class="hljs-string">via</span> <span class="hljs-string">Arc)</span>
<span class="hljs-string">Test</span> <span class="hljs-string">coverage</span> <span class="hljs-string">up</span> <span class="hljs-string">from</span> <span class="hljs-number">12</span><span class="hljs-string">%</span> <span class="hljs-string">to</span> <span class="hljs-number">87</span><span class="hljs-string">%</span>
<span class="hljs-string">Code</span> <span class="hljs-string">complexity</span> <span class="hljs-string">down</span> <span class="hljs-number">78</span><span class="hljs-string">%</span>
</code></pre>
<p><strong>Incident Log (Next 30 Days):</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Week 1:</span> <span class="hljs-number">0</span> <span class="hljs-string">parser-related</span> <span class="hljs-string">bugs</span> <span class="hljs-string">(was</span> <span class="hljs-number">2</span><span class="hljs-number">-3</span><span class="hljs-string">/week)</span>
<span class="hljs-attr">Week 2:</span> <span class="hljs-number">0</span> <span class="hljs-string">parser-related</span> <span class="hljs-string">bugs</span>
<span class="hljs-attr">Week 3:</span> <span class="hljs-number">0</span> <span class="hljs-string">parser-related</span> <span class="hljs-string">bugs</span>
<span class="hljs-attr">Week 4:</span> <span class="hljs-number">0</span> <span class="hljs-string">parser-related</span> <span class="hljs-string">bugs</span>

<span class="hljs-attr">Total:</span> <span class="hljs-number">0</span> <span class="hljs-string">bugs!</span>
</code></pre>
<p><strong>Feature Velocity:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Formats added:</span> <span class="hljs-number">12</span> <span class="hljs-string">(all</span> <span class="hljs-string">enterprise</span> <span class="hljs-string">client</span> <span class="hljs-string">formats)</span>
<span class="hljs-attr">Time taken:</span> <span class="hljs-number">3.5</span> <span class="hljs-string">hours</span> <span class="hljs-string">total</span>
<span class="hljs-attr">Bugs introduced:</span> <span class="hljs-number">0</span>
<span class="hljs-attr">Files modified:</span> <span class="hljs-number">12</span> <span class="hljs-string">new</span> <span class="hljs-string">files,</span> <span class="hljs-number">0</span> <span class="hljs-string">existing</span> <span class="hljs-string">files</span> <span class="hljs-string">modified</span>
</code></pre>
<h2 id="heading-when-to-use-and-not-use-factory-pattern">When to Use (and Not Use) Factory Pattern</h2>
<p>Now that you've seen the Factory Pattern in action, let's talk about when it makes sense and when it doesn't.</p>
<h3 id="heading-use-factory-pattern-when">Use Factory Pattern When:</h3>
<p><strong>1. Creating objects requires complex logic</strong></p>
<p>If you find yourself with multiple conditional branches, configuration-based instantiation, or object creation that varies based on input, a factory can help centralise and simplify that logic.</p>
<p><strong>2. You have a family of related classes</strong></p>
<p>When you have multiple types that implement the same interface and are used interchangeably (like our parsers), a factory provides a clean way to select between them.</p>
<p><strong>3. Object creation is scattered</strong></p>
<p>The biggest red flag is finding the same creation code duplicated across your codebase. That's exactly what Sam was dealing with, and exactly what the factory fixed.</p>
<p><strong>4. You want loose coupling</strong></p>
<p>If you want client code to work with abstractions rather than concrete types, a factory acts as the bridge. Clients ask for "a parser" without knowing or caring about the specific implementation.</p>
<h3 id="heading-dont-use-factory-pattern-when">Don't Use Factory Pattern When:</h3>
<p><strong>1. Object creation is simple</strong></p>
<p>If creating an object is just calling <code>new()</code> with no complex logic, a factory adds unnecessary abstraction:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Overkill.</span>
<span class="hljs-keyword">let</span> user = UserFactory::create(name, email);

<span class="hljs-comment">// Better - just use new directly.</span>
<span class="hljs-keyword">let</span> user = User::new(name, email);
</code></pre>
<p><strong>2. You only have one concrete type</strong></p>
<p>If there's no polymorphism needed, no variants to choose from, a factory adds no value.</p>
<p><strong>3. Performance is critical</strong></p>
<p>Factories add a small amount of indirection. For most applications this is negligible, but in performance-critical hot paths, direct instantiation may be faster. Always profile before optimizing!</p>
<p><strong>4. Added complexity not justified</strong></p>
<p>If your team doesn't understand the pattern, or if you're solving a simple problem with a complex solution, step back and ask if you really need it. <mark>Remember YAGNI, You Aren't Gonna Need It.</mark></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When I first started writing backend systems, I made the same mistakes Sam did. I'd copy-paste code, add another <code>else if</code> branch, and tell myself "I'll clean this up later." Spoiler: later never came. Instead, the codebase grew into something I dreaded opening every morning.</p>
<p>The Factory Pattern wasn't something I learned from a textbook and immediately understood. It clicked for me only after I'd experienced the pain firsthand, after I'd introduced production bugs because I forgot to update one of twelve files, after I'd spent entire weekends doing what should have been a thirty-minute task.</p>
<p>Here's what I want you to take away from this:</p>
<p><strong>Design patterns aren't academic exercises.</strong> They're battle scars turned into blueprints. <mark>Every pattern exists because thousands of developers before us hit the same wall and figured out how to climb over it. </mark> The Factory Pattern exists because object creation gets messy, and centralising that mess into one place makes everything else cleaner.</p>
<p><strong>Start simple, refactor when it hurts.</strong> <mark>I'm not saying you should use the Factory Pattern everywhere from day one.</mark> If you're building something small with two or three types, direct instantiation is fine. But pay attention to the warning signs: duplicated creation logic, fear of adding new types, bugs that keep appearing in the same places. When you feel that pain, that's when patterns become your friend.</p>
<p><strong>The Registry Pattern is underrated.</strong> Combining Factory with Registry gave me something I didn't expect, the ability to extend the system without touching existing code. That's not just convenient; it's liberating. New requirements stopped feeling like a burden and started feeling like opportunities.</p>
<p><strong>Rust makes this elegant.</strong> Traits, <code>Arc</code>, <code>match</code> expressions, Rust's type system practically guides you toward clean factory implementations. The compiler catches mistakes that would have been runtime bugs in other languages. If you're coming from a dynamically typed background, lean into Rust's strictness. It's not fighting you; it's protecting you.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768037859993/faf6478f-fd37-41a7-a82c-e6b1134de32d.png" alt class="image--center mx-auto" /></p>
<p>Looking back at the document parser API we built together, I'm genuinely proud of how it turned out. It's not over-engineered. It's not clever for the sake of being clever. It's just... clean. And clean code is code you can hand off to someone else without writing a novel of documentation. It's code you can come back to six months later and actually understand.</p>
<p>If you've made it this far, thank you for reading. I hope Sam's story resonated with you, and I hope the next time you find yourself drowning in if-else chains, you'll remember there's a better way.</p>
<p><em>Now go build something. And when it gets messy, because it will, you'll know what to do.</em></p>
]]></content:encoded></item><item><title><![CDATA[Bob The Builder]]></title><description><![CDATA[Meet Bob. Two months ago, they joined a fast-growing data analytics startup as a backend developer. The company's main product is a data warehouse API that lets customers query massive datasets. Bob's job? Build a client library that makes it easy to...]]></description><link>https://writer.mrmehta.in/bob-the-builder</link><guid isPermaLink="true">https://writer.mrmehta.in/bob-the-builder</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[builder pattern]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Thu, 01 Jan 2026 20:15:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767283264718/af82f977-4749-4a2e-82d4-738bea80ec20.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767290640235/07dfb5b0-7280-4a3c-ba83-78de0fc8708e.png" alt class="image--center mx-auto" /></p>
<p>Meet Bob. Two months ago, they joined a fast-growing data analytics startup as a backend developer. The company's main product is a data warehouse API that lets customers query massive datasets. Bob's job? Build a client library that makes it easy to interact with this API.</p>
<p>Sounds simple, right? Not quite.</p>
<p>It's 2:47 PM on a Tuesday. Bob stares at the screen, eyes twitching. The code review comments just came in:</p>
<blockquote>
<p>"This is unreadable. I have no idea which parameter is which. Also, you forgot to set the timeout on line 47, causing a production incident."</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767291189888/1ecd4ae5-4ef0-4145-a1c6-df068f384642.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-current-code-the-mess">The Current Code (The Mess)</h3>
<p>Here's what Bob has been dealing with:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// The current API request builder - not pretty.</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"reports"</span>.to_string(),
    method: HttpMethod::GET,
    query_params: <span class="hljs-literal">Some</span>(query_params),
    headers: <span class="hljs-literal">None</span>,
    body: <span class="hljs-literal">None</span>,
    auth: AuthType::Bearer(<span class="hljs-string">"token"</span>.into()),
    timeout_seconds: <span class="hljs-literal">Some</span>(<span class="hljs-number">30</span>),
    retry_count: <span class="hljs-literal">Some</span>(<span class="hljs-number">3</span>),
    cache_results: <span class="hljs-literal">true</span>,
};

<span class="hljs-comment">// Creating a POST request - even worse.</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"analytics/query"</span>.to_string(),
    method: HttpMethod::POST,
    query_params: <span class="hljs-literal">None</span>,
    headers: <span class="hljs-literal">Some</span>(headers),
    body: <span class="hljs-literal">Some</span>(body_params), <span class="hljs-comment">// Can be a huge JSON written inline here.</span>
    auth: AuthType::Bearer(<span class="hljs-string">"token"</span>.into()),
    timeout_seconds: <span class="hljs-literal">None</span>,
    retry_count: <span class="hljs-literal">None</span>,
    cache_results: <span class="hljs-literal">false</span>,
};

<span class="hljs-comment">// A week later, Bob can't remember which field is which...</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"users"</span>.to_string(),
    method: HttpMethod::GET,
    query_params: <span class="hljs-literal">Some</span>(params),
    headers: <span class="hljs-literal">None</span>,
    body: <span class="hljs-literal">Some</span>(body),
    auth: AuthType::<span class="hljs-literal">None</span>,
    timeout_seconds: <span class="hljs-literal">Some</span>(<span class="hljs-number">30</span>),
    retry_count: <span class="hljs-literal">Some</span>(retry),
    cache_results: <span class="hljs-literal">true</span>,
};
</code></pre>
<p>Bob's internal monologue goes something like this:</p>
<blockquote>
<p>"Every time I create a request, I spend 5 minutes double-checking the field order. Is it <code>query_params</code> then <code>headers</code>, or <code>headers</code> then <code>body</code>? Why do I need to specify <code>None</code> for fields I don't care about? And the worst part? If I forget a crucial field like <code>timeout_seconds</code>, I only find out when the request hangs forever in production."</p>
</blockquote>
<h3 id="heading-the-pain-points">The Pain Points</h3>
<p>Let's visualise Bob's suffering:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767291446097/c0e9f362-8e4d-4e23-9fd0-1b2a164704e7.png" alt class="image--center mx-auto" /></p>
<p>The specific problems:</p>
<ol>
<li><p><strong>Parameter Soup</strong>: 9 fields, most are <code>Option&lt;T&gt;</code> - which ones are required?</p>
</li>
<li><p><strong>No Guidance</strong>: No hints about what's required versus optional.</p>
</li>
<li><p><strong>Order Matters</strong>: Struct fields must be in exact order.</p>
</li>
<li><p><strong>Easy to Forget</strong>: Miss one field? Runtime error or silent failure.</p>
</li>
<li><p><strong><mark>No Validation</mark></strong><mark>: Can set contradictory values. (GET with body)</mark></p>
</li>
<li><p><strong>Unreadable Code</strong>: Looking at the code, can you tell what it does?</p>
</li>
</ol>
<pre><code class="lang-rust"><span class="hljs-comment">// Quick! What does this request do?</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"data"</span>.to_string(),
    method: HttpMethod::POST,
    query_params: <span class="hljs-literal">None</span>,
    headers: <span class="hljs-literal">None</span>,
    body: <span class="hljs-literal">Some</span>(serde_json::json!({<span class="hljs-string">"query"</span>: <span class="hljs-string">"SELECT *"</span>})),
    auth: AuthType::<span class="hljs-literal">None</span>,
    timeout_seconds: <span class="hljs-literal">None</span>,
    retry_count: <span class="hljs-literal">None</span>,
    cache_results: <span class="hljs-literal">false</span>,
};

<span class="hljs-comment">// Answer: Creates an analytics query without auth, timeout, or retry.</span>
<span class="hljs-comment">// But you had to READ EVERY SINGLE LINE to figure that out!</span>
</code></pre>
<p>Most requests don't need headers, custom timeouts, or retry logic. But you still have to write <code>headers: None</code>, <code>timeout_seconds: None</code>, <code>retry_count: None</code> for every single request. A simple GET request that only needs an endpoint ends up being 9 lines of code, with 6 of those lines just saying "I don't need this." <mark>This bloats the codebase and makes every request harder to read and maintain.</mark></p>
<h2 id="heading-the-constructor-chaos">The Constructor Chaos</h2>
<p>Let's break down why Bob's code is such a nightmare.</p>
<h3 id="heading-positional-parameters">Positional Parameters</h3>
<p>When constructors take many parameters in a specific order, reading the code becomes a guessing game. You see values like <code>None</code>, <code>Some(30)</code>, and <code>true</code> lined up, but without the function signature open in another window, you have no idea what each position represents. This forces developers to constantly jump between files or count commas to understand simple function calls.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Traditional constructor. (if it existed)</span>
<span class="hljs-keyword">impl</span> WarehouseRequest {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(
        endpoint: <span class="hljs-built_in">String</span>,
        method: HttpMethod,
        query_params: <span class="hljs-built_in">Option</span>&lt;QueryParams&gt;,
        headers: <span class="hljs-built_in">Option</span>&lt;Headers&gt;,
        body: <span class="hljs-built_in">Option</span>&lt;BodyParams&gt;,
        auth: AuthType,
        timeout_seconds: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u64</span>&gt;,
        retry_count: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u32</span>&gt;,
        cache_results: <span class="hljs-built_in">bool</span>,
    ) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            endpoint,
            method,
            query_params,
            headers,
            body,
            auth,
            timeout_seconds,
            retry_count,
            cache_results,
        }
    }
}

<span class="hljs-comment">// Using it:</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest::new(
    <span class="hljs-string">"reports"</span>.to_string(),
    HttpMethod::GET,
    <span class="hljs-literal">Some</span>(params),
    <span class="hljs-literal">None</span>,  <span class="hljs-comment">// Which field is this?</span>
    <span class="hljs-literal">None</span>,  <span class="hljs-comment">// And this?</span>
    AuthType::Bearer(<span class="hljs-string">"token"</span>.into()),
    <span class="hljs-literal">Some</span>(<span class="hljs-number">30</span>),  <span class="hljs-comment">// Wait, timeout or retry?</span>
    <span class="hljs-literal">Some</span>(<span class="hljs-number">3</span>),   <span class="hljs-comment">// Definitely lost now.</span>
    <span class="hljs-literal">true</span>,
);
</code></pre>
<p>Nobody can read this! You need to count parameters and cross-reference with the function signature.</p>
<h3 id="heading-optional-field-explosion">Optional Field Explosion</h3>
<p>With six optional fields, there are 64 mathematically possible combinations of which fields to set. In practice, nobody knows which combinations make sense for their use case. Should a GET request have retry logic? Does caching require authentication? Without clear patterns, every developer creates requests differently, leading to inconsistent code across the codebase and making it impossible to establish best practices.</p>
<p>With 9 fields and 6 optional, that's <strong>2^6 = 64</strong> possible combinations!</p>
<pre><code class="lang-rust"><span class="hljs-comment">// All these are valid, but which is correct?</span>
<span class="hljs-keyword">let</span> r1 = WarehouseRequest { <span class="hljs-comment">/* all None */</span> };
<span class="hljs-keyword">let</span> r2 = WarehouseRequest { <span class="hljs-comment">/* only timeout */</span> };
<span class="hljs-keyword">let</span> r3 = WarehouseRequest { <span class="hljs-comment">/* timeout + retry */</span> };
<span class="hljs-keyword">let</span> r4 = WarehouseRequest { <span class="hljs-comment">/* timeout + retry + headers */</span> };
<span class="hljs-comment">// ... 60 more combinations!</span>
</code></pre>
<h3 id="heading-no-compile-time-guarantees">No Compile-Time Guarantees</h3>
<p>The struct accepts any values that match the types, even when those values make no sense. An empty endpoint, a POST request without a body, or a timeout of zero seconds all compile successfully. The type system can't encode business rules, so invalid states slip through compilation and only surface as runtime errors in production when real users are affected.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// This compiles fine but crashes at runtime!</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">""</span>.to_string(),  <span class="hljs-comment">// Empty endpoint!</span>
    method: HttpMethod::POST,
    query_params: <span class="hljs-literal">None</span>,
    headers: <span class="hljs-literal">None</span>,
    body: <span class="hljs-literal">None</span>,  <span class="hljs-comment">// POST without body!</span>
    auth: AuthType::<span class="hljs-literal">None</span>,
    timeout_seconds: <span class="hljs-literal">Some</span>(<span class="hljs-number">0</span>),  <span class="hljs-comment">// Zero timeout!</span>
    retry_count: <span class="hljs-literal">Some</span>(<span class="hljs-number">100</span>),    <span class="hljs-comment">// 100 retries!</span>
    cache_results: <span class="hljs-literal">true</span>,
};

<span class="hljs-comment">// Runtime: BOOM!</span>
</code></pre>
<p>The compiler is happy, but the code is broken.</p>
<h3 id="heading-the-real-world-impact">The Real-World Impact</h3>
<p>These aren't just theoretical problems. Bob's production logs show the real cost: hung requests from missing timeouts, failed operations from forgotten retry logic, and support tickets from malformed requests. Each incident means frustrated users, emergency debugging sessions, and post-mortems. <strong>The current approach isn't just inconvenient; it's actively causing business problems that could be prevented with better API design.</strong></p>
<p>Bob keeps a log of production incidents:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Week 1:</span> <span class="hljs-string">Forgot</span> <span class="hljs-string">to</span> <span class="hljs-string">set</span> <span class="hljs-string">timeout</span> <span class="hljs-string">→</span> <span class="hljs-string">Request</span> <span class="hljs-string">hung</span> <span class="hljs-string">for</span> <span class="hljs-number">10</span> <span class="hljs-string">minutes</span> <span class="hljs-string">→</span> <span class="hljs-string">User</span> <span class="hljs-string">complained.</span>
<span class="hljs-attr">Week 2:</span> <span class="hljs-string">Set</span> <span class="hljs-string">body</span> <span class="hljs-string">on</span> <span class="hljs-string">GET</span> <span class="hljs-string">request</span> <span class="hljs-string">→</span> <span class="hljs-number">400</span> <span class="hljs-string">Bad</span> <span class="hljs-string">Request</span> <span class="hljs-string">→</span> <span class="hljs-string">Monitoring</span> <span class="hljs-string">alerted.</span>
<span class="hljs-attr">Week 3:</span> <span class="hljs-string">Typo</span> <span class="hljs-string">in</span> <span class="hljs-string">endpoint</span> <span class="hljs-string">("repots"</span> <span class="hljs-string">instead</span> <span class="hljs-string">of</span> <span class="hljs-string">"reports"</span><span class="hljs-string">)</span> <span class="hljs-string">→</span> <span class="hljs-number">404</span> <span class="hljs-string">→</span> <span class="hljs-string">Support</span> <span class="hljs-string">ticket.</span>
<span class="hljs-attr">Week 4:</span> <span class="hljs-string">Forgot</span> <span class="hljs-string">retry_count</span> <span class="hljs-string">→</span> <span class="hljs-string">Single</span> <span class="hljs-string">network</span> <span class="hljs-string">blip</span> <span class="hljs-string">caused</span> <span class="hljs-string">failure</span> <span class="hljs-string">→</span> <span class="hljs-string">Data</span> <span class="hljs-string">loss.</span>
<span class="hljs-attr">Week 5:</span> <span class="hljs-string">Set</span> <span class="hljs-string">timeout</span> <span class="hljs-string">to</span> <span class="hljs-number">0</span> <span class="hljs-string">→</span> <span class="hljs-string">Instant</span> <span class="hljs-string">timeout</span> <span class="hljs-string">on</span> <span class="hljs-string">all</span> <span class="hljs-string">requests</span> <span class="hljs-string">→</span> <span class="hljs-string">Service</span> <span class="hljs-string">down.</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767292624910/59a3b27f-485c-4395-b2a9-e8fbd157ab8c.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-breaking-point">The Breaking Point</h2>
<p>Friday afternoon. Bob's manager, Casey, schedules a code review.</p>
<p>"Bob, we need to talk about the API client code."</p>
<p>Bob, a bit defensive: "I know it's not perfect, but it works!"</p>
<p>Casey pulls up the screen: "Does it? Look at this production log from yesterday."</p>
<pre><code class="lang-yaml">[<span class="hljs-string">ERROR</span>] <span class="hljs-attr">Request to warehouse failed:</span> <span class="hljs-string">timeout</span> <span class="hljs-string">after</span> <span class="hljs-string">0ms</span>
[<span class="hljs-string">ERROR</span>] <span class="hljs-string">Endpoint</span> <span class="hljs-string">''</span> <span class="hljs-string">not</span> <span class="hljs-string">found</span> <span class="hljs-string">(404)</span>
[<span class="hljs-string">ERROR</span>] <span class="hljs-string">POST</span> <span class="hljs-string">request</span> <span class="hljs-string">to</span> <span class="hljs-string">/reports</span> <span class="hljs-string">missing</span> <span class="hljs-string">required</span> <span class="hljs-string">body</span>
[<span class="hljs-string">ERROR</span>] <span class="hljs-attr">Invalid retry count:</span> <span class="hljs-number">255</span>
</code></pre>
<p>"All of these are from your client library. The warehouse API is fine. It's how we're calling it."</p>
<p>"But... I tested all these requests locally!"</p>
<p>"Did you test every possible combination of optional parameters? All 64 of them?"</p>
<p><strong>Silence.</strong></p>
<p>"Look, I'm not blaming you. The API is hard to use. But we need a better solution. <mark>Have you heard of the Builder Pattern</mark>?"</p>
<p>"Builder? Like... building things?"</p>
<p>"Sort of. It's a design pattern that makes constructing complex objects easier. Instead of this..."</p>
<blockquote>
<p><strong>Builder</strong> is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.</p>
</blockquote>
<p>Casey points at the screen:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"reports"</span>.to_string(),
    method: HttpMethod::GET,
    query_params: <span class="hljs-literal">Some</span>(params),
    headers: <span class="hljs-literal">None</span>,
    body: <span class="hljs-literal">None</span>,
    auth: AuthType::Bearer(<span class="hljs-string">"token"</span>.into()),
    timeout_seconds: <span class="hljs-literal">Some</span>(<span class="hljs-number">30</span>),
    retry_count: <span class="hljs-literal">Some</span>(<span class="hljs-number">3</span>),
    cache_results: <span class="hljs-literal">true</span>,
};
</code></pre>
<p>"...we could have this:"</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"reports"</span>)
    .query_params(params)
    .bearer_auth(<span class="hljs-string">"token"</span>)
    .timeout(<span class="hljs-number">30</span>)
    .retry(<span class="hljs-number">3</span>)
    .cache(<span class="hljs-literal">true</span>)
    .build()?;
</code></pre>
<p>Bob's eyes widen: "That's... actually readable! I can tell exactly what it's doing!"</p>
<p>"And check this out - <mark>in Rust, we can use the type system to enforce that required fields are set at compile time. If you forget the endpoint, it won't even compile.</mark>"</p>
<p>"No more runtime errors for missing fields?"</p>
<p>"Exactly. Let me show you how it works."</p>
<h2 id="heading-enter-the-builder-pattern">Enter the Builder Pattern</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767292995904/f6a2e105-929f-4045-a4c6-902859cb55db.png" alt class="image--center mx-auto" /></p>
<p>Casey pulls up a browser and starts sketching on the whiteboard.</p>
<p>"The Builder Pattern separates the construction of a complex object from its representation. Think of it like building a house."</p>
<p>Casey draws:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Building a House:</span>

<span class="hljs-attr">WITHOUT Builder:</span>
<span class="hljs-string">House(foundation,</span> <span class="hljs-string">walls,</span> <span class="hljs-string">roof,</span> <span class="hljs-string">windows,</span> <span class="hljs-string">doors,</span> <span class="hljs-string">plumbing,</span> <span class="hljs-string">electrical,</span> <span class="hljs-string">...)</span>
<span class="hljs-string">↑</span> <span class="hljs-string">All</span> <span class="hljs-string">at</span> <span class="hljs-string">once,</span> <span class="hljs-string">in</span> <span class="hljs-string">exact</span> <span class="hljs-string">order,</span> <span class="hljs-string">can't</span> <span class="hljs-string">skip</span> <span class="hljs-string">any</span>

<span class="hljs-attr">WITH Builder:</span>
<span class="hljs-string">HouseBuilder()</span>
  <span class="hljs-string">.foundation(concrete)</span>
  <span class="hljs-string">.walls(brick)</span>
  <span class="hljs-string">.roof(tile)</span>
  <span class="hljs-string">.plumbing(copper)</span>
  <span class="hljs-string">.build()</span>
<span class="hljs-string">↑</span> <span class="hljs-string">Step</span> <span class="hljs-string">by</span> <span class="hljs-string">step,</span> <span class="hljs-string">any</span> <span class="hljs-string">order,</span> <span class="hljs-string">skip</span> <span class="hljs-string">optional</span> <span class="hljs-string">parts</span>
</code></pre>
<p>"The key benefits are:"</p>
<ol>
<li><p><strong>Readability</strong>: Each method call is self-documenting</p>
</li>
<li><p><strong>Flexibility</strong>: Set parameters in any order</p>
</li>
<li><p><strong>Optional Parameters</strong>: Only set what you need</p>
</li>
<li><p><strong>Validation</strong>: Check correctness before building</p>
</li>
<li><p><strong>Immutability</strong>: Builder is consumed, final object is immutable</p>
</li>
</ol>
<p><strong>"So instead of one massive constructor, we have many small methods that each set one thing?"</strong></p>
<p>"Exactly! And in Rust, we can make it even better with the type-state pattern."</p>
<h2 id="heading-the-type-state-pattern">The Type-State Pattern</h2>
<p>The <a target="_blank" href="https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html">type-state pattern</a> is Rust's secret weapon for catching bugs at compile time. It uses generic type parameters to track what state an object is in, t<mark>hen uses the type system to control which methods are available in each state.</mark> This means the compiler can enforce that you must set required fields before building the final object. Unlike runtime validation that checks for errors when the code runs, type-state validation happens during compilation, making entire classes of bugs impossible.</p>
<p>Casey draws another diagram:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Type-State</span> <span class="hljs-string">Pattern</span> <span class="hljs-string">(Compile-time</span> <span class="hljs-string">guarantees):</span>

<span class="hljs-attr">State 1:</span> <span class="hljs-string">NoEndpoint</span>
  <span class="hljs-string">↓</span> <span class="hljs-string">.endpoint("reports")</span>
<span class="hljs-attr">State 2:</span> <span class="hljs-string">HasEndpoint</span>
  <span class="hljs-string">↓</span> <span class="hljs-string">.query_param(...),</span> <span class="hljs-string">.timeout(...),</span> <span class="hljs-string">etc.</span>
<span class="hljs-attr">State 3:</span> <span class="hljs-string">ReadyToBuild</span>
  <span class="hljs-string">↓</span> <span class="hljs-string">.build()</span>
<span class="hljs-attr">Final:</span> <span class="hljs-string">WarehouseRequest</span>

<span class="hljs-attr">Rules enforced by TYPE SYSTEM:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">Can</span> <span class="hljs-string">only</span> <span class="hljs-string">call</span> <span class="hljs-string">.build()</span> <span class="hljs-string">in</span> <span class="hljs-string">HasEndpoint</span> <span class="hljs-string">state</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">Can't</span> <span class="hljs-string">build</span> <span class="hljs-string">without</span> <span class="hljs-string">setting</span> <span class="hljs-string">endpoint</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">Compiler</span> <span class="hljs-string">catches</span> <span class="hljs-string">mistakes!</span>
</code></pre>
<p>"Wait, so if I forget to set the endpoint, the code won't even compile?"</p>
<p>"Right! The compiler will say 'build() doesn't exist for WarehouseQueryBuilder'"</p>
<p>"That's amazing! So many of our production bugs would be caught at compile time!"</p>
<p>"Exactly. Ready to implement it?"</p>
<p>"Let's do this!"</p>
<h2 id="heading-what-is-builder-pattern">What is Builder Pattern?</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://refactoring.guru/design-patterns/builder">https://refactoring.guru/design-patterns/builder</a></div>
<p> </p>
<h3 id="heading-the-core-idea">The Core Idea</h3>
<p>Instead of this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Create</span> <span class="hljs-string">object</span> <span class="hljs-string">with</span> <span class="hljs-string">all</span> <span class="hljs-string">parameters</span> <span class="hljs-string">at</span> <span class="hljs-string">once</span>
<span class="hljs-string">↓</span>
<span class="hljs-string">Hope</span> <span class="hljs-string">everything</span> <span class="hljs-string">is</span> <span class="hljs-string">correct</span>
<span class="hljs-string">↓</span>
<span class="hljs-string">Runtime</span> <span class="hljs-string">errors</span> <span class="hljs-string">if</span> <span class="hljs-string">something</span> <span class="hljs-string">wrong</span>
</code></pre>
<p>We do this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Create</span> <span class="hljs-string">builder</span>
<span class="hljs-string">↓</span>
<span class="hljs-string">Set</span> <span class="hljs-string">parameters</span> <span class="hljs-string">one</span> <span class="hljs-string">by</span> <span class="hljs-string">one</span> <span class="hljs-string">(fluent</span> <span class="hljs-string">API)</span>
<span class="hljs-string">↓</span>
<span class="hljs-string">Validate</span>
<span class="hljs-string">↓</span>
<span class="hljs-string">Build</span> <span class="hljs-string">final</span> <span class="hljs-string">object</span>
</code></pre>
<h3 id="heading-uml-diagram">UML Diagram</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767293549891/9edcba91-b67d-430b-93fb-640e1392782f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-comparison">Comparison</h3>
<p><strong>Traditional Constructor:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// Hard to read, easy to mess up.</span>
<span class="hljs-keyword">let</span> request = WarehouseRequest::new(
    <span class="hljs-string">"reports"</span>,
    HttpMethod::GET,
    <span class="hljs-literal">Some</span>(params),
    <span class="hljs-literal">None</span>,
    <span class="hljs-literal">None</span>,
    AuthType::Bearer(<span class="hljs-string">"token"</span>.into()),
    <span class="hljs-literal">Some</span>(<span class="hljs-number">30</span>),
    <span class="hljs-literal">Some</span>(<span class="hljs-number">3</span>),
    <span class="hljs-literal">true</span>
);
</code></pre>
<p><strong>Builder Pattern:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// Clear, self-documenting, flexible.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"reports"</span>)
    .method(HttpMethod::GET)
    .query_params(params)
    .bearer_auth(<span class="hljs-string">"token"</span>)
    .timeout(<span class="hljs-number">30</span>)
    .retry(<span class="hljs-number">3</span>)
    .cache(<span class="hljs-literal">true</span>)
    .build()?;
</code></pre>
<p><strong>Key Differences:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Constructor</td><td>Builder</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Readability</strong></td><td>Low (positional)</td><td>High (named methods)</td></tr>
<tr>
<td><strong>Order</strong></td><td>Must match signature</td><td>Any order</td></tr>
<tr>
<td><strong>Optional params</strong></td><td>Still required (as None)</td><td>Truly optional (skip)</td></tr>
<tr>
<td><strong>Validation</strong></td><td>After construction</td><td>Before building</td></tr>
<tr>
<td><strong>Error messages</strong></td><td>Cryptic type errors</td><td>Clear, contextual</td></tr>
<tr>
<td><strong>Extensibility</strong></td><td>Hard (signature changes)</td><td>Easy (add methods)</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-rusts-type-state-pattern">Rust's Type-State Pattern</h2>
<p>Casey continues: "Now here's where Rust really shines. We can use the type system to track the builder's state and enforce rules at compile time."</p>
<h3 id="heading-the-problem-with-simple-builders">The Problem with Simple Builders</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// Simple builder (WITHOUT type-state)</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .timeout(<span class="hljs-number">30</span>)
    .retry(<span class="hljs-number">3</span>)
    .build()?;  <span class="hljs-comment">// Runtime error: missing endpoint!</span>
</code></pre>
<p>This compiles fine, but crashes at runtime when we call <code>build()</code>.</p>
<h3 id="heading-the-type-state-solution">The Type-State Solution</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// Type-state builder.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()  <span class="hljs-comment">// Type: Builder&lt;NoEndpoint&gt;</span>
    .timeout(<span class="hljs-number">30</span>)
    .retry(<span class="hljs-number">3</span>)
    .build();  <span class="hljs-comment">// COMPILE ERROR: build() doesn't exist!</span>

<span class="hljs-comment">// Correct usage:</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()  <span class="hljs-comment">// Type: Builder&lt;NoEndpoint&gt;</span>
    .endpoint(<span class="hljs-string">"reports"</span>)                     <span class="hljs-comment">// Type: Builder&lt;HasEndpoint&gt;</span>
    .timeout(<span class="hljs-number">30</span>)
    .retry(<span class="hljs-number">3</span>)
    .build()?;  <span class="hljs-comment">// OK! build() exists for HasEndpoint.</span>
</code></pre>
<h3 id="heading-how-it-works">How It Works</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767294054657/045963fd-c882-4150-a457-98400310b4bb.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-phantom-types">Phantom Types</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::marker::PhantomData;

<span class="hljs-comment">// Define marker traits for states.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">EndpointState</span></span> {}
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">NoEndpoint</span></span>;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">HasEndpoint</span></span>;

<span class="hljs-keyword">impl</span> EndpointState <span class="hljs-keyword">for</span> NoEndpoint {}
<span class="hljs-keyword">impl</span> EndpointState <span class="hljs-keyword">for</span> HasEndpoint {}

<span class="hljs-comment">// Builder is generic over state.</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">WarehouseQueryBuilder</span></span>&lt;E: EndpointState&gt; {
    endpoint: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">String</span>&gt;,
    <span class="hljs-comment">// ... other fields ...</span>
    _endpoint_state: PhantomData&lt;E&gt;,  <span class="hljs-comment">// Zero-cost type marker.</span>
}
</code></pre>
<p><strong>What is</strong> <code>PhantomData</code>?</p>
<p><code>PhantomData&lt;E&gt;</code> is a special zero-sized type in Rust. It doesn't take up any memory at runtime, but it carries type information that the compiler uses to enforce rules. Think of it as a compile-time flag that costs nothing in performance but gives us compile-time safety.</p>
<p><strong>Why do we need it?</strong></p>
<p>Without <code>PhantomData</code>, the compiler would complain that the generic type parameter <code>E</code> is unused. <code>PhantomData&lt;E&gt;</code> tells the compiler "I'm intentionally using this type parameter for compile-time type checking, even though it doesn't appear in any fields."</p>
<p>"So it's like a compile-time flag?" Bob asks.</p>
<p>"Exactly! It costs nothing at runtime, but gives us compile-time safety."</p>
<h3 id="heading-method-availability-based-on-state">Method Availability Based on State</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;NoEndpoint&gt; {
    <span class="hljs-comment">// Only available in NoEndpoint state.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>() -&gt; <span class="hljs-keyword">Self</span> { ... }

    <span class="hljs-comment">// Transitions to HasEndpoint state.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">endpoint</span></span>(<span class="hljs-keyword">self</span>, endpoint: <span class="hljs-built_in">String</span>) -&gt; WarehouseQueryBuilder&lt;HasEndpoint&gt; {
        WarehouseQueryBuilder {
            endpoint: <span class="hljs-literal">Some</span>(endpoint),
            <span class="hljs-comment">// ... transfer other fields ...</span>
            _endpoint_state: PhantomData,  <span class="hljs-comment">// New state!</span>
        }
    }
}

<span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;HasEndpoint&gt; {
    <span class="hljs-comment">// Only available in HasEndpoint state.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, method: HttpMethod) -&gt; <span class="hljs-keyword">Self</span> { ... }
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">timeout</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, seconds: <span class="hljs-built_in">u64</span>) -&gt; <span class="hljs-keyword">Self</span> { ... }
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">retry</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, count: <span class="hljs-built_in">u32</span>) -&gt; <span class="hljs-keyword">Self</span> { ... }

    <span class="hljs-comment">// Only HasEndpoint can build!</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">build</span></span>(<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;WarehouseRequest, BuildError&gt; { ... }
}
</code></pre>
<p><strong>Understanding Ownership and</strong> <code>self</code></p>
<p>Notice that <code>endpoint()</code> takes <code>self</code> (not <code>&amp;self</code> or <code>&amp;mut self</code>). <mark>This means it </mark> <strong><mark>consumes</mark></strong> <mark>the builder - takes ownership of it. It then returns a new builder in a different state. This is key to the type-state pattern!</mark></p>
<p>For the other methods like <code>timeout()</code>, we use <code>mut self</code>, which also takes ownership, modifies the builder, and returns it. This enables method chaining.</p>
<p>"Oh! So <code>.build()</code> literally doesn't exist until you've set the endpoint. The compiler won't even let you call it!"</p>
<p>"Bingo! This is the power of Rust's type system. Entire categories of bugs are prevented at compile time."</p>
<h3 id="heading-compile-time-error-examples">Compile-Time Error Examples</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// COMPILE ERROR: build() doesn't exist for NoEndpoint.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .timeout(<span class="hljs-number">30</span>)
    .build();

<span class="hljs-comment">// Compiler says:</span>
<span class="hljs-comment">// error[E0599]: no method named `build` found for struct.</span>
<span class="hljs-comment">// `WarehouseQueryBuilder&lt;NoEndpoint&gt;`</span>
</code></pre>
<pre><code class="lang-rust"><span class="hljs-comment">// COMPILE ERROR: endpoint() doesn't exist for HasEndpoint.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"reports"</span>)
    .endpoint(<span class="hljs-string">"users"</span>)  <span class="hljs-comment">// Can't set endpoint twice!</span>
    .build();

<span class="hljs-comment">// Compiler says:</span>
<span class="hljs-comment">// error[E0599]: no method named `endpoint` found for struct.</span>
<span class="hljs-comment">// `WarehouseQueryBuilder&lt;HasEndpoint&gt;`</span>
</code></pre>
<p>Bob is amazed: "This is incredible! The compiler is like a super-strict code reviewer that never gets tired!"</p>
<h2 id="heading-the-solution">The Solution</h2>
<p>Now comes the fun part - actually building the type-safe builder. We'll walk through each step incrementally, starting with basic Rust structs and gradually adding the type-state pattern. This isn't just theory; we'll write real, working code that you can compile and run. By the end, you'll understand not just what the builder pattern is, but how to implement it in Rust using advanced type system features that make invalid states unrepresentable at compile time.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/warehouse-query-api">https://github.com/kartikmehta8/warehouse-query-api</a></div>
<p> </p>
<p>"Alright," Bob says, cracking knuckles. "Let's build this thing!"</p>
<h3 id="heading-project-setup">Project Setup</h3>
<pre><code class="lang-bash">cargo new warehouse-query-api
<span class="hljs-built_in">cd</span> warehouse-query-api
</code></pre>
<p>Update <code>Cargo.toml</code>:</p>
<pre><code class="lang-toml"><span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"warehouse-query-api"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">axum</span> = <span class="hljs-string">"0.7"</span>
<span class="hljs-attr">tokio</span> = { version = <span class="hljs-string">"1"</span>, features = [<span class="hljs-string">"full"</span>] }
<span class="hljs-attr">serde</span> = { version = <span class="hljs-string">"1.0"</span>, features = [<span class="hljs-string">"derive"</span>] }
<span class="hljs-attr">serde_json</span> = <span class="hljs-string">"1.0"</span>
<span class="hljs-attr">tower</span> = <span class="hljs-string">"0.4"</span>
<span class="hljs-attr">tower-http</span> = { version = <span class="hljs-string">"0.5"</span>, features = [<span class="hljs-string">"cors"</span>] }
<span class="hljs-attr">chrono</span> = { version = <span class="hljs-string">"0.4"</span>, features = [<span class="hljs-string">"serde"</span>] }
<span class="hljs-attr">reqwest</span> = { version = <span class="hljs-string">"0.11"</span>, features = [<span class="hljs-string">"json"</span>] }
<span class="hljs-attr">thiserror</span> = <span class="hljs-string">"1.0"</span>
</code></pre>
<p><strong>About the dependencies:</strong></p>
<ul>
<li><p><code>axum</code>: Web framework for building the HTTP API</p>
</li>
<li><p><code>tokio</code>: Async runtime (required by axum)</p>
</li>
<li><p><code>serde</code> &amp; <code>serde_json</code>: Serialization / deserialization</p>
</li>
<li><p><code>tower</code> &amp; <code>tower-http</code>: Middleware support</p>
</li>
<li><p><code>chrono</code>: Date/time handling</p>
</li>
<li><p><code>reqwest</code>: HTTP client for making requests</p>
</li>
<li><p><code>thiserror</code>: Easy error type creation</p>
</li>
</ul>
<h3 id="heading-define-the-target-struct">Define the Target Struct</h3>
<p>Create <code>src/models.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">//! Core data models for the Warehouse Query API.</span>

<span class="hljs-keyword">use</span> serde::{Deserialize, Serialize};
<span class="hljs-keyword">use</span> std::collections::HashMap;

<span class="hljs-comment">/// HTTP methods supported by the API.</span>
<span class="hljs-meta">#[derive(Debug, Clone, Serialize, Deserialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">HttpMethod</span></span> {
    GET,
    POST,
    PUT,
    DELETE,
    PATCH,
}

<span class="hljs-comment">/// Authentication mechanisms.</span>
<span class="hljs-meta">#[derive(Debug, Clone, Serialize, Deserialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">AuthType</span></span> {
    <span class="hljs-literal">None</span>,
    Bearer(<span class="hljs-built_in">String</span>),
    ApiKey(<span class="hljs-built_in">String</span>),
    Basic { username: <span class="hljs-built_in">String</span>, password: <span class="hljs-built_in">String</span> },
}

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">QueryParams</span></span> = HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;;
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Headers</span></span> = HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;;
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">BodyParams</span></span> = serde_json::Value;

<span class="hljs-comment">// ...</span>

<span class="hljs-comment">/// Request payload for building a query.</span>
<span class="hljs-meta">#[derive(Debug, Deserialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BuildQueryRequest</span></span> {
    <span class="hljs-keyword">pub</span> endpoint: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> method: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">String</span>&gt;,
    <span class="hljs-keyword">pub</span> query_params: <span class="hljs-built_in">Option</span>&lt;QueryParams&gt;,
    <span class="hljs-keyword">pub</span> headers: <span class="hljs-built_in">Option</span>&lt;Headers&gt;,
    <span class="hljs-keyword">pub</span> body: <span class="hljs-built_in">Option</span>&lt;BodyParams&gt;,
    <span class="hljs-keyword">pub</span> auth_token: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">String</span>&gt;,
    <span class="hljs-keyword">pub</span> timeout_seconds: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u64</span>&gt;,
    <span class="hljs-keyword">pub</span> retry_count: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u32</span>&gt;,
    <span class="hljs-keyword">pub</span> cache_results: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">bool</span>&gt;,
}

<span class="hljs-comment">/// Response containing the built query.</span>
<span class="hljs-meta">#[derive(Debug, Serialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BuildQueryResponse</span></span> {
    <span class="hljs-keyword">pub</span> request: WarehouseRequest,
    <span class="hljs-keyword">pub</span> message: <span class="hljs-built_in">String</span>,
}
</code></pre>
<p><strong>Understanding the derives:</strong></p>
<ul>
<li><p><code>Debug</code>: Allows printing the struct with <code>{:?}</code> for debugging</p>
</li>
<li><p><code>Clone</code>: Allows creating copies of the struct</p>
</li>
<li><p><code>Serialize</code>: Converts struct to JSON (via serde)</p>
</li>
<li><p><code>Deserialize</code>: Converts JSON to struct (via serde)</p>
</li>
</ul>
<p>Bob's note: "This is what we're trying to build. 9 fields, 6 optional. Perfect candidate for a builder!"</p>
<h3 id="heading-define-state-markers">Define State Markers</h3>
<p>Create <code>src/query_builder.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::marker::PhantomData;

<span class="hljs-comment">/// Marker trait for endpoint state.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">EndpointState</span></span> {}

<span class="hljs-comment">/// State: No endpoint set yet.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">NoEndpoint</span></span>;

<span class="hljs-comment">/// State: Endpoint has been set.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">HasEndpoint</span></span>;

<span class="hljs-keyword">impl</span> EndpointState <span class="hljs-keyword">for</span> NoEndpoint {}
<span class="hljs-keyword">impl</span> EndpointState <span class="hljs-keyword">for</span> HasEndpoint {}
</code></pre>
<p>Casey: "These are our type-level states. They exist only at compile time - zero runtime cost!"</p>
<p><strong>What are marker traits?</strong></p>
<p>Marker traits are traits with no methods. They're used purely for compile-time type checking. <code>EndpointState</code> is a marker trait that both <code>NoEndpoint</code> and <code>HasEndpoint</code> implement, allowing us to constrain our builder to only accept types that represent endpoint states.</p>
<h3 id="heading-create-the-builder-struct">Create the Builder Struct</h3>
<pre><code class="lang-rust"><span class="hljs-comment">/// The Builder.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">WarehouseQueryBuilder</span></span>&lt;E: EndpointState&gt; {
    endpoint: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">String</span>&gt;,
    method: HttpMethod,
    query_params: <span class="hljs-built_in">Option</span>&lt;QueryParams&gt;,
    headers: <span class="hljs-built_in">Option</span>&lt;Headers&gt;,
    body: <span class="hljs-built_in">Option</span>&lt;BodyParams&gt;,
    auth: AuthType,
    timeout_seconds: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u64</span>&gt;,
    retry_count: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">u32</span>&gt;,
    cache_results: <span class="hljs-built_in">bool</span>,
    _endpoint_state: PhantomData&lt;E&gt;,  <span class="hljs-comment">// Type-level state marker.</span>
}
</code></pre>
<p>Bob: "So the builder holds all the same fields as <code>WarehouseRequest</code>, plus a phantom type for state tracking."</p>
<h3 id="heading-implement-initial-state-noendpoint">Implement Initial State (NoEndpoint)</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;NoEndpoint&gt; {
    <span class="hljs-comment">/// Create a new builder.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// Starts in NoEndpoint state.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            endpoint: <span class="hljs-literal">None</span>,
            method: HttpMethod::GET,  <span class="hljs-comment">// Sensible default.</span>
            query_params: <span class="hljs-literal">None</span>,
            headers: <span class="hljs-literal">None</span>,
            body: <span class="hljs-literal">None</span>,
            auth: AuthType::<span class="hljs-literal">None</span>,
            timeout_seconds: <span class="hljs-literal">None</span>,
            retry_count: <span class="hljs-literal">None</span>,
            cache_results: <span class="hljs-literal">false</span>,
            _endpoint_state: PhantomData,
        }
    }

    <span class="hljs-comment">/// Set the endpoint. (REQUIRED)</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// This transitions from NoEndpoint to HasEndpoint state.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">endpoint</span></span>(<span class="hljs-keyword">self</span>, endpoint: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;) -&gt; WarehouseQueryBuilder&lt;HasEndpoint&gt; {
        WarehouseQueryBuilder {
            endpoint: <span class="hljs-literal">Some</span>(endpoint.into()),
            method: <span class="hljs-keyword">self</span>.method,
            query_params: <span class="hljs-keyword">self</span>.query_params,
            headers: <span class="hljs-keyword">self</span>.headers,
            body: <span class="hljs-keyword">self</span>.body,
            auth: <span class="hljs-keyword">self</span>.auth,
            timeout_seconds: <span class="hljs-keyword">self</span>.timeout_seconds,
            retry_count: <span class="hljs-keyword">self</span>.retry_count,
            cache_results: <span class="hljs-keyword">self</span>.cache_results,
            _endpoint_state: PhantomData,  <span class="hljs-comment">// New state!</span>
        }
    }
}
</code></pre>
<p><strong>What is</strong> <code>impl Into&lt;String&gt;</code>?</p>
<p>This is a trait bound that accepts anything that can be converted into a <code>String</code>. This includes <code>&amp;str</code>, <code>String</code>, and other types. It makes the API more flexible - users can pass <code>"reports"</code> (a string slice) instead of <code>"reports".to_string()</code>.</p>
<p><strong>Key insight</strong>: The <code>endpoint()</code> method <strong>consumes</strong> <code>self</code> (takes ownership) and <strong>returns</strong> a builder in a different state!</p>
<h3 id="heading-implement-builder-methods-hasendpoint">Implement Builder Methods (HasEndpoint)</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;HasEndpoint&gt; {
    <span class="hljs-comment">/// Set HTTP method.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, method: HttpMethod) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.method = method;
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set query parameters.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">query_params</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, params: QueryParams) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.query_params = <span class="hljs-literal">Some</span>(params);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Add a single query parameter.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">query_param</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, key: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;, value: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> params = <span class="hljs-keyword">self</span>.query_params.unwrap_or_default();
        params.insert(key.into(), value.into());
        <span class="hljs-keyword">self</span>.query_params = <span class="hljs-literal">Some</span>(params);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set headers.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">headers</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, headers: Headers) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.headers = <span class="hljs-literal">Some</span>(headers);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Add a single header.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">header</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, key: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;, value: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> headers = <span class="hljs-keyword">self</span>.headers.unwrap_or_default();
        headers.insert(key.into(), value.into());
        <span class="hljs-keyword">self</span>.headers = <span class="hljs-literal">Some</span>(headers);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set request body.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">body</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, body: BodyParams) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.body = <span class="hljs-literal">Some</span>(body);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set bearer token authentication.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">bearer_auth</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, token: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.auth = AuthType::Bearer(token.into());
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set timeout in seconds.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">timeout</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, seconds: <span class="hljs-built_in">u64</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.timeout_seconds = <span class="hljs-literal">Some</span>(seconds);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Set retry count.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">retry</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, count: <span class="hljs-built_in">u32</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.retry_count = <span class="hljs-literal">Some</span>(count);
        <span class="hljs-keyword">self</span>
    }

    <span class="hljs-comment">/// Enable result caching.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cache</span></span>(<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, enabled: <span class="hljs-built_in">bool</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">self</span>.cache_results = enabled;
        <span class="hljs-keyword">self</span>
    }
}
</code></pre>
<p>Bob: "Each method takes <code>mut self</code>, modifies it, and returns <code>self</code>. <mark>That's the 'fluent API' pattern!</mark>"</p>
<p>Casey: "Right! It enables method chaining: <code>.timeout(30).retry(3).cache(true)</code>"</p>
<p><strong>Understanding</strong> <code>mut self</code>:</p>
<p>When we write <code>mut self</code>, we're taking ownership of <code>self</code> and declaring we can mutate it. After modifying the builder, we return it so the next method in the chain can use it. This is different from <code>&amp;mut self</code>, which would borrow the builder mutably but not consume it.</p>
<h3 id="heading-implement-build-method">Implement build() Method</h3>
<pre><code class="lang-rust"><span class="hljs-comment">/// Build errors.</span>
<span class="hljs-meta">#[derive(Debug, thiserror::Error)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">BuildError</span></span> {
    <span class="hljs-meta">#[error(<span class="hljs-meta-string">"Endpoint cannot be empty"</span>)]</span>
    EmptyEndpoint,

    <span class="hljs-meta">#[error(<span class="hljs-meta-string">"Body is required for POST/PUT/PATCH requests"</span>)]</span>
    MissingBody,

    <span class="hljs-meta">#[error(<span class="hljs-meta-string">"Invalid JSON body: {0}"</span>)]</span>
    InvalidJson(<span class="hljs-meta">#[from]</span> serde_json::Error),
}

<span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;HasEndpoint&gt; {
    <span class="hljs-comment">/// Build the final WarehouseRequest.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// This method is ONLY available in HasEndpoint state!</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">build</span></span>(<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;WarehouseRequest, BuildError&gt; {
        <span class="hljs-comment">// Validation.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(<span class="hljs-keyword">ref</span> endpoint) = <span class="hljs-keyword">self</span>.endpoint {
            <span class="hljs-keyword">if</span> endpoint.is_empty() {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(BuildError::EmptyEndpoint);
            }
        }

        <span class="hljs-comment">// Method-specific validation.</span>
        <span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span>.method {
            HttpMethod::POST | HttpMethod::PUT | HttpMethod::PATCH =&gt; {
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.body.is_none() {
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(BuildError::MissingBody);
                }
            }
            _ =&gt; {}
        }

        <span class="hljs-literal">Ok</span>(WarehouseRequest {
            endpoint: <span class="hljs-keyword">self</span>.endpoint.unwrap(), <span class="hljs-comment">// Safe! Type system guarantees it's Some.</span>
            method: <span class="hljs-keyword">self</span>.method,
            query_params: <span class="hljs-keyword">self</span>.query_params,
            headers: <span class="hljs-keyword">self</span>.headers,
            body: <span class="hljs-keyword">self</span>.body,
            auth: <span class="hljs-keyword">self</span>.auth,
            timeout_seconds: <span class="hljs-keyword">self</span>.timeout_seconds,
            retry_count: <span class="hljs-keyword">self</span>.retry_count,
            cache_results: <span class="hljs-keyword">self</span>.cache_results,
        })
    }
}
</code></pre>
<p><strong>Understanding</strong> <code>Result&lt;T, E&gt;</code>:</p>
<p><code>Result</code> is Rust's way of handling operations that might fail. <code>Result&lt;WarehouseRequest, BuildError&gt;</code> means the function either returns a <code>WarehouseRequest</code> (wrapped in <code>Ok</code>) or a <code>BuildError</code> (wrapped in <code>Err</code>). The <code>?</code> operator can be used to propagate errors up the call stack.</p>
<p><strong>Understanding</strong> <code>thiserror</code>:</p>
<p>The <code>thiserror</code> crate makes it easy to create custom error types. The <code>#[error(...)]</code> attribute defines the error message, and <code>#[from]</code> automatically implements conversion from other error types (like <code>serde_json::Error</code>).</p>
<p>Bob is excited: "Look! The <code>unwrap()</code> on endpoint is safe because the type system guarantees it's <code>Some</code>. We're in <code>HasEndpoint</code> state, which means <code>.endpoint()</code> was called!"</p>
<p>Casey: "Exactly! No runtime panic possible here. The type system has our back."</p>
<h3 id="heading-add-preset-builders-bonus">Add Preset Builders (Bonus!)</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> WarehouseQueryBuilder&lt;NoEndpoint&gt; {
    <span class="hljs-comment">/// Preset for fetching reports. (GET request)</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fetch_reports</span></span>() -&gt; WarehouseQueryBuilder&lt;HasEndpoint&gt; {
        WarehouseQueryBuilder::new()
            .endpoint(<span class="hljs-string">"reports"</span>)
            .method(HttpMethod::GET)
            .cache(<span class="hljs-literal">true</span>)
    }

    <span class="hljs-comment">/// Preset for creating a report. (POST request)</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">create_report</span></span>() -&gt; WarehouseQueryBuilder&lt;HasEndpoint&gt; {
        WarehouseQueryBuilder::new()
            .endpoint(<span class="hljs-string">"reports"</span>)
            .method(HttpMethod::POST)
            .header(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    }

    <span class="hljs-comment">/// Preset for analytics query.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">analytics_query</span></span>() -&gt; WarehouseQueryBuilder&lt;HasEndpoint&gt; {
        WarehouseQueryBuilder::new()
            .endpoint(<span class="hljs-string">"analytics/query"</span>)
            .method(HttpMethod::POST)
            .timeout(<span class="hljs-number">300</span>)  <span class="hljs-comment">// Long-running queries.</span>
            .retry(<span class="hljs-number">3</span>)
    }
}
</code></pre>
<p>Bob: "These are awesome! Common patterns pre-configured. Users can just call <code>fetch_reports()</code> and customise from there!"</p>
<h3 id="heading-usage-examples">Usage Examples</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// Example 1: Simple GET request.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"reports"</span>)
    .build()?;

<span class="hljs-comment">// Example 2: GET with query params and auth.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"users"</span>)
    .query_param(<span class="hljs-string">"limit"</span>, <span class="hljs-string">"10"</span>)
    .query_param(<span class="hljs-string">"offset"</span>, <span class="hljs-string">"0"</span>)
    .bearer_auth(<span class="hljs-string">"my-token"</span>)
    .build()?;

<span class="hljs-comment">// Example 3: POST with body.</span>
<span class="hljs-keyword">let</span> body = serde_json::json!({
    <span class="hljs-string">"query"</span>: <span class="hljs-string">"SELECT * FROM sales WHERE year = 2024"</span>
});
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::new()
    .endpoint(<span class="hljs-string">"analytics/query"</span>)
    .method(HttpMethod::POST)
    .body(body)
    .bearer_auth(<span class="hljs-string">"my-token"</span>)
    .timeout(<span class="hljs-number">300</span>)
    .retry(<span class="hljs-number">3</span>)
    .build()?;

<span class="hljs-comment">// Example 4: Using a preset.</span>
<span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::fetch_reports()
    .query_param(<span class="hljs-string">"limit"</span>, <span class="hljs-string">"20"</span>)
    .bearer_auth(<span class="hljs-string">"my-token"</span>)
    .build()?;
</code></pre>
<h2 id="heading-the-complete-implementation">The Complete Implementation</h2>
<p>Let's see how everything fits together in the API.</p>
<h3 id="heading-the-warehouse-client">The Warehouse Client</h3>
<p>Create <code>src/warehouse_client.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::models::*;
<span class="hljs-keyword">use</span> crate::query_builder::WarehouseQueryBuilder;

<span class="hljs-comment">/// Client for executing warehouse API requests.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">WarehouseClient</span></span> {
    base_url: <span class="hljs-built_in">String</span>,
}

<span class="hljs-keyword">impl</span> WarehouseClient {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(base_url: <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Into</span>&lt;<span class="hljs-built_in">String</span>&gt;) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            base_url: base_url.into(),
        }
    }

    <span class="hljs-comment">/// Execute a warehouse request.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">execute</span></span>(&amp;<span class="hljs-keyword">self</span>, request: WarehouseRequest) -&gt; <span class="hljs-built_in">Result</span>&lt;WarehouseResponse, <span class="hljs-built_in">String</span>&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Executing warehouse request:"</span>);
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"   URL: {}/{}"</span>, <span class="hljs-keyword">self</span>.base_url, request.endpoint);
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"   Method: {:?}"</span>, request.method);

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(<span class="hljs-keyword">ref</span> params) = request.query_params {
            <span class="hljs-built_in">println!</span>(<span class="hljs-string">"   Query Params: {:?}"</span>, params);
        }

        <span class="hljs-comment">// Simulate response.</span>
        <span class="hljs-keyword">let</span> response_data = serde_json::json!({
            <span class="hljs-string">"success"</span>: <span class="hljs-literal">true</span>,
            <span class="hljs-string">"data"</span>: {
                <span class="hljs-string">"reports"</span>: [
                    {<span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"Sales Report Q1"</span>},
                    {<span class="hljs-string">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"Sales Report Q2"</span>}
                ],
                <span class="hljs-string">"total"</span>: <span class="hljs-number">2</span>
            }
        });

        <span class="hljs-literal">Ok</span>(WarehouseResponse {
            status: <span class="hljs-number">200</span>,
            data: response_data,
            cached: request.cache_results,
            execution_time_ms: <span class="hljs-number">150</span>,
        })
    }

    <span class="hljs-comment">/// Convenience method: Fetch reports with pagination.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fetch_reports</span></span>(&amp;<span class="hljs-keyword">self</span>, limit: <span class="hljs-built_in">u32</span>, offset: <span class="hljs-built_in">u32</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;WarehouseResponse, <span class="hljs-built_in">String</span>&gt; {
        <span class="hljs-comment">// Look how clean this is!</span>
        <span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::fetch_reports()
            .query_param(<span class="hljs-string">"limit"</span>, limit.to_string())
            .query_param(<span class="hljs-string">"offset"</span>, offset.to_string())
            .bearer_auth(<span class="hljs-string">"demo-token"</span>)
            .build()
            .map_err(|e| e.to_string())?;

        <span class="hljs-keyword">self</span>.execute(request).<span class="hljs-keyword">await</span>
    }
}
</code></pre>
<p><strong>Understanding</strong> <code>async</code> and <code>.await</code>:</p>
<p>In Rust, <code>async</code> functions return a <code>Future</code> that must be <code>.await</code>ed to execute. This enables efficient concurrent I/O operations. When you call an <code>async</code> function, it doesn't execute immediately - you need to <code>.await</code> it.</p>
<p>Bob: "Look how readable <code>fetch_reports()</code> is now! Before, it was 20 lines of struct initialization. Now it's 5 lines of fluent API calls!"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767296616123/27296307-998c-4790-be89-c743bddce9ed.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-testing-our-builder">Testing Our Builder</h2>
<h3 id="heading-running-the-server">Running the Server</h3>
<pre><code class="lang-bash">cargo run
</code></pre>
<p>Output:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Starting</span> <span class="hljs-string">Warehouse</span> <span class="hljs-string">Query</span> <span class="hljs-string">API...</span>

<span class="hljs-string">Warehouse</span> <span class="hljs-string">Query</span> <span class="hljs-string">API</span> <span class="hljs-string">running</span> <span class="hljs-string">on</span> <span class="hljs-string">http://127.0.0.1:3000</span>

<span class="hljs-attr">API Endpoints:</span>
   <span class="hljs-string">POST</span>   <span class="hljs-string">/query/build</span>    <span class="hljs-bullet">-</span> <span class="hljs-string">Build</span> <span class="hljs-string">a</span> <span class="hljs-string">warehouse</span> <span class="hljs-string">query</span>
   <span class="hljs-string">POST</span>   <span class="hljs-string">/query/execute</span>  <span class="hljs-bullet">-</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">execute</span> <span class="hljs-string">a</span> <span class="hljs-string">query</span>
   <span class="hljs-string">GET</span>    <span class="hljs-string">/query/examples</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Get</span> <span class="hljs-string">example</span> <span class="hljs-string">queries</span>
   <span class="hljs-string">GET</span>    <span class="hljs-string">/health</span>         <span class="hljs-bullet">-</span> <span class="hljs-string">Health</span> <span class="hljs-string">check</span>

<span class="hljs-string">Using</span> <span class="hljs-string">Builder</span> <span class="hljs-string">Pattern</span> <span class="hljs-string">for</span> <span class="hljs-string">flexible</span> <span class="hljs-string">query</span> <span class="hljs-string">construction!</span>
</code></pre>
<h3 id="heading-simple-get-request">Simple GET Request</h3>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/query/build \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "endpoint": "reports",
    "method": "GET",
    "cache_results": true
  }'</span>
</code></pre>
<p>Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"request"</span>: {
    <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"reports"</span>,
    <span class="hljs-attr">"method"</span>: <span class="hljs-string">"GET"</span>,
    <span class="hljs-attr">"query_params"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"headers"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"body"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"auth"</span>: <span class="hljs-string">"None"</span>,
    <span class="hljs-attr">"timeout_seconds"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"retry_count"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"cache_results"</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Query built successfully using builder pattern"</span>
}
</code></pre>
<p>Clean and simple! Only specified what we needed.</p>
<h3 id="heading-post-with-all-options">POST with All Options</h3>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/query/build \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "endpoint": "analytics/query",
    "method": "POST",
    "body": {
      "query": "SELECT * FROM sales WHERE year = 2024"
    },
    "auth_token": "secret-token-123",
    "timeout_seconds": 300,
    "retry_count": 3,
    "cache_results": false
  }'</span>
</code></pre>
<p>Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"request"</span>: {
    <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"analytics/query"</span>,
    <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
    <span class="hljs-attr">"query_params"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"headers"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"body"</span>: {
      <span class="hljs-attr">"query"</span>: <span class="hljs-string">"SELECT * FROM sales WHERE year = 2024"</span>
    },
    <span class="hljs-attr">"auth"</span>: {
      <span class="hljs-attr">"Bearer"</span>: <span class="hljs-string">"secret-token-123"</span>
    },
    <span class="hljs-attr">"timeout_seconds"</span>: <span class="hljs-number">300</span>,
    <span class="hljs-attr">"retry_count"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"cache_results"</span>: <span class="hljs-literal">false</span>
  },
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Query built successfully using builder pattern"</span>
}
</code></pre>
<p>Perfect! All options set clearly.</p>
<h3 id="heading-validation-missing-body">Validation (Missing Body)</h3>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/query/build \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "endpoint": "reports",
    "method": "POST"
  }'</span>
</code></pre>
<p>Response:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Status:</span> <span class="hljs-number">400</span> <span class="hljs-string">Bad</span> <span class="hljs-string">Request</span>
</code></pre>
<p>Validation works! POST request without body is rejected.</p>
<h2 id="heading-the-resolution">The Resolution</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767297081680/7664c36c-fdeb-47e7-a6db-959263265bf8.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-monday-morning-code-review">Monday Morning Code Review</h3>
<p>Bob submits the pull request with the new builder pattern implementation. Casey reviews it immediately.</p>
<p>Casey is impressed: "Bob, this is excellent work! Look at the diff stats:"</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Files changed:</span>
<span class="hljs-attr">src/query_builder.rs:</span> <span class="hljs-string">+350</span> <span class="hljs-string">lines</span> <span class="hljs-string">(new)</span>
<span class="hljs-attr">src/warehouse_client.rs:</span> <span class="hljs-number">-120</span> <span class="hljs-string">lines,</span> <span class="hljs-string">+45</span> <span class="hljs-string">lines</span>
<span class="hljs-attr">src/handlers.rs:</span> <span class="hljs-number">-80</span> <span class="hljs-string">lines,</span> <span class="hljs-string">+60</span> <span class="hljs-string">lines</span>
<span class="hljs-attr">tests/:</span> <span class="hljs-string">+150</span> <span class="hljs-string">lines</span> <span class="hljs-string">(new</span> <span class="hljs-string">tests)</span>
<span class="hljs-attr">Overall:</span> <span class="hljs-number">-200</span> <span class="hljs-string">lines</span> <span class="hljs-string">of</span> <span class="hljs-string">messy</span> <span class="hljs-string">code,</span> <span class="hljs-string">+400</span> <span class="hljs-string">lines</span> <span class="hljs-string">of</span> <span class="hljs-string">clean,</span> <span class="hljs-string">type-safe</span> <span class="hljs-string">code</span>
</code></pre>
<p>"But more importantly, look at the usage:"</p>
<p>Before:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> request = WarehouseRequest {
    endpoint: <span class="hljs-string">"analytics/query"</span>.to_string(),
    method: HttpMethod::POST,
    query_params: <span class="hljs-literal">None</span>,
    headers: <span class="hljs-literal">Some</span>(headers),
    body: <span class="hljs-literal">Some</span>(serde_json::json!({<span class="hljs-string">"query"</span>: query_string})),
    auth: AuthType::Bearer(token.clone()),
    timeout_seconds: <span class="hljs-literal">Some</span>(<span class="hljs-number">300</span>),
    retry_count: <span class="hljs-literal">Some</span>(<span class="hljs-number">3</span>),
    cache_results: <span class="hljs-literal">false</span>,
};
</code></pre>
<p>After:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> request = WarehouseQueryBuilder::analytics_query()
    .body(serde_json::json!({<span class="hljs-string">"query"</span>: query_string}))
    .bearer_auth(token)
    .build()?;
</code></pre>
<p><mark>"From 11 lines to 4 lines. And infinitely more readable!"</mark></p>
<h3 id="heading-team-adoption">Team Adoption</h3>
<p>The rest of the team starts using the builder. The results are immediate:</p>
<p><strong>Week 1:</strong></p>
<ul>
<li><p>5 pull requests updated to use builder</p>
</li>
<li><p>0 production incidents (down from 3-5 per week)</p>
</li>
<li><p>Code review time cut in half</p>
</li>
</ul>
<p><strong>Week 2:</strong></p>
<ul>
<li><p><em>Junior developer Sarah:</em> "I just built my first warehouse query. Took 5 minutes. The fluent API told me exactly what I needed!"</p>
</li>
<li><p><em>Senior developer Marcus:</em> "The type-state pattern caught 3 bugs in my code before I even ran it. Compiler is our best QA!"</p>
</li>
</ul>
<p><strong>Week 3:</strong></p>
<ul>
<li><p><em>Support team:</em> "We haven't had a single 'malformed request' ticket this week!"</p>
</li>
<li><p><em>DevOps:</em> "Error rates down 80%"</p>
</li>
</ul>
<h3 id="heading-metrics-comparison">Metrics Comparison</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Metric</td><td>Before Builder</td><td>After Builder</td><td>Improvement</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Lines of code</strong> (avg request)</td><td>11</td><td>4</td><td>63% reduction</td></tr>
<tr>
<td><strong>Time to write</strong></td><td>5-10 min</td><td>1-2 min</td><td>80% faster</td></tr>
<tr>
<td><strong>Runtime errors</strong></td><td>5-8/week</td><td>0-1/week</td><td>90% reduction</td></tr>
<tr>
<td><strong>Code review time</strong></td><td>15 min</td><td>5 min</td><td>67% faster</td></tr>
<tr>
<td><strong>Production incidents</strong></td><td>4/week</td><td>0/week</td><td>100% elimination</td></tr>
<tr>
<td><strong>Developer satisfaction</strong></td><td>3/10</td><td>9/10</td><td>3x improvement</td></tr>
</tbody>
</table>
</div><h3 id="heading-bobs-reflection">Bob's Reflection</h3>
<p>Bob writes in the team Slack:</p>
<blockquote>
<p>"Team update: The builder pattern has been a game-changer.</p>
<p>Before: I dreaded creating warehouse requests. Every time was a minefield of potential bugs.</p>
<p>After: I actually enjoy it! The fluent API guides me, the compiler catches my mistakes, and the code reads like English.</p>
<p>My favourite part? Zero-cost abstractions. All that type-safety has ZERO runtime overhead. It's compile-time magic!"</p>
</blockquote>
<p>Casey's response:</p>
<blockquote>
<p>"Great work Bob! This is what good software engineering looks like:</p>
<ol>
<li><p>Identify a real problem</p>
</li>
<li><p>Apply an appropriate pattern</p>
</li>
<li><p>Measure the improvement</p>
</li>
<li><p>Share the knowledge</p>
</li>
</ol>
<p>You've made our entire team more productive."</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767297366394/98aa2bd4-d5b3-4bd5-baed-2ea148f9a39d.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-when-to-use-and-not-use-builder-pattern">When to Use (and Not Use) Builder Pattern</h2>
<h3 id="heading-use-builder-pattern-when">Use Builder Pattern When:</h3>
<ol>
<li><p><strong>Object has many parameters (5+)</strong></p>
<ul>
<li><p>Configuration objects</p>
</li>
<li><p>API requests/responses</p>
</li>
<li><p>Complex domain objects</p>
</li>
<li><p>UI component props</p>
</li>
</ul>
</li>
<li><p><strong>Many parameters are optional</strong></p>
<ul>
<li><p>Default values make sense</p>
</li>
<li><p>Not all combinations are valid</p>
</li>
<li><p>Different use cases need different fields</p>
</li>
</ul>
</li>
<li><p><strong>Parameter order is confusing</strong></p>
<ul>
<li><p>Positional parameters are unclear</p>
</li>
<li><p>Multiple parameters of same type</p>
</li>
<li><p>Easy to mix up order</p>
</li>
</ul>
</li>
<li><p><strong>Immutability is desired</strong></p>
<ul>
<li><p>Build once, use many times</p>
</li>
<li><p>Thread-safe sharing</p>
</li>
<li><p>Functional programming style</p>
</li>
</ul>
</li>
<li><p><strong>Validation is complex</strong></p>
<ul>
<li><p>Cross-field validation</p>
</li>
<li><p>Business rules</p>
</li>
<li><p>Type-state enforcement</p>
</li>
</ul>
</li>
<li><p><strong>Fluent API improves readability</strong></p>
<ul>
<li><p>Code reads like English</p>
</li>
<li><p>Self-documenting</p>
</li>
<li><p>IDE autocomplete helps</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-dont-use-builder-pattern-when">Don't Use Builder Pattern When:</h3>
<ol>
<li><strong>Object is simple (1-3 parameters)</strong></li>
</ol>
<pre><code class="lang-rust"><span class="hljs-comment">// Overkill.</span>
<span class="hljs-keyword">let</span> user = UserBuilder::new()
    .name(<span class="hljs-string">"Alice"</span>)
    .build();

<span class="hljs-comment">// Better.</span>
<span class="hljs-keyword">let</span> user = User::new(<span class="hljs-string">"Alice"</span>);
</code></pre>
<ol start="2">
<li><strong>All parameters are required</strong></li>
</ol>
<pre><code class="lang-rust"><span class="hljs-comment">// Unnecessary.</span>
<span class="hljs-keyword">let</span> point = PointBuilder::new()
    .x(<span class="hljs-number">10.0</span>)
    .y(<span class="hljs-number">20.0</span>)
    .build();

<span class="hljs-comment">// Better.</span>
<span class="hljs-keyword">let</span> point = Point::new(<span class="hljs-number">10.0</span>, <span class="hljs-number">20.0</span>);
</code></pre>
<ol start="3">
<li><p><strong>Object is frequently modified</strong></p>
<ul>
<li><p>Builder creates immutable objects</p>
</li>
<li><p>If you need mutation, use direct field access</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When I first started working on this warehouse API client, I genuinely thought the problem was me. Every production incident felt like a personal failure - another timeout I forgot to set, another malformed request I should have caught. I kept detailed logs, trying to spot patterns in my mistakes, hoping I'd eventually memorize all the edge cases and stop breaking things.</p>
<p>But the real problem wasn't me. It was the API itself. <strong>When you force developers to specify nine fields in exact order, with no guidance about what's required and what's optional, you're not building an API - you're building a minefield.</strong> Every request becomes an exercise in careful counting, constant cross-referencing, and hoping you didn't miss anything important.</p>
<p>The builder pattern changed everything. Not because it's clever or elegant, but because it solves real problems that were costing us time, money, and sleep. Type-state builders let the compiler do what compilers do best: catch mistakes before they become production incidents. The code is more readable, yes, but more importantly, it's safer. My junior teammates can write correct requests on their first try because the fluent API guides them. Our error rates dropped not through better discipline, but through better design.</p>
<p>This is what good engineering looks like. Not clever abstractions for their own sake, but practical solutions to concrete problems. The builder pattern isn't perfect for everything, but for complex object construction with validation requirements, it's been transformative. If you're fighting with constructors that have too many parameters or dealing with runtime errors that should be compile-time errors, give builders a try. Your future self will thank you.</p>
<p><strong>Thanks for reading! May your APIs always be fluent!</strong></p>
<p><em>Written by</em> Bob*, Senior Developer and Builder Pattern enthusiast.*</p>
]]></content:encoded></item><item><title><![CDATA[The Configuration Chaos]]></title><description><![CDATA[Meet Jordan. Six months ago, Jordan joined a promising startup called "FastAPI Co." as a DevOps engineer. The company's main product is a high-traffic API service that handles configuration management for thousands of micro-services. At peak times, t...]]></description><link>https://writer.mrmehta.in/the-configuration-chaos</link><guid isPermaLink="true">https://writer.mrmehta.in/the-configuration-chaos</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[Singleton Design Pattern]]></category><category><![CDATA[Threads]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sat, 20 Dec 2025 19:20:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766249760526/84458ca7-e88d-4316-80a9-881092018498.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766250905565/c8289994-f538-44b7-9568-265b9e7dbd2c.png" alt /></p>
<p>Meet <strong>Jordan</strong>. Six months ago, Jordan joined a promising startup called "FastAPI Co." as a DevOps engineer. <mark>The company's main product is a high-traffic API service that handles configuration management for thousands of micro-services.</mark> At peak times, they process 5,000+ requests per minute.</p>
<p>Everything seemed fine during the first few months. The monitoring metrics looked acceptable, users were happy, and the product was growing. But then came <strong>December 20th, 2025</strong>.</p>
<h3 id="heading-the-incident">The Incident</h3>
<p>It's 3:17 AM on a cold Friday morning. Jordan's phone vibrates violently on the nightstand, then starts blaring alerts:</p>
<pre><code class="lang-yaml"><span class="hljs-string">🚨</span> <span class="hljs-attr">CRITICAL: HIGH MEMORY USAGE:</span> <span class="hljs-number">95</span><span class="hljs-string">%</span> <span class="hljs-string">(Threshold:</span> <span class="hljs-number">80</span><span class="hljs-string">%)</span>
<span class="hljs-string">🚨</span> <span class="hljs-attr">CRITICAL: DATABASE:</span> <span class="hljs-string">Too</span> <span class="hljs-string">many</span> <span class="hljs-string">connections</span> <span class="hljs-string">(ERROR</span> <span class="hljs-number">1040</span><span class="hljs-string">)</span>
<span class="hljs-string">🚨</span> <span class="hljs-attr">WARNING: RESPONSE TIME:</span> <span class="hljs-string">5247ms</span> <span class="hljs-string">(Normal:</span> <span class="hljs-number">45</span><span class="hljs-string">-60ms)</span>
<span class="hljs-string">🚨</span> <span class="hljs-attr">CRITICAL:</span> <span class="hljs-string">SERVER</span> <span class="hljs-string">CRASH</span> <span class="hljs-string">IMMINENT</span> <span class="hljs-bullet">-</span> <span class="hljs-string">OOM</span> <span class="hljs-string">Killer</span> <span class="hljs-string">Active</span>
<span class="hljs-string">🚨</span> <span class="hljs-attr">ALERT: Error rate:</span> <span class="hljs-number">45</span><span class="hljs-string">%</span> <span class="hljs-string">(Normal:</span> <span class="hljs-string">&lt;0.1%)</span>
</code></pre>
<p>Jordan jolts awake, grabs the laptop with shaking hands, and logs into the monitoring dashboard. The graphs look like a horror movie:</p>
<p><strong>Monitoring Dashboard Snapshot:</strong></p>
<ul>
<li><p><strong>Memory usage</strong>: Climbing steadily from 2GB to 7.8GB in 90 minutes</p>
</li>
<li><p><strong>Database connection pools</strong>: 523 active pools!?</p>
</li>
<li><p><strong>Config file reads</strong>: 14,247 in the last hour</p>
</li>
<li><p><strong>Active database connections</strong>: 8,941 (PostgreSQL limit: 100)</p>
</li>
<li><p><strong>Cache misses</strong>: 99.7%</p>
</li>
</ul>
<p><strong>Jordan's internal monologue:</strong></p>
<blockquote>
<p>"What the hell is happening? Why are there 523 database connection pools? We should only need ONE! And why is the config file being read 14 THOUSAND times? We have caching, don't we? Why are different parts of the app seeing DIFFERENT configuration values? This doesn't make ANY sense! We're about to hit OOM and the database is rejecting connections... I need to figure this out NOW."</p>
</blockquote>
<h3 id="heading-the-current-code">The Current Code</h3>
<p>Jordan opens the codebase and starts tracing execution paths. What they find is terrifying:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// ❌ THE PROBLEM CODE - What Jordan inherited.</span>
<span class="hljs-comment">// This code is scattered across 30+ files!</span>

<span class="hljs-comment">// In src/database.rs</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">connect_to_database</span></span>() -&gt; DatabaseConnection {
    <span class="hljs-comment">// PROBLEM 1: Reading config file from disk EVERY TIME!</span>
    <span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);

    <span class="hljs-comment">// PROBLEM 2: Creating a NEW connection pool EVERY TIME!</span>
    <span class="hljs-keyword">let</span> pool = ConnectionPool::new(
        &amp;config.database_url,
        config.max_connections  <span class="hljs-comment">// Each pool holds 10-50 connections!</span>
    );

    DatabaseConnection { pool }
}

<span class="hljs-comment">// In src/handlers.rs</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle_api_request</span></span>(req: Request) -&gt; Response {
    <span class="hljs-comment">// ANOTHER config read from disk!</span>
    <span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);

    <span class="hljs-comment">// Each request reads config for feature flags.</span>
    <span class="hljs-keyword">if</span> config.enable_feature_x {
        <span class="hljs-comment">// Process with feature X.</span>
    }

    <span class="hljs-keyword">if</span> config.enable_caching {
        <span class="hljs-comment">// Check cache... but wait, cache is also initialized per request!</span>
        <span class="hljs-keyword">let</span> cache = Cache::new(config.cache_size);  <span class="hljs-comment">// NEW CACHE!</span>
    }

    <span class="hljs-comment">// ... more logic.</span>
}

<span class="hljs-comment">// In src/logger.rs</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">log</span></span>(level: &amp;<span class="hljs-built_in">str</span>, message: &amp;<span class="hljs-built_in">str</span>) {
    <span class="hljs-comment">// YET ANOTHER config read!</span>
    <span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);

    <span class="hljs-comment">// Check log level threshold.</span>
    <span class="hljs-keyword">if</span> should_log(level, &amp;config.log_level) {
        <span class="hljs-comment">// Initialize NEW logger instance!</span>
        <span class="hljs-keyword">let</span> logger = Logger::new(&amp;config.log_file_path);  <span class="hljs-comment">// NEW LOGGER!</span>
        logger.write(level, message);
    }
}

<span class="hljs-comment">// In src/middleware/auth.rs</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">auth_middleware</span></span>(req: Request, next: Next) -&gt; Response {
    <span class="hljs-comment">// ANOTHER ONE!</span>
    <span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);

    <span class="hljs-keyword">let</span> start = Instant::now();

    <span class="hljs-keyword">let</span> response = next.run(req).<span class="hljs-keyword">await</span>;

    <span class="hljs-comment">// Check timeout.</span>
    <span class="hljs-keyword">if</span> start.elapsed().as_secs() &gt; config.api_timeout_seconds {
        <span class="hljs-keyword">return</span> timeout_response();
    }

    response
}

<span class="hljs-comment">// This pattern repeats in 30+ files across the codebase!</span>
<span class="hljs-comment">// EVERY function that needs configuration creates NEW instances of EVERYTHING!</span>
</code></pre>
<h3 id="heading-the-symptoms">The Symptoms</h3>
<p>Jordan creates a comprehensive diagram to visualise what's actually happening:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766251424352/3a8df3f5-6eb3-4f8b-b0d9-dedf5ac80e80.png" alt class="image--center mx-auto" /></p>
<p><strong>The Pain Points:</strong></p>
<ol>
<li><p><strong>Performance Killer - Repeated Disk I/O</strong></p>
<ul>
<li><p><strong>What</strong>: Config file read from disk on EVERY request</p>
</li>
<li><p><strong>Why it's bad</strong>: Disk I/O is 100,000x slower than RAM access</p>
</li>
<li><p><strong>Impact</strong>: Each request wasted 2-6ms just reading config</p>
</li>
<li><p><strong>Math</strong>: 5,000 requests/min × 4ms = 20,000ms = 20 seconds wasted per minute!</p>
</li>
</ul>
</li>
<li><p><strong>Memory Leak - Resource Multiplication</strong></p>
<ul>
<li><p><strong>What</strong>: New database connection pool created for each request</p>
</li>
<li><p><strong>Why it's bad</strong>: Each pool holds 10-50 database connections</p>
</li>
<li><p><strong>Impact</strong>: 500 requests = 500 pools = 5,000-25,000 DB connections</p>
</li>
<li><p><strong>Math</strong>: PostgreSQL default max_connections = 100. We hit that in seconds!</p>
</li>
</ul>
</li>
<li><p><strong>Resource Exhaustion - Database Rejection</strong></p>
<ul>
<li><p><strong>What</strong>: Database refuses new connections -3.</p>
</li>
<li><p><strong>Why it's bad</strong>: Database has a hard limit on connections</p>
</li>
<li><p><strong>Impact</strong>: Requests start failing with "Too many connections" error</p>
</li>
<li><p><strong>Math</strong>: After 10-20 requests, database connection limit reached</p>
</li>
</ul>
</li>
<li><p><strong>Race Conditions - Inconsistent State</strong></p>
<ul>
<li><p><strong>What</strong>: Config file updated during request processing</p>
</li>
<li><p><strong>Why it's bad</strong>: Different threads see different configuration</p>
</li>
<li><p><strong>Impact</strong>: Inconsistent behaviour, hard-to-debug issues</p>
</li>
<li><p><strong>Example</strong>: Thread A sees max_connections=10, Thread B sees 50</p>
</li>
</ul>
</li>
<li><p><strong>Initialisation Storm - Repeated Setup</strong></p>
<ul>
<li><p><strong>What</strong>: Logger, cache, metrics collectors all initialised repeatedly</p>
</li>
<li><p><strong>Why it's bad</strong>: Initialisation is expensive (allocations, setup)</p>
</li>
<li><p><strong>Impact</strong>: CPU cycles wasted, memory fragmented</p>
</li>
<li><p><strong>Math</strong>: 1ms per initialisation × 500 requests = 500ms wasted</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-the-disaster">The Disaster</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766251789825/616f8c5d-1392-4fd6-8b14-e0b6892913b4.png" alt class="image--center mx-auto" /></p>
<p>Let's break down EXACTLY what's happening at the system level. Understanding the problem deeply is crucial for appreciating why the Singleton pattern is the right solution.</p>
<h3 id="heading-problem-1-repeated-file-io-the-io-bottleneck">Problem 1: Repeated File I/O (The I/O Bottleneck)</h3>
<p>Imagine if every time you wanted to check the time, you had to walk to the library, find a clock on the wall, read it, and walk back. That's essentially what's happening here. The application is reading the configuration file from disk for every single operation that needs config data, instead of keeping it in memory. <strong>Disk operations are incredibly slow compared to memory access</strong>, we're talking about a difference of 100,000x in speed. This seemingly innocent function call is creating a massive performance bottleneck that compounds with every request.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// This innocent-looking function is called THOUSANDS of times!</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_config_from_file</span></span>(path: &amp;<span class="hljs-built_in">str</span>) -&gt; Config {
    <span class="hljs-comment">// Step 1: Open file. (syscall to operating system)</span>
    <span class="hljs-keyword">let</span> file = File::open(path).unwrap();  <span class="hljs-comment">// ~1-2ms</span>

    <span class="hljs-comment">// Step 2: Create buffered reader.</span>
    <span class="hljs-keyword">let</span> reader = BufReader::new(file);

    <span class="hljs-comment">// Step 3: Parse JSON. (CPU-intensive)</span>
    serde_json::from_reader(reader).unwrap()  <span class="hljs-comment">// ~0.5-1ms</span>
}
</code></pre>
<p><strong>What's actually happening under the hood:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766252487857/bc4abe0f-a570-45bc-97d9-017dfeb4ce0f.png" alt class="image--center mx-auto" /></p>
<p><strong>Performance Reality Check:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Operation</strong></td><td><strong>Time (Best Case)</strong></td><td><strong>Time (Worst Case)</strong></td><td><strong>Notes</strong></td></tr>
</thead>
<tbody>
<tr>
<td>System call (open)</td><td>1-2 μs</td><td>50-100 μs</td><td>Context switch to kernel</td></tr>
<tr>
<td>Disk seek</td><td>0 μs (cache hit)</td><td>5-10 ms (cache miss)</td><td>SSD vs HDD matters</td></tr>
<tr>
<td>Read file (1KB)</td><td>10-50 μs</td><td>500-1000 μs</td><td>Depends on disk cache</td></tr>
<tr>
<td>JSON parsing</td><td>100-200 μs</td><td>1-2 ms</td><td>CPU-bound operation</td></tr>
<tr>
<td><strong>TOTAL</strong></td><td><strong>~200 μs</strong></td><td><strong>~5-10 ms</strong></td><td>Depends on caching</td></tr>
</tbody>
</table>
</div><p><strong>Multiplication Effect:</strong></p>
<ul>
<li><p><strong>1 request</strong>: ~2ms for config (acceptable)</p>
</li>
<li><p><strong>100 requests</strong>: ~200ms wasted (noticeable)</p>
</li>
<li><p><strong>1,000 requests</strong>: ~2 seconds wasted (bad)</p>
</li>
<li><p><strong>5,000 requests/min</strong>: ~<mark>10 seconds wasted per minute (catastrophic!)</mark></p>
</li>
</ul>
<p><strong>Memory Impact:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// Each config read allocates memory:</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Config</span></span> {
    server_host: <span class="hljs-built_in">String</span>,        <span class="hljs-comment">// 24 bytes + heap allocation.</span>
    server_port: <span class="hljs-built_in">u16</span>,           <span class="hljs-comment">// 2 bytes.</span>
    database_url: <span class="hljs-built_in">String</span>,       <span class="hljs-comment">// 24 bytes + heap allocation.</span>
    max_connections: <span class="hljs-built_in">u32</span>,       <span class="hljs-comment">// 4 bytes.</span>
    log_level: <span class="hljs-built_in">String</span>,          <span class="hljs-comment">// 24 bytes + heap allocation.</span>
    <span class="hljs-comment">// ... more fields.</span>
}

<span class="hljs-comment">// Total: ~150-200 bytes per Config instance.</span>
<span class="hljs-comment">// 5,000 requests × 200 bytes = 1MB just for config copies!</span>
<span class="hljs-comment">// But wait... each config is cloned in multiple places!</span>
<span class="hljs-comment">// Real impact: 5-10 MB wasted memory per minute.</span>
</code></pre>
<h3 id="heading-problem-2-resource-multiplication">Problem 2: Resource Multiplication</h3>
<p>This is where things get truly catastrophic. Every time a request needs database access, it creates a brand new connection pool. <mark>Think of it like building an entire new highway system every time a single car wants to travel somewhere.</mark> Each connection pool maintains multiple active TCP connections to the database, consuming memory and holding valuable database connection slots. With hundreds of concurrent requests, we're creating hundreds of pools, thousands of connections, and exhausting both our server's memory and the database's connection limit. The database eventually says "no more!" and starts rejecting connections, causing a cascade of failures.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Every request creates a NEW connection pool!</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DatabaseConnection</span></span> {
    pool: ConnectionPool,  <span class="hljs-comment">// This is the killer.</span>
}

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ConnectionPool</span></span> {
    connections: <span class="hljs-built_in">Vec</span>&lt;Connection&gt;,  <span class="hljs-comment">// Holds 10-50 actual TCP connections!</span>
    url: <span class="hljs-built_in">String</span>,
    max_size: <span class="hljs-built_in">u32</span>,
}
</code></pre>
<p><strong>Visual representation of resource multiplication:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766252844305/8840406c-fcfa-45e1-86d6-2cabd911e00b.png" alt class="image--center mx-auto" /></p>
<p><strong>Database Connection States:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766253101541/3bd80b6c-0ab4-4add-9cd4-d731a08aa9ff.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-problem-3-inconsistent-state-the-race-condition">Problem 3: Inconsistent State (The Race Condition)</h3>
<p>When multiple threads are reading the configuration file independently, they can end up with different versions of the config at the same time. This is like having a company where the sales team is looking at last month's price list while the support team is looking at this month's updated prices! If an administrator updates the config file while the application is running, some threads will see the old values while others see the new ones. This creates unpredictable behavior that's extremely difficult to debug because it only happens under specific timing conditions.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Scenario: Sys admin updates config while app is running.</span>

<span class="hljs-comment">// Timeline:</span>
<span class="hljs-comment">// T=0: config.json contains: { "max_connections": 10 }</span>

<span class="hljs-comment">// T=1: Thread 1 starts processing request</span>
<span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);
<span class="hljs-comment">// Thread 1 sees: max_connections = 10</span>

<span class="hljs-comment">// T=2: Sys admin updates config.json</span>
<span class="hljs-comment">// New content: { "max_connections": 50 }</span>

<span class="hljs-comment">// T=3: Thread 2 starts processing request</span>
<span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);
<span class="hljs-comment">// Thread 2 sees: max_connections = 50</span>

<span class="hljs-comment">// T=4: Thread 1 creates database connection with max=10</span>
<span class="hljs-keyword">let</span> db1 = DatabaseConnection::new(&amp;config.database_url, <span class="hljs-number">10</span>);

<span class="hljs-comment">// T=5: Thread 2 creates database connection with max=50</span>
<span class="hljs-keyword">let</span> db2 = DatabaseConnection::new(&amp;config.database_url, <span class="hljs-number">50</span>);

<span class="hljs-comment">// PROBLEM: Two different configurations in the same running application!</span>
<span class="hljs-comment">// This leads to:</span>
<span class="hljs-comment">// - Unpredictable behavior</span>
<span class="hljs-comment">// - Hard-to-reproduce bugs</span>
<span class="hljs-comment">// - Inconsistent resource limits</span>
<span class="hljs-comment">// - Data race conditions</span>
</code></pre>
<p><strong>Visual timeline of the race condition:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766253361935/e33f260c-fd59-4cff-a418-09472d1ffe52.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-problem-4-the-memory-usage-graph">Problem 4: The Memory Usage Graph</h3>
<p>Numbers and code are important, but sometimes a graph tells the story best. When Jordan pulled up the monitoring dashboard, the memory usage graph painted a terrifying picture: a steady, relentless climb from normal operating levels to near-crash conditions in just under two hours. <mark>This isn't a memory leak in the traditional sense, the memory is technically being used, but it's being wasted on thousands of duplicate instances of objects that should exist only once.</mark> The graph shows the inevitable trajectory toward system failure, with memory consumption accelerating as traffic increases.</p>
<p>Jordan pulls up the actual monitoring dashboard and exports the data:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766253500389/5f10185f-185c-4640-ae22-21fa378f900c.png" alt class="image--center mx-auto" /></p>
<p><strong>Jordan's realisation:</strong></p>
<blockquote>
<p>"Oh my god. We're not reusing ANYTHING! Every single request creates new instances of everything. <mark>We have 523 connection pools when we should have ONE. We have 892 logger instances when we should have ONE. We have 456 cache instances when we should have ONE!</mark></p>
<p>The actual application data - the stuff users care about - is only 10% of our memory usage. The other 90% is completely wasted duplication.</p>
<p>We need ONE config, ONE logger, ONE connection pool, ONE cache manager. Everything should be shared safely across all requests. But how do we do that in Rust?"</p>
</blockquote>
<h2 id="heading-the-breaking-point">The Breaking Point</h2>
<p>Monday morning, 9:00 AM. The post-incident review meeting. The atmosphere is tense.</p>
<p><strong>Attendees:</strong></p>
<ul>
<li><p>Maria (CTO)</p>
</li>
<li><p>Alex (Senior Backend Engineer)</p>
</li>
<li><p>Jordan (DevOps Engineer - looking exhausted)</p>
</li>
<li><p>Sarah (Product Manager)</p>
</li>
<li><p>Mike (Database Administrator)</p>
</li>
</ul>
<p><strong>Maria</strong> <em>(pulling up crash reports)</em>: "Alright team. Let's break down what happened Friday night. Jordan, can you walk us through the incident?"</p>
<p><strong>Jordan</strong> <em>(opening laptop, screen showing monitoring graphs)</em>: "At 3:17 AM, we had a catastrophic failure. Memory usage went from normal 2GB to 7.8GB in 90 minutes. The database started rejecting connections. Response times went from 50ms to over 5 seconds. The system crashed at 3:42 AM."</p>
<p><strong>Mike</strong> <em>(database admin, frustrated)</em>: "I was getting alerts about too many connections. We have a limit of 100 concurrent connections. But I was seeing connection attempts in the thousands! How is that even possible?"</p>
<p><strong>Jordan</strong> <em>(pulling up code)</em>: "I traced through the codebase. Every single request creates a new database connection pool. Each pool tries to establish 10 connections. So after just 10-20 requests, we hit the database limit."</p>
<p><strong>Alex</strong> <em>(leaning forward)</em>: "Wait, what? Show me the code."</p>
<p><strong>Jordan</strong> <em>(sharing screen)</em>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// This is in our request handler.</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle_request</span></span>(req: Request) -&gt; Response {
    <span class="hljs-keyword">let</span> config = read_config_from_file(<span class="hljs-string">"config.json"</span>);
    <span class="hljs-keyword">let</span> db = DatabaseConnection::new(&amp;config);  <span class="hljs-comment">// Creates NEW pool!</span>
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p><strong>Alex</strong> <em>(eyes widening)</em>: "Oh no. OH NO. Are we reading the config file on every request?"</p>
<p><strong>Jordan</strong> <em>(nodding grimly)</em>: "Not just every request. Every FUNCTION that needs config. I counted 30+ places in the codebase. We're reading that file thousands of times per minute."</p>
<p><strong>Sarah</strong> <em>(confused)</em>: "But... why doesn't it just use the same config? <mark>Can't we just... keep it in memory?</mark>"</p>
<p><strong>Alex</strong>: "We can and we should! <strong>What we're seeing here is the classic anti-pattern: no singleton</strong>. We're creating new instances of everything that should be shared."</p>
<p><strong>Jordan</strong>: "Singleton? I've heard the term but never really understood it. What is it?"</p>
<p><strong>Alex</strong> <em>(standing up and walking to the whiteboard)</em>: "Alright, let me explain. <mark>A singleton is a design pattern that ensures you have exactly ONE instance of something in your entire application. Not two, not a thousand - ONE.</mark>"</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://refactoring.guru/design-patterns/singleton/rust/example">https://refactoring.guru/design-patterns/singleton/rust/example</a></div>
<p> </p>
<p><em>Alex draws on the whiteboard:</em></p>
<pre><code class="lang-yaml"><span class="hljs-string">Current</span> <span class="hljs-string">State</span> <span class="hljs-string">(THE</span> <span class="hljs-string">PROBLEM):</span>
<span class="hljs-string">============================</span>
<span class="hljs-string">Request</span> <span class="hljs-number">1</span> <span class="hljs-string">→</span> <span class="hljs-string">Config</span> <span class="hljs-comment">#1, DB Pool #1, Logger #1, Cache #1</span>
<span class="hljs-string">Request</span> <span class="hljs-number">2</span> <span class="hljs-string">→</span> <span class="hljs-string">Config</span> <span class="hljs-comment">#2, DB Pool #2, Logger #2, Cache #2</span>
<span class="hljs-string">Request</span> <span class="hljs-number">3</span> <span class="hljs-string">→</span> <span class="hljs-string">Config</span> <span class="hljs-comment">#3, DB Pool #3, Logger #3, Cache #3</span>
<span class="hljs-string">...</span>
<span class="hljs-string">Request</span> <span class="hljs-string">N</span> <span class="hljs-string">→</span> <span class="hljs-string">Config</span> <span class="hljs-comment">#N, DB Pool #N, Logger #N, Cache #N</span>

<span class="hljs-attr">Result:</span> <span class="hljs-string">N</span> <span class="hljs-string">requests</span> <span class="hljs-string">=</span> <span class="hljs-string">N</span> <span class="hljs-string">instances</span> <span class="hljs-string">of</span> <span class="hljs-string">EVERYTHING!</span>
<span class="hljs-attr">Memory:</span> <span class="hljs-number">500</span> <span class="hljs-string">requests</span> <span class="hljs-string">×</span> <span class="hljs-string">15MB</span> <span class="hljs-string">=</span> <span class="hljs-number">7.</span><span class="hljs-string">5GB</span>
<span class="hljs-attr">DB connections:</span> <span class="hljs-number">500</span> <span class="hljs-string">pools</span> <span class="hljs-string">×</span> <span class="hljs-number">10</span> <span class="hljs-string">=</span> <span class="hljs-number">5</span><span class="hljs-string">,000</span> <span class="hljs-string">connections</span>
<span class="hljs-attr">Config file reads:</span> <span class="hljs-number">500</span> <span class="hljs-string">requests</span> <span class="hljs-string">×</span> <span class="hljs-number">6</span> <span class="hljs-string">functions</span> <span class="hljs-string">=</span> <span class="hljs-number">3</span><span class="hljs-string">,000</span> <span class="hljs-string">I/O</span> <span class="hljs-string">operations!</span>

<span class="hljs-string">Desired</span> <span class="hljs-string">State</span> <span class="hljs-string">(WITH</span> <span class="hljs-string">SINGLETON):</span>
<span class="hljs-string">================================</span>
<span class="hljs-string">Request</span> <span class="hljs-number">1</span> <span class="hljs-string">↘</span>
<span class="hljs-string">Request</span> <span class="hljs-number">2</span> <span class="hljs-string">→</span> <span class="hljs-string">ONE</span> <span class="hljs-string">Config,</span> <span class="hljs-string">ONE</span> <span class="hljs-string">DB</span> <span class="hljs-string">Pool,</span> <span class="hljs-string">ONE</span> <span class="hljs-string">Logger,</span> <span class="hljs-string">ONE</span> <span class="hljs-string">Cache</span>
<span class="hljs-string">Request</span> <span class="hljs-number">3</span> <span class="hljs-string">↗</span>
<span class="hljs-string">Request</span> <span class="hljs-number">4</span> <span class="hljs-string">↗</span>
<span class="hljs-string">...</span>
<span class="hljs-string">Request</span> <span class="hljs-string">N</span> <span class="hljs-string">↗</span>

<span class="hljs-attr">Result:</span> <span class="hljs-string">N</span> <span class="hljs-string">requests</span> <span class="hljs-string">=</span> <span class="hljs-number">1</span> <span class="hljs-string">instance</span> <span class="hljs-string">of</span> <span class="hljs-string">shared</span> <span class="hljs-string">resources!</span>
<span class="hljs-attr">Memory:</span> <span class="hljs-string">15MB</span> <span class="hljs-string">(constant,</span> <span class="hljs-string">regardless</span> <span class="hljs-string">of</span> <span class="hljs-string">request</span> <span class="hljs-string">count!)</span>
<span class="hljs-attr">DB connections:</span> <span class="hljs-number">1</span> <span class="hljs-string">pool</span> <span class="hljs-string">×</span> <span class="hljs-number">10</span> <span class="hljs-string">=</span> <span class="hljs-number">10</span> <span class="hljs-string">connections</span>
<span class="hljs-attr">Config file reads:</span> <span class="hljs-number">1</span> <span class="hljs-string">(total,</span> <span class="hljs-string">for</span> <span class="hljs-string">entire</span> <span class="hljs-string">application</span> <span class="hljs-string">lifetime!)</span>
</code></pre>
<p><strong>Maria</strong>: "So we need to refactor to use singletons. How long will this take?"</p>
<p><strong>Alex</strong>: "In most languages, creating thread-safe singletons is tricky and error-prone. But in Rust, the compiler helps us. We can use <code>Arc</code>, <code>RwLock</code>, and <code>once_cell</code> to create singletons that are thread-safe by default."</p>
<p><strong>Jordan</strong> <em>(determined)</em>: "I can do this. Give me until Wednesday, and I'll have a solution."</p>
<p><strong>Alex</strong>: "I'll pair with you. This is important to get right."</p>
<p><strong>Maria</strong>: "Alright. Jordan and Alex, this is your top priority. Sarah, let's communicate with customers about Friday's downtime. Mike, increase the database connection limit temporarily as a band-aid, but we know the real fix needs to happen in the code."</p>
<p><strong>Mike</strong>: "Already done. I bumped it to 500, but that's just delaying the inevitable if we don't fix the root cause."</p>
<p><strong>Jordan</strong>: "We'll fix the root cause. No more band-aids."</p>
<h2 id="heading-the-discovery">The Discovery</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766254502235/ebb7093c-4fe4-464b-84b5-f55aaac06fa8.png" alt class="image--center mx-auto" /></p>
<p><strong>Alex</strong>: "Alright Jordan, let's build your understanding from the ground up. First, what do you think a singleton is?"</p>
<p><strong>Jordan</strong>: "From what you said yesterday... it's a way to ensure only one instance of something exists?"</p>
<p><strong>Alex</strong>: "Exactly! Let me give you some real-world analogies to make this concrete."</p>
<h3 id="heading-real-world-singleton-examples">Real-World Singleton Examples</h3>
<p><strong>Alex's whiteboard:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">Real-World Singletons:</span>
<span class="hljs-string">======================</span>

<span class="hljs-number">1</span><span class="hljs-string">.</span> <span class="hljs-string">The</span> <span class="hljs-string">Sun</span> <span class="hljs-string">in</span> <span class="hljs-string">our</span> <span class="hljs-string">solar</span> <span class="hljs-string">system</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">We</span> <span class="hljs-string">have</span> <span class="hljs-string">exactly</span> <span class="hljs-string">ONE</span> <span class="hljs-string">sun</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Everyone</span> <span class="hljs-string">on</span> <span class="hljs-string">Earth</span> <span class="hljs-string">sees</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">sun</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">You</span> <span class="hljs-string">can't</span> <span class="hljs-string">create</span> <span class="hljs-string">a</span> <span class="hljs-string">second</span> <span class="hljs-string">sun</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">It</span> <span class="hljs-string">exists</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">lifetime</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">solar</span> <span class="hljs-string">system</span>

<span class="hljs-number">2</span><span class="hljs-string">.</span> <span class="hljs-string">The</span> <span class="hljs-string">President</span> <span class="hljs-string">of</span> <span class="hljs-string">a</span> <span class="hljs-string">country</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">A</span> <span class="hljs-string">country</span> <span class="hljs-string">has</span> <span class="hljs-string">ONE</span> <span class="hljs-string">president</span> <span class="hljs-string">at</span> <span class="hljs-string">any</span> <span class="hljs-string">given</span> <span class="hljs-string">time</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">citizens</span> <span class="hljs-string">interact</span> <span class="hljs-string">with</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">president</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">You</span> <span class="hljs-string">can't</span> <span class="hljs-string">have</span> <span class="hljs-string">multiple</span> <span class="hljs-string">presidents</span> <span class="hljs-string">simultaneously</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">When</span> <span class="hljs-string">the</span> <span class="hljs-string">term</span> <span class="hljs-string">ends,</span> <span class="hljs-string">a</span> <span class="hljs-string">new</span> <span class="hljs-string">president</span> <span class="hljs-string">is</span> <span class="hljs-string">elected</span> <span class="hljs-string">(but</span> <span class="hljs-string">still</span> <span class="hljs-string">ONE)</span>

<span class="hljs-number">3</span><span class="hljs-string">.</span> <span class="hljs-string">A</span> <span class="hljs-string">Company's</span> <span class="hljs-string">Configuration</span> <span class="hljs-string">System</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">A</span> <span class="hljs-string">company</span> <span class="hljs-string">has</span> <span class="hljs-string">ONE</span> <span class="hljs-string">HR</span> <span class="hljs-string">policy</span> <span class="hljs-string">manual</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">employees</span> <span class="hljs-string">follow</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">policies</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">If</span> <span class="hljs-string">each</span> <span class="hljs-string">department</span> <span class="hljs-string">had</span> <span class="hljs-string">different</span> <span class="hljs-string">policies</span> <span class="hljs-string">=</span> <span class="hljs-string">chaos!</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Updates</span> <span class="hljs-string">to</span> <span class="hljs-string">the</span> <span class="hljs-string">manual</span> <span class="hljs-string">apply</span> <span class="hljs-string">to</span> <span class="hljs-string">everyone</span> <span class="hljs-string">immediately</span>

<span class="hljs-attr">In Software:</span>
<span class="hljs-string">============</span>

<span class="hljs-number">1</span><span class="hljs-string">.</span> <span class="hljs-string">Application</span> <span class="hljs-string">Configuration</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">ONE</span> <span class="hljs-string">config</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">entire</span> <span class="hljs-string">app</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">modules</span> <span class="hljs-string">read</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">values</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Ensures</span> <span class="hljs-string">consistency</span>

<span class="hljs-number">2</span><span class="hljs-string">.</span> <span class="hljs-string">Logger</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">ONE</span> <span class="hljs-string">central</span> <span class="hljs-string">logging</span> <span class="hljs-string">system</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">components</span> <span class="hljs-string">write</span> <span class="hljs-string">to</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">log</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Makes</span> <span class="hljs-string">debugging</span> <span class="hljs-string">easier</span>

<span class="hljs-number">3</span><span class="hljs-string">.</span> <span class="hljs-string">Database</span> <span class="hljs-string">Connection</span> <span class="hljs-string">Pool</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">ONE</span> <span class="hljs-string">pool</span> <span class="hljs-string">manager</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">database</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">requests</span> <span class="hljs-string">share</span> <span class="hljs-string">connections</span> <span class="hljs-string">from</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">pool</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Prevents</span> <span class="hljs-string">connection</span> <span class="hljs-string">exhaustion</span>

<span class="hljs-number">4</span><span class="hljs-string">.</span> <span class="hljs-string">Cache</span> <span class="hljs-string">Manager</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">ONE</span> <span class="hljs-string">cache</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">application</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">All</span> <span class="hljs-string">components</span> <span class="hljs-string">share</span> <span class="hljs-string">the</span> <span class="hljs-string">SAME</span> <span class="hljs-string">cached</span> <span class="hljs-string">data</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">Maximizes</span> <span class="hljs-string">cache</span> <span class="hljs-string">hit</span> <span class="hljs-string">rate</span>
</code></pre>
<p><strong>Jordan</strong>: "Okay, that makes sense. But how do we enforce 'only one instance' in code? What stops someone from creating a second instance?"</p>
<p><strong>Alex</strong>: "Great question! Let's look at how other languages do it first, then we'll see why Rust is better."</p>
<h3 id="heading-the-traditional-singleton">The Traditional Singleton</h3>
<p><strong>Alex writes Java code on the whiteboard:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Traditional Singleton in Java.</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConfigManager</span> </span>{
    <span class="hljs-comment">// Private static instance.</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ConfigManager instance;

    <span class="hljs-comment">// Private constructor - can't create instance from outside!</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ConfigManager</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Load configuration.</span>
    }

    <span class="hljs-comment">// Public static method to get the instance.</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ConfigManager <span class="hljs-title">getInstance</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">if</span> (instance == <span class="hljs-keyword">null</span>) {
            instance = <span class="hljs-keyword">new</span> ConfigManager();  <span class="hljs-comment">// Create on first access.</span>
        }
        <span class="hljs-keyword">return</span> instance;
    }
}

<span class="hljs-comment">// Usage:</span>
ConfigManager config = ConfigManager.getInstance();  <span class="hljs-comment">// Gets the ONLY instance.</span>
</code></pre>
<p><strong>Alex</strong>: <mark>"See the pattern? The constructor is private, so nobody can call </mark> <code>new ConfigManager()</code><mark>. The only way to get an instance is through </mark> <code>getInstance()</code><mark>, which creates it once and reuses it."</mark></p>
<p><strong>Jordan</strong>: "That's clever! But wait... what if two threads call <code>getInstance()</code> at the same time?"</p>
<p><strong>Alex</strong> <em>(grinning)</em>: "EXACTLY! You've found the problem! If two threads enter <code>getInstance()</code> simultaneously, before the instance is created, you can end up with TWO instances!"</p>
<p><strong>Jordan</strong>: "So it's not actually a singleton anymore?"</p>
<p><strong>Alex</strong>: "Right. <strong>In Java, you need to add synchronisation</strong>:"</p>
<pre><code class="lang-java"><span class="hljs-comment">// Thread-safe singleton in Java. (more complex)</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConfigManager</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">volatile</span> ConfigManager instance;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ConfigManager</span><span class="hljs-params">()</span> </span>{}

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ConfigManager <span class="hljs-title">getInstance</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">if</span> (instance == <span class="hljs-keyword">null</span>) {  <span class="hljs-comment">// First check (no locking)</span>
            <span class="hljs-keyword">synchronized</span> (ConfigManager.class) {  // Lock <span class="hljs-keyword">for</span> thread-safety.
                <span class="hljs-keyword">if</span> (instance == <span class="hljs-keyword">null</span>) {  // Second check. (inside lock)
                    instance = <span class="hljs-keyword">new</span> ConfigManager();
                }
            }
        }
        <span class="hljs-keyword">return</span> instance;
    }
}
</code></pre>
<p><strong>Alex</strong>: "This is called 'double-checked locking'. It's tricky to get right. People mess it up all the time. <strong>And it has performance overhead because of the synchronisation.</strong>"</p>
<p><strong>Jordan</strong>: "Yikes. That looks complicated."</p>
<p><strong>Alex</strong>: "Now let me show you the Rust way."</p>
<h3 id="heading-the-rust-advantage">The Rust Advantage</h3>
<p><strong>Alex writes Rust code:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// Singleton in Rust - Simple and Thread-Safe!</span>
<span class="hljs-keyword">use</span> once_cell::sync::Lazy;

<span class="hljs-keyword">static</span> CONFIG: Lazy&lt;ConfigManager&gt; = Lazy::new(|| {
    ConfigManager::new()
});

<span class="hljs-comment">// Usage:</span>
<span class="hljs-keyword">let</span> config = &amp;*CONFIG;  <span class="hljs-comment">// Gets the ONLY instance</span>
</code></pre>
<p><strong>Jordan</strong>: "That's... it? Just three lines?"</p>
<p><strong>Alex</strong>: "Yep! And it's automatically thread-safe. <mark>The </mark> <code>Lazy</code> <mark>type from </mark> <code>once_cell</code> <mark>guarantees that the initialisation happens exactly once, even if a thousand threads try to access it simultaneously.</mark>"</p>
<p><strong>Jordan</strong>: "How does it work?"</p>
<p><strong>Alex</strong>: "Under the hood, <code>Lazy</code> uses atomic operations and locks efficiently. But the key is: we don't have to think about it! The library handles all the thread-safety for us, and the Rust compiler ensures we use it correctly."</p>
<p><strong>Jordan</strong>: "Okay, but what if we need to modify the config? Like when we hot-reload it?"</p>
<p><strong>Alex</strong>: "Ah, now we need interior mutability. That's where <code>Arc</code> and <code>RwLock</code> come in."</p>
<p><strong>Alex draws a more complete example:</strong></p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> once_cell::sync::Lazy;
<span class="hljs-keyword">use</span> parking_lot::RwLock;  <span class="hljs-comment">// Better performance than std::sync::RwLock.</span>
<span class="hljs-keyword">use</span> std::sync::Arc;

<span class="hljs-comment">// The singleton instance.</span>
<span class="hljs-keyword">static</span> CONFIG: Lazy&lt;Arc&lt;RwLock&lt;ConfigManager&gt;&gt;&gt; = Lazy::new(|| {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Initializing config - this prints EXACTLY ONCE"</span>);
    Arc::new(RwLock::new(ConfigManager::default()))
});

<span class="hljs-comment">// Safe concurrent reads.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_config</span></span>() -&gt; ConfigManager {
    CONFIG.read().clone()  <span class="hljs-comment">// Multiple threads can read simultaneously.</span>
}

<span class="hljs-comment">// Safe exclusive writes.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">update_config</span></span>(new_config: ConfigManager) {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> config = CONFIG.write();  <span class="hljs-comment">// Only one thread can write.</span>
    *config = new_config;
}
</code></pre>
<p><strong>Jordan</strong>: "Okay, now I have more questions. What's <code>Arc</code>? What's <code>RwLock</code>? And why do we need both?"</p>
<p><strong>Alex</strong>: "Perfect! Let's break down each component. Understanding these is crucial for building safe concurrent systems in Rust."</p>
<h2 id="heading-the-singleton-pattern">The Singleton Pattern</h2>
<p>Let's dive deep into the Singleton pattern - what it is, why it exists, and how it works.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766255421199/595f4b66-fc12-4921-b325-96196184c034.png" alt class="image--center mx-auto" /></p>
<p><strong>Breaking it down:</strong></p>
<ol>
<li><p><strong>"Ensure a class has only one instance"</strong></p>
<ul>
<li><p>Not zero instances (that would be useless)</p>
</li>
<li><p>Not two or more instances (that defeats the purpose)</p>
</li>
<li><p>Exactly ONE instance for the entire application lifetime</p>
</li>
</ul>
</li>
<li><p><strong>"Provide a global point of access to it"</strong></p>
<ul>
<li><p>Anyone who needs the instance can get it</p>
</li>
<li><p>No need to pass it around as parameters</p>
</li>
<li><p>Consistent access mechanism</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-the-structure">The Structure</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766255504596/834c06db-9ce2-4e0f-8561-ce419132869d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-when-to-use-singletons">When to Use Singletons</h3>
<p><strong>Use Singletons When:</strong></p>
<ol>
<li><p><strong>Exactly one instance must exist</strong></p>
<pre><code class="lang-rust"> <span class="hljs-comment">// Application configuration - one source of truth.</span>
 <span class="hljs-keyword">static</span> CONFIG: Lazy&lt;Arc&lt;RwLock&lt;Config&gt;&gt;&gt; = ...;

 <span class="hljs-comment">// Logger - centralized logging.</span>
 <span class="hljs-keyword">static</span> LOGGER: Lazy&lt;Logger&gt; = ...;

 <span class="hljs-comment">// Database connection pool - manage limited resources.</span>
 <span class="hljs-keyword">static</span> DB_POOL: Lazy&lt;Arc&lt;ConnectionPool&gt;&gt; = ...;
</code></pre>
</li>
<li><p><strong>Global access is genuinely needed</strong></p>
<pre><code class="lang-rust"> <span class="hljs-comment">// Accessed from many different modules/layers.</span>
 <span class="hljs-keyword">mod</span> handlers {
     <span class="hljs-keyword">use</span> crate::CONFIG;
     <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle</span></span>() { <span class="hljs-keyword">let</span> config = CONFIG.read(); }
 }

 <span class="hljs-keyword">mod</span> database {
     <span class="hljs-keyword">use</span> crate::CONFIG;
     <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">connect</span></span>() { <span class="hljs-keyword">let</span> config = CONFIG.read(); }
 }

 <span class="hljs-keyword">mod</span> middleware {
     <span class="hljs-keyword">use</span> crate::CONFIG;
     <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">auth</span></span>() { <span class="hljs-keyword">let</span> config = CONFIG.read(); }
 }
</code></pre>
</li>
<li><p><strong>Initialisation is expensive</strong></p>
<pre><code class="lang-rust"> <span class="hljs-comment">// Loading large reference data</span>
 <span class="hljs-keyword">static</span> COUNTRY_DATA: Lazy&lt;HashMap&lt;<span class="hljs-built_in">String</span>, Country&gt;&gt; = Lazy::new(|| {
     <span class="hljs-comment">// Load 200MB of country/city data once</span>
     load_countries_from_disk()  <span class="hljs-comment">// Expensive!</span>
 });

 <span class="hljs-comment">// Singleton ensures this expensive operation happens only once.</span>
</code></pre>
</li>
</ol>
<p><strong>Don't Use Singletons When:</strong></p>
<ol>
<li><p><strong>You need multiple instances</strong></p>
<pre><code class="lang-rust"> <span class="hljs-comment">// ❌ DON'T: User sessions should NOT be singletons!</span>
 <span class="hljs-comment">// Each user needs their own session.</span>
 <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">UserSession</span></span> { <span class="hljs-comment">/* ... */</span> }

 <span class="hljs-comment">// ✅ DO: Create instances per user.</span>
 <span class="hljs-keyword">let</span> session1 = UserSession::new(user1);
 <span class="hljs-keyword">let</span> session2 = UserSession::new(user2);
</code></pre>
</li>
<li><p><strong>Testing is difficult</strong></p>
<pre><code class="lang-rust"> <span class="hljs-comment">// ❌ Singletons make unit testing harder.</span>
 <span class="hljs-meta">#[test]</span>
 <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_something</span></span>() {
     <span class="hljs-comment">// Hard to mock or reset singleton state between tests.</span>
     <span class="hljs-keyword">let</span> result = some_function_using_singleton();
 }

 <span class="hljs-comment">// ✅ Better: Dependency injection.</span>
 <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">some_function</span></span>(config: &amp;Config) -&gt; <span class="hljs-built_in">Result</span> { <span class="hljs-comment">/* ... */</span> }

 <span class="hljs-meta">#[test]</span>
 <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_something</span></span>() {
     <span class="hljs-keyword">let</span> mock_config = Config::test_default();
     <span class="hljs-keyword">let</span> result = some_function(&amp;mock_config);  <span class="hljs-comment">// Easy to test!</span>
 }
</code></pre>
</li>
</ol>
<h3 id="heading-the-lifecycle">The Lifecycle</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766255931383/760f9d71-0174-467f-a173-f8424841b28a.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-rust-concepts-for-thread-safe-singletons">Rust Concepts for Thread-Safe Singletons</h2>
<p>Before we build the solution, we need to understand the Rust primitives that make thread-safe singletons possible. This is where Rust truly shines compared to other languages.</p>
<h3 id="heading-1-static-variables-in-rust">1. Static Variables in Rust</h3>
<p>Static variables live for the entire program duration and have a fixed memory address.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Static variable - lives forever.</span>
<span class="hljs-keyword">static</span> COUNTER: <span class="hljs-built_in">i32</span> = <span class="hljs-number">0</span>;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, COUNTER);  <span class="hljs-comment">// ✅ Can read.</span>
    <span class="hljs-comment">// COUNTER += 1;          // ❌ Can't mutate!</span>
}
</code></pre>
<p><strong>The problem:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// You might think: just make it mutable!</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">mut</span> COUNTER: <span class="hljs-built_in">i32</span> = <span class="hljs-number">0</span>;  <span class="hljs-comment">// ⚠️ This requires unsafe!</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">unsafe</span> {
        COUNTER += <span class="hljs-number">1</span>;  <span class="hljs-comment">// ⚠️ Unsafe! No compiler protection!</span>
    }
}
</code></pre>
<p><strong>Why is</strong> <code>static mut</code> unsafe?</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Data race! Multiple threads modifying COUNTER simultaneously.</span>
<span class="hljs-keyword">use</span> std::thread;

<span class="hljs-keyword">static</span> <span class="hljs-keyword">mut</span> COUNTER: <span class="hljs-built_in">i32</span> = <span class="hljs-number">0</span>;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> handles: <span class="hljs-built_in">Vec</span>&lt;_&gt; = (<span class="hljs-number">0</span>..<span class="hljs-number">10</span>)
        .map(|_| {
            thread::spawn(|| {
                <span class="hljs-keyword">unsafe</span> {
                    <span class="hljs-comment">// All threads increment simultaneously</span>
                    <span class="hljs-comment">// No synchronization!</span>
                    <span class="hljs-comment">// Final value is unpredictable!</span>
                    COUNTER += <span class="hljs-number">1</span>;  <span class="hljs-comment">// ⚠️ DATA RACE!</span>
                }
            })
        })
        .collect();

    <span class="hljs-keyword">for</span> handle <span class="hljs-keyword">in</span> handles {
        handle.join().unwrap();
    }

    <span class="hljs-keyword">unsafe</span> {
        <span class="hljs-comment">// Expected: 10</span>
        <span class="hljs-comment">// Actual: ??? (could be anything from 1 to 10!)</span>
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Counter: {}"</span>, COUNTER);
    }
}
</code></pre>
<p><strong>The solution:</strong> <mark>Interior mutability with thread-safe synchronisation</mark></p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::sync::Mutex;

<span class="hljs-comment">// Safe! Mutex provides interior mutability + thread safety.</span>
<span class="hljs-keyword">static</span> COUNTER: Mutex&lt;<span class="hljs-built_in">i32</span>&gt; = Mutex::new(<span class="hljs-number">0</span>);

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> counter = COUNTER.lock().unwrap();
    *counter += <span class="hljs-number">1</span>;  <span class="hljs-comment">// Safe! Compiler-enforced locking.</span>
}
</code></pre>
<h3 id="heading-2-oncecelllazy-lazy-initialisation">2. <code>once_cell::Lazy</code> - Lazy Initialisation</h3>
<p><strong>The problem:</strong> How do we initialise complex types at runtime?</p>
<pre><code class="lang-rust"><span class="hljs-comment">// ❌ This doesn't work!</span>
<span class="hljs-keyword">static</span> CONFIG: AppConfig = AppConfig::new();
<span class="hljs-comment">// Error: cannot call non-const fn in static.</span>

<span class="hljs-comment">// Why? Static variables must be initialized at compile-time,</span>
<span class="hljs-comment">// but AppConfig::new() runs at runtime!</span>
</code></pre>
<p><strong>The solution:</strong> <code>once_cell::Lazy</code></p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> once_cell::sync::Lazy;

<span class="hljs-keyword">static</span> CONFIG: Lazy&lt;AppConfig&gt; = Lazy::new(|| {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Initializing config..."</span>);
    AppConfig::load_from_env()  <span class="hljs-comment">// Runs at first access!</span>
});

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// First access - triggers initialization.</span>
    <span class="hljs-keyword">let</span> config1 = &amp;*CONFIG;
    <span class="hljs-comment">// Prints: "Initializing config..."</span>

    <span class="hljs-comment">// Second access - reuses existing instance.</span>
    <span class="hljs-keyword">let</span> config2 = &amp;*CONFIG;
    <span class="hljs-comment">// No print! Uses existing instance.</span>

    <span class="hljs-comment">// config1 and config2 point to THE SAME data!</span>
}
</code></pre>
<p><strong>How</strong> <code>Lazy</code> works internally:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766256337577/1eebdb7a-3651-4e13-a17b-f2159b4881ce.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-arc-atomic-reference-counting">3. <code>Arc&lt;T&gt;</code> - Atomic Reference Counting</h3>
<p><strong>The problem:</strong> How do we share ownership across multiple threads?</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Regular references don't work across threads:</span>
<span class="hljs-keyword">let</span> data = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
<span class="hljs-keyword">let</span> data_ref = &amp;data;

thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, data_ref);  <span class="hljs-comment">// Error! Can't send reference across threads.</span>
});
</code></pre>
<p><strong>The solution:</strong> <code>Arc</code> <mark>(Atomic Reference Counted)</mark></p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::sync::Arc;
<span class="hljs-keyword">use</span> std::thread;

<span class="hljs-keyword">let</span> data = Arc::new(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);

<span class="hljs-comment">// Clone the Arc (cheap! just increments reference count)</span>
<span class="hljs-keyword">let</span> data_clone1 = Arc::clone(&amp;data);
<span class="hljs-keyword">let</span> data_clone2 = Arc::clone(&amp;data);

<span class="hljs-comment">// Now we can send to threads!</span>
<span class="hljs-keyword">let</span> handle1 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Thread 1: {:?}"</span>, data_clone1);  <span class="hljs-comment">// Works!</span>
});

<span class="hljs-keyword">let</span> handle2 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Thread 2: {:?}"</span>, data_clone2);  <span class="hljs-comment">// Works!</span>
});

handle1.join().unwrap();
handle2.join().unwrap();

<span class="hljs-comment">// Original Arc still valid.</span>
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Main: {:?}"</span>, data);  <span class="hljs-comment">// Works!</span>
</code></pre>
<p><strong>How Arc works - Memory Layout:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766256812658/65b62fd4-4deb-442a-a0e3-d2f466df0453.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-4-rwlock-reader-writer-lock">4. <code>RwLock&lt;T&gt;</code> - Reader-Writer Lock</h3>
<p><strong>The problem:</strong> How do we allow multiple readers OR one writer?</p>
<pre><code class="lang-rust"><span class="hljs-comment">// With Mutex, only ONE thread can access at a time:</span>
<span class="hljs-keyword">use</span> std::sync::{Arc, Mutex};

<span class="hljs-keyword">let</span> data = Arc::new(Mutex::new(<span class="hljs-number">0</span>));

<span class="hljs-comment">// Reader 1 locks.</span>
<span class="hljs-keyword">let</span> guard1 = data.lock().unwrap();
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Reader 1: {}"</span>, *guard1);

<span class="hljs-comment">// Reader 2 wants to read... but must wait!</span>
<span class="hljs-comment">// Even though reading doesn't conflict with other reads!</span>
</code></pre>
<p><strong>The solution:</strong> <code>RwLock</code> (Reader-Writer Lock)</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::sync::{Arc, RwLock};

<span class="hljs-keyword">let</span> data = Arc::new(RwLock::new(<span class="hljs-number">0</span>));

<span class="hljs-comment">// Multiple readers can hold read locks simultaneously!</span>
<span class="hljs-keyword">let</span> reader1 = data.read().unwrap();
<span class="hljs-keyword">let</span> reader2 = data.read().unwrap();  <span class="hljs-comment">// OK! Both reading.</span>
<span class="hljs-keyword">let</span> reader3 = data.read().unwrap();  <span class="hljs-comment">// OK! All reading.</span>
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} {} {}"</span>, *reader1, *reader2, *reader3);

<span class="hljs-comment">// But writer must wait for all readers...</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> writer = data.write().unwrap();
<span class="hljs-comment">// ⏳ Blocks until reader1, reader2, reader3 are dropped</span>

*writer = <span class="hljs-number">42</span>;  <span class="hljs-comment">// Now writer has exclusive access.</span>
</code></pre>
<p><code>RwLock</code> State Machine:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766257014425/574d267d-acf2-4b6e-ac79-73800e7fcf87.png" alt class="image--center mx-auto" /></p>
<p><strong>When to use RwLock vs Mutex:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Scenario</td><td>Use This</td><td>Why</td></tr>
</thead>
<tbody>
<tr>
<td>Reads &gt;&gt; Writes</td><td><code>RwLock</code></td><td>Multiple readers don't block each other</td></tr>
<tr>
<td>Reads ≈ Writes</td><td><code>Mutex</code></td><td>Simpler, less overhead</td></tr>
<tr>
<td>Only writes</td><td><code>Mutex</code></td><td>RwLock adds unnecessary complexity</td></tr>
<tr>
<td>Short critical sections</td><td><code>Mutex</code></td><td>Less overhead</td></tr>
<tr>
<td>Long read operations</td><td><code>RwLock</code></td><td>Maximize concurrency</td></tr>
</tbody>
</table>
</div><h3 id="heading-5-putting-it-all-together">5. Putting It All Together</h3>
<p>Now we combine all these primitives to create the perfect singleton:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> once_cell::sync::Lazy;
<span class="hljs-keyword">use</span> parking_lot::RwLock;
<span class="hljs-keyword">use</span> std::sync::Arc;

<span class="hljs-comment">/// The Complete Singleton Pattern in Rust.</span>
<span class="hljs-keyword">static</span> CONFIG: Lazy&lt;Arc&lt;RwLock&lt;AppConfig&gt;&gt;&gt; = Lazy::new(|| {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Initializing config singleton!"</span>);
    Arc::new(RwLock::new(AppConfig::default()))
});

<span class="hljs-comment">// Let's break down each layer:</span>

<span class="hljs-comment">// Layer 1: Lazy&lt;...&gt;</span>
<span class="hljs-comment">// - Delays initialization until first access</span>
<span class="hljs-comment">// - Guarantees initialization happens exactly once</span>
<span class="hljs-comment">// - Thread-safe initialization</span>

<span class="hljs-comment">// Layer 2: Arc&lt;...&gt;</span>
<span class="hljs-comment">// - Allows shared ownership across threads</span>
<span class="hljs-comment">// - Reference counted (atomically)</span>
<span class="hljs-comment">// - Data is freed when last Arc is dropped</span>

<span class="hljs-comment">// Layer 3: RwLock&lt;...&gt;</span>
<span class="hljs-comment">// - Allows multiple readers OR one writer</span>
<span class="hljs-comment">// - Interior mutability (modify through shared reference)</span>
<span class="hljs-comment">// - Thread-safe access to the data</span>

<span class="hljs-comment">// Layer 4: AppConfig</span>
<span class="hljs-comment">// - The actual data we want to store</span>
</code></pre>
<p><strong>How they work together:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766257208029/2e4c0657-b318-472c-8f62-dbcb39073544.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-getting-the-complete-code">Getting the Complete Code</h2>
<p>The complete working implementation of the singleton pattern we've discussed is available in the GitHub repository.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/config-manager-api?nocache">https://github.com/kartikmehta8/config-manager-api?nocache</a></div>
<p> </p>
<p>Clone it, run it, and experiment with it to solidify your understanding!</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/kartikmehta8/config-manager-api.git
<span class="hljs-built_in">cd</span> config-manager-api
cargo run
</code></pre>
<h2 id="heading-the-learning-session">The Learning Session</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766257644749/2ace28af-7b36-4810-9af1-e3b91067bbea.png" alt class="image--center mx-auto" /></p>
<p><strong>Jordan</strong> <em>(leaning back in the chair, mind buzzing with new knowledge)</em>: "Wow. Okay. So we've covered:</p>
<ul>
<li><p>Static variables and why they're immutable by default</p>
</li>
<li><p><code>Lazy</code> for deferred initialisation</p>
</li>
<li><p><code>Arc</code> for shared ownership across threads</p>
</li>
<li><p><code>RwLock</code> for concurrent reads with exclusive writes</p>
</li>
<li><p><mark>And how they all work together to create a perfect singleton</mark>"</p>
</li>
</ul>
<p><strong>Alex</strong> <em>(smiling)</em>: "Exactly! And the beautiful thing about Rust is that the compiler enforces all the safety guarantees. You literally cannot create a data race or deadlock without using <code>unsafe</code>. The type system guides you toward correct concurrent code."</p>
<p><strong>Jordan</strong>: "I have to admit, when I first saw <code>Lazy&lt;Arc&lt;RwLock&lt;T&gt;&gt;&gt;</code>, it looked intimidating. But now I understand why each layer is necessary and what it does."</p>
<p><strong>Alex</strong>: "That's the Rust learning curve. It looks complex at first, but once you understand the primitives, you realise they're all simple concepts that compose elegantly. Each type has one job and does it well."</p>
<p><strong>Jordan</strong> <em>(opening laptop)</em>: "Alright, I'm ready to start implementing. Let me create the config singleton first, then the logger, then refactor the database connections."</p>
<p><strong>Alex</strong> <em>(standing up)</em>: "Perfect! That's the spirit! I'll be at my desk if you need help. You've got this, Jordan."</p>
<h2 id="heading-the-conclusion">The Conclusion</h2>
<p>The singleton pattern might seem simple on the surface, "just one instance, right?" but as we've seen, implementing it correctly in a concurrent environment requires careful consideration of thread safety, lazy vs eager initialisation, read vs write patterns, and memory management.</p>
<p>Rust gives us the tools to handle all of these concerns with compile-time guarantees. No other mainstream language offers this level of safety without garbage collection or runtime overhead.</p>
<p>As Jordan discovered, the learning curve is worth it. The initial complexity of <code>Lazy&lt;Arc&lt;RwLock&lt;T&gt;&gt;&gt;</code> pays dividends in correctness, performance, and peace of mind.</p>
<p><em>Thanks for reading! If you found this helpful, star the repo and share it with fellow Rustaceans!</em></p>
<p><strong>Stay caffeinated, stay rusty! 🦀</strong></p>
]]></content:encoded></item><item><title><![CDATA[The Coffee Shop Crisis]]></title><description><![CDATA[Meet Alex. Three months ago, Alex fulfilled a lifelong dream: opening "Rusty Bean", a cozy coffee shop in downtown Portland. Business is booming! Students camp out with their laptops, seniors meet for their morning ritual, and the loyalty program is ...]]></description><link>https://writer.mrmehta.in/the-coffee-shop-crisis</link><guid isPermaLink="true">https://writer.mrmehta.in/the-coffee-shop-crisis</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[strategy design pattern]]></category><category><![CDATA[Open Closed Principle]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 19 Dec 2025 18:51:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766083803020/0ceec2cc-5a3e-4217-bcae-5115d24ef106.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766124266360/a8fdbd8f-dc6b-4576-92e3-c5d9cb51cc44.png" alt class="image--center mx-auto" /></p>
<p>Meet <strong>Alex</strong>. Three months ago, Alex fulfilled a lifelong dream: opening <strong>"Rusty Bean",</strong> a cozy coffee shop in downtown Portland. Business is booming! Students camp out with their laptops, seniors meet for their morning ritual, and the loyalty program is attracting regulars.</p>
<p>But there's a problem. A big one.</p>
<p>It's 2 AM, and Alex is staring at the laptop screen in the empty shop. The code that handles discount calculations has become a monster. <em>What started as a simple "if student, then 20% off" has grown into an unmaintainable nightmare.</em></p>
<h2 id="heading-the-mess">The Mess</h2>
<p>Let's take a look at what Alex is dealing with. The discount calculation system started simple enough, a few if statements to handle student discounts. But as the coffee shop grew and more discount types were added (seniors, loyalty members, different coffee types), the function spiraled out of control. What you're about to see is the reality of technical debt: <strong>a function that's been copy-pasted, modified, and patched so many times that it's become unmaintainable</strong>.</p>
<h3 id="heading-the-current-code">The Current Code</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// Alex's current discount calculation function.</span>
<span class="hljs-comment">// WARNING: This is the BEFORE code - the mess we need to fix!</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_price</span></span>(
    coffee_type: &amp;<span class="hljs-built_in">str</span>,
    customer_type: &amp;<span class="hljs-built_in">str</span>,
    base_price: <span class="hljs-built_in">f64</span>
) -&gt; <span class="hljs-built_in">f64</span> {
    <span class="hljs-keyword">if</span> customer_type == <span class="hljs-string">"student"</span> {
        <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"espresso"</span> {
            base_price * <span class="hljs-number">0.8</span>  <span class="hljs-comment">// 20% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"latte"</span> {
            base_price * <span class="hljs-number">0.8</span>  <span class="hljs-comment">// 20% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"cappuccino"</span> {
            base_price * <span class="hljs-number">0.8</span>  <span class="hljs-comment">// 20% off.</span>
        } <span class="hljs-keyword">else</span> {
            base_price * <span class="hljs-number">0.8</span>  <span class="hljs-comment">// default to 20% off.</span>
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> customer_type == <span class="hljs-string">"senior"</span> {
        <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"espresso"</span> {
            base_price * <span class="hljs-number">0.85</span>  <span class="hljs-comment">// 15% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"latte"</span> {
            base_price * <span class="hljs-number">0.85</span>  <span class="hljs-comment">// 15% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"cappuccino"</span> {
            base_price * <span class="hljs-number">0.85</span>  <span class="hljs-comment">// 15% off.</span>
        } <span class="hljs-keyword">else</span> {
            base_price * <span class="hljs-number">0.85</span>  <span class="hljs-comment">// default to 15% off.</span>
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> customer_type == <span class="hljs-string">"loyalty_member"</span> {
        <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"espresso"</span> {
            base_price * <span class="hljs-number">0.9</span>  <span class="hljs-comment">// 10% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"latte"</span> {
            base_price * <span class="hljs-number">0.9</span>  <span class="hljs-comment">// 10% off.</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> coffee_type == <span class="hljs-string">"cappuccino"</span> {
            base_price * <span class="hljs-number">0.9</span>  <span class="hljs-comment">// 10% off.</span>
        } <span class="hljs-keyword">else</span> {
            base_price * <span class="hljs-number">0.9</span>  <span class="hljs-comment">// default to 10% off.</span>
        }
    } <span class="hljs-keyword">else</span> {
        base_price  <span class="hljs-comment">// no discount.</span>
    }
}

<span class="hljs-comment">// And this is just for simple percentage discounts!</span>
<span class="hljs-comment">// There's another function for "buy 2 get 1 free" deals...</span>
<span class="hljs-comment">// And another for "happy hour" discounts...</span>
<span class="hljs-comment">// It goes on and on...</span>
</code></pre>
<blockquote>
<p>"This is insane. I copy-pasted this code 47 times. FORTY-SEVEN! Last week, I changed the student discount from 20% to 25% and had to modify it in 12 different places. I missed three of them and gave some students 20% off while others got 25% off. My accountant is NOT happy."</p>
</blockquote>
<h3 id="heading-the-pain-points">The Pain Points</h3>
<p>Let's break down why this code is a disaster:</p>
<ol>
<li><p><strong>Massive Duplication</strong>: The same discount logic repeated for every coffee type</p>
</li>
<li><p><strong>Hard to Modify</strong>: Changing a discount percentage means hunting through the entire codebase</p>
</li>
<li><p><strong>Error-Prone</strong>: Easy to miss one place when making changes</p>
</li>
<li><p><strong>Hard to Test</strong>: How do you test this? Mock every possible combination?</p>
</li>
<li><p><strong>Not Scalable</strong>: Adding a new discount type means copying everything again</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766125021036/f06b5524-66f6-4659-a9a7-efc8798a2e37.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-understanding-the-chaos">Understanding the Chaos</h3>
<p>Let's visualise what's happening in Alex's code:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766125353800/85657cd9-a5fc-4c20-a6ef-4e8f9f4e38df.png" alt class="image--center mx-auto" /></p>
<p><strong>Notice the problem?</strong> Every customer type branches into checking the coffee type, even though the coffee type doesn't affect the discount! This is unnecessary complexity.</p>
<p>The core problems are:</p>
<ol>
<li><p><strong>Mixing "What" with "How"</strong>: The code mixes WHAT discount to apply with HOW to calculate it</p>
</li>
<li><p><strong>Lack of Encapsulation</strong>: Discount logic is scattered, not contained</p>
</li>
<li><p><strong>Tight Coupling</strong>: The main function knows too much about every discount type</p>
</li>
<li><p><strong>No Abstraction</strong>: Each discount is just a number in an if-else chain</p>
</li>
</ol>
<p>Monday morning, 8:37 AM. Alex's phone rings. It's the Marketing Manager, Jamie.</p>
<p><strong>Jamie</strong>: "Alex! Great news! We're launching a 'Happy Hour' promotion - <em>25% off all drinks from 3 PM to 5 PM.</em> Can you have it ready by Friday?"</p>
<p><strong>Alex</strong> <em>(already feeling the dread)</em>: "Um... sure. Let me check the code..."</p>
<p>Alex opens the laptop. To add Happy Hour discount, <strong>Alex needs to</strong>:</p>
<ol>
<li><p>Add another if-else condition for time checking</p>
</li>
<li><p>Copy-paste it for every coffee type</p>
</li>
<li><p>Make sure it doesn't conflict with student/senior/loyalty discounts</p>
</li>
<li><p>Modify the order processing logic</p>
</li>
<li><p>Update the receipt printing code</p>
</li>
<li><p>Modify the daily sales report generator</p>
</li>
<li><p>Touch at least 8 different files</p>
</li>
</ol>
<p><strong>Alex</strong> <em>(voice cracking)</em>: "Jamie... I'll need until next Wednesday. Maybe Thursday."</p>
<p><strong>Jamie</strong>: "But it's just one discount! The competitor down the street implemented it in a day!"</p>
<p>After the call, Alex stares at the screen. There's got to be a better way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766167313109/44fbf55b-d5a8-4dda-9a77-1f94aabe62b2.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-mentor">The Mentor</h2>
<p>Tuesday afternoon. Sarah, a regular customer who works as a <strong>senior Rust developer</strong> at a tech company, notices Alex's frustrated expression.</p>
<p><strong>Sarah</strong>: "Rough day?"</p>
<p><strong>Alex</strong>: "You could say that. I'm drowning in discount calculations. Every time marketing wants a new promotion, I need to modify code in a dozen places."</p>
<p><strong>Sarah</strong>: "Mind if I take a look?"</p>
<p>Alex shows her the code. Sarah's eyes widen.</p>
<p><strong>Sarah</strong>: "Ah, I see the problem. You're experiencing the 'God Function' anti-pattern. This function is trying to do everything."</p>
<blockquote>
<p>The <strong>God Function/Object Anti-Pattern</strong> <strong>describes a single class or function that takes on far too many responsibilities, knowing and doing too much, violating the Single Responsibility Principle (SRP) by handling unrelated tasks like data access, UI logic, and business rules, leading to code that's hard to maintain, test, understand, and extend due to high coupling and ripple-effect bugs</strong>.</p>
</blockquote>
<p><strong>Alex</strong>: "How would you solve it?"</p>
<p><strong>Sarah</strong>: "Ever heard of the <strong>Strategy Pattern</strong>?"</p>
<p><strong>Alex</strong>: "Design patterns? I've heard of them but never really used them..."</p>
<p>Sarah pulls out a napkin and starts drawing.</p>
<p><strong>Sarah</strong>: "Think of it this way. You have different discount strategies: student discount, senior discount, loyalty discount. Right now, you're choosing which strategy to use and HOW to calculate it in the same place. That's the problem."</p>
<p>She draws three boxes:</p>
<pre><code class="lang-yaml"><span class="hljs-string">┌─────────────────┐</span>      <span class="hljs-string">┌──────────────────┐</span>      <span class="hljs-string">┌─────────────────┐</span>
<span class="hljs-string">│</span>  <span class="hljs-string">Student</span>        <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Senior</span>          <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Loyalty</span>        <span class="hljs-string">│</span>
<span class="hljs-string">│</span>  <span class="hljs-string">Discount</span>       <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Discount</span>        <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Discount</span>       <span class="hljs-string">│</span>
<span class="hljs-string">│</span>  <span class="hljs-string">Strategy</span>       <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Strategy</span>        <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-string">Strategy</span>       <span class="hljs-string">│</span>
<span class="hljs-string">│</span>                 <span class="hljs-string">│</span>      <span class="hljs-string">│</span>                  <span class="hljs-string">│</span>      <span class="hljs-string">│</span>                 <span class="hljs-string">│</span>
<span class="hljs-string">│</span>  <span class="hljs-number">20</span><span class="hljs-string">%</span> <span class="hljs-string">off</span>        <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-number">15</span><span class="hljs-string">%</span> <span class="hljs-string">off</span>         <span class="hljs-string">│</span>      <span class="hljs-string">│</span>  <span class="hljs-number">10</span><span class="hljs-string">%</span> <span class="hljs-string">off</span>        <span class="hljs-string">│</span>
<span class="hljs-string">└─────────────────┘</span>      <span class="hljs-string">└──────────────────┘</span>      <span class="hljs-string">└─────────────────┘</span>
</code></pre>
<p><strong>Sarah</strong>: "Each discount strategy is a separate, independent piece of code. Your order just says, 'Hey, apply whatever discount strategy you have.' It doesn't know or care HOW the discount is calculated."</p>
<p><strong>Alex</strong>: "So... separate the WHAT from the HOW?"</p>
<ul>
<li><p><strong>WHAT = Which discount to apply (student, senior, loyalty, etc.)</strong></p>
</li>
<li><p><strong>HOW = The actual calculation logic (20% off, 15% off, etc.)</strong></p>
</li>
</ul>
<p>Alex pauses, thinking it through. "<mark>So in my current code, I'm doing both at the same time, checking WHO the customer is AND calculating their discount, all tangled together.</mark> <strong>But with this pattern, I'd just say 'This customer needs the student discount strategy' without knowing or caring that it's 20% off.</strong> The strategy itself knows the HOW part, the math. My order just knows WHAT strategy to use." A light bulb moment. "It's like when a customer orders a 'latte', I don't need to know HOW to make every drink. I just hand them the latte they ordered. The barista knows HOW to make it!"</p>
<p><strong>Sarah</strong>: "Exactly! In Rust, we can use traits for this. Let me show you."</p>
<h2 id="heading-the-strategy-pattern">The Strategy Pattern</h2>
<p><mark>The Strategy Pattern is a behavioural design pattern that lets you define a family of algorithms, encapsulate each one, and make them interchangeable.</mark></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://refactoring.guru/design-patterns/strategy">https://refactoring.guru/design-patterns/strategy</a></div>
<p> </p>
<h3 id="heading-the-key-idea">The Key Idea</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766167702251/0cca4f88-27b8-4844-899d-c7e5e45e9479.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-three-components">The Three Components</h3>
<p>Every Strategy Pattern implementation has three essential pieces working together. Let's break them down in a way that makes sense for our coffee shop scenario:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766127952168/2c56d6e8-ec9d-4a08-8246-809cad3646b0.png" alt class="image--center mx-auto" /></p>
<p><strong>Looking at this diagram, here's what each part means:</strong></p>
<ol>
<li><p><strong>Strategy Interface</strong> (DiscountStrategy trait): This is the <strong>blueprint</strong> that defines what all discounts must be able to do. Think of it as a contract: "If you want to be a discount in this system, you MUST be able to calculate a price." It doesn't care HOW you calculate it, just that you CAN.</p>
</li>
<li><p><strong>Concrete Strategies</strong> (StudentDiscount, SeniorDiscount, etc.): These are the <strong>actual implementations</strong>, the workers who follow the blueprint. Each one says, "Yes, I can calculate a discount, and here's MY specific way of doing it." StudentDiscount does 20% off, SeniorDiscount does 15% off, and so on. They all follow the same interface, but each does it their own way.</p>
</li>
<li><p><strong>Context</strong> (CoffeeOrder): This is the <strong>user</strong> of strategies. It's your coffee order that needs a discount applied. The crucial part? It doesn't know OR care which specific discount it has. It just knows, "I have some discount strategy, and when I need to calculate the final price, I'll ask it to do its thing."</p>
</li>
</ol>
<p>Think of payment methods at a store:</p>
<ul>
<li><p><strong>Strategy Interface</strong>: "Process Payment"</p>
</li>
<li><p><strong>Concrete Strategies</strong>: Cash, Credit Card, Mobile Payment, Cryptocurrency</p>
</li>
<li><p><strong>Context</strong>: Your purchase</p>
</li>
</ul>
<p><mark>The cashier doesn't need to know HOW each payment method works internally. They just say, "Process this payment," and each method handles it differently.</mark></p>
<p><strong>Sarah</strong>: "In your case, the 'Strategy Interface' is 'Calculate Discount.' Each discount type (student, senior, loyalty) is a 'Concrete Strategy.' And your coffee order is the 'Context' that uses whichever strategy is appropriate."</p>
<p><strong>Alex</strong>: "So when I add Happy Hour discount, I just create a new strategy? I don't touch any existing code?"</p>
<p><strong>Sarah</strong>: "Exactly! <strong>That's the Open/Closed Principle: open for extension, closed for modification.</strong>"</p>
<blockquote>
<p>The <strong>Open/Closed Principle (OCP)</strong> states that <strong>software entities (classes, modules, functions, etc.) should be open for extension but closed for modification</strong>.</p>
<p>This means you should be able to add new functionality or behaviours to a system without changing its existing, stable source code. The principle significantly improves the modularity and maintainability of code by preventing modifications in one area from introducing bugs in another.</p>
</blockquote>
<h2 id="heading-the-refresher">The Refresher</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766168419499/742f4287-cf3b-466b-9e1c-9b8ff8cc379c.png" alt class="image--center mx-auto" /></p>
<p>Before we dive into the implementation, let's understand the Rust concepts we'll use.</p>
<h3 id="heading-1-traits-rusts-interfaces">1. Traits: Rust's Interfaces</h3>
<p>A trait defines behaviour that types can implement. It's like a contract - if a type implements a trait, it promises to provide certain functionality.</p>
<blockquote>
<p><strong>Learn More</strong>: For a comprehensive guide to traits, see the <a target="_blank" href="https://doc.rust-lang.org/book/ch10-02-traits.html">official Rust book chapter on traits</a>.</p>
</blockquote>
<pre><code class="lang-rust"><span class="hljs-comment">// A trait is like a promise:</span>
<span class="hljs-comment">// I can do "these things" FOR SURE.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}
</code></pre>
<p>Any type implementing this trait must provide an <code>apply_discount</code> method.</p>
<p><strong>Why traits?</strong></p>
<ul>
<li><p><strong>Define shared behaviour</strong>: Multiple types can share the same interface without sharing implementation</p>
</li>
<li><p><strong>Polymorphism</strong>: Different types can be used interchangeably through the same trait</p>
</li>
<li><p><strong>Zero-cost abstraction</strong>: Trait methods can be optimised away at compile time</p>
</li>
<li><p><strong>Composition over inheritance</strong>: Rust doesn't have traditional inheritance; traits provide a more flexible alternative</p>
</li>
</ul>
<p><strong>Traits vs Interfaces in Other Languages</strong>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// In Java/C#, you might write:</span>
<span class="hljs-comment">// interface Discount {</span>
<span class="hljs-comment">//     double applyDiscount(double basePrice);</span>
<span class="hljs-comment">// }</span>

<span class="hljs-comment">// In Rust, it's:</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Discount</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}
</code></pre>
<p>The key difference? Rust traits can be implemented for types you don't own, and they support <strong>default implementations</strong>:</p>
<pre><code class="lang-rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;

    <span class="hljs-comment">// Default implementation.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_and_round</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-keyword">self</span>.apply_discount(base_price).round()
    }
}
</code></pre>
<p><strong>Sarah</strong>: "Think of traits as capabilities. When you say 'this type implements DiscountStrategy,' you're saying 'this type has the capability to calculate discounts.' <strong>The compiler then ensures you actually provide that capability.</strong>"</p>
<h3 id="heading-2-box-dynamic-dispatch">2. Box: Dynamic Dispatch</h3>
<blockquote>
<p><strong>Learn More</strong>: Understanding <code>Box&lt;T&gt;</code> is crucial for heap allocation. Read more in the <a target="_blank" href="https://doc.rust-lang.org/book/ch15-01-box.html">Rust book's chapter on Box</a> and <a target="_blank" href="https://doc.rust-lang.org/book/ch17-02-trait-objects.html">trait objects</a>.</p>
</blockquote>
<pre><code class="lang-rust"><span class="hljs-comment">// This can hold ANY type that implements DiscountStrategy.</span>
<span class="hljs-keyword">let</span> strategy: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-built_in">Box</span>::new(StudentDiscount);
</code></pre>
<p><strong>Breaking it down:</strong></p>
<ul>
<li><p><code>Box&lt;T&gt;</code>: A smart pointer that allocates <code>T</code> on the heap</p>
<ul>
<li><p>Why heap? Because we don't know the size of the concrete type at compile time</p>
</li>
<li><p>Each strategy might have different sizes if they store data</p>
</li>
<li><p>The Box gives us a fixed-size pointer (8 bytes on 64-bit systems)</p>
</li>
</ul>
</li>
<li><p><code>dyn Trait</code>: Dynamic dispatch - the method to call is determined at runtime</p>
<ul>
<li><p>"dyn" stands for "dynamic"</p>
</li>
<li><p>Tells Rust: "I don't know the exact type, but it implements this trait"</p>
</li>
<li><p>Uses a <strong>vtable</strong> (virtual method table) to look up method implementations</p>
</li>
</ul>
</li>
<li><p><strong>Combined</strong>: <mark>A heap-allocated value of unknown type that implements the trait</mark></p>
</li>
</ul>
<p><strong>Visual representation:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766128889192/c4967899-81dc-4396-a50f-44575ce7da36.png" alt class="image--center mx-auto" /></p>
<p><strong>What's a vtable?</strong></p>
<p>A vtable (virtual method table) is a lookup table of function pointers:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766129138200/3d15044e-8373-476f-8e83-b85f3cb4130a.png" alt class="image--center mx-auto" /></p>
<p>When you call <code>strategy.apply_discount()</code>, Rust:</p>
<ol>
<li><p>Looks at the vtable pointer</p>
</li>
<li><p>Finds the entry for <code>apply_discount</code></p>
</li>
<li><p>Calls the function at that address</p>
</li>
</ol>
<p><strong>This small runtime cost (one pointer indirection) gives us flexibility!</strong></p>
<p><strong>Alex</strong>: "So every time we call a method, it has to look it up in this table?"</p>
<p><strong>Sarah</strong>: "Yes, but it's incredibly fast - just one memory lookup. The trade-off is worth it when you need runtime flexibility."</p>
<h3 id="heading-3-static-vs-dynamic-dispatch">3. Static vs Dynamic Dispatch</h3>
<p><strong>Static Dispatch (Compile-time)</strong>:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate</span></span>&lt;T: DiscountStrategy&gt;(strategy: T, price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
    strategy.apply_discount(price)
}
<span class="hljs-comment">// Compiler generates specific code for each type.</span>
<span class="hljs-comment">// Fast! But increases binary size.</span>
</code></pre>
<p><strong>Dynamic Dispatch (Runtime)</strong>:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate</span></span>(strategy: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt;, price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
    strategy.apply_discount(price)
}
<span class="hljs-comment">// Compiler uses vtable to look up method.</span>
<span class="hljs-comment">// Slight overhead, but more flexible.</span>
</code></pre>
<p><strong>For our use case</strong>, we need dynamic dispatch because we don't know which discount strategy to use until runtime (when the customer orders).</p>
<h3 id="heading-4-ownership-and-borrowing-in-strategies">4. Ownership and Borrowing in Strategies</h3>
<pre><code class="lang-rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span> {
    <span class="hljs-comment">// &amp;self means "borrow self immutably".</span>
    <span class="hljs-comment">// The strategy doesn't need to own or modify itself.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}
</code></pre>
<p>The <code>&amp;self</code> means our strategies are <strong>immutable</strong> and can be shared safely.</p>
<h3 id="heading-5-send-and-sync-traits">5. Send and Sync Traits</h3>
<blockquote>
<p><strong>Learn More</strong>: For a deep dive into thread safety, see the <a target="_blank" href="https://doc.rust-lang.org/nomicon/send-and-sync.html">Rustonomicon chapter on Send and Sync</a>.</p>
</blockquote>
<pre><code class="lang-rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span>: <span class="hljs-built_in">Send</span> + <span class="hljs-built_in">Sync</span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}
</code></pre>
<p>These are <strong>marker traits</strong> that tell the compiler about thread safety:</p>
<ul>
<li><p><code>Send</code>: A type is Send if it can be <strong>transferred</strong> between threads</p>
<ul>
<li><p>Ownership can move from one thread to another</p>
</li>
<li><p>Most types are Send (integers, strings, your custom structs)</p>
</li>
</ul>
</li>
<li><p><code>Sync</code>: A type is Sync if it can be <strong>shared</strong> between threads (via <code>&amp;T</code>)</p>
<ul>
<li><p>Multiple threads can have immutable references simultaneously</p>
</li>
<li><p>If <code>&amp;T</code> is Send, then <code>T</code> is Sync</p>
</li>
<li><p>Example: <code>Arc&lt;T&gt;</code> (atomic reference counted) is Sync</p>
</li>
</ul>
</li>
</ul>
<p>Imagine you have toy blocks:</p>
<ul>
<li><p><strong>Send</strong>: You can <strong>give your block</strong> to another kid (but then you don't have it anymore)</p>
</li>
<li><p><strong>Sync</strong>: Multiple kids can <strong>look at the same block</strong> at the same time (like through a display case)</p>
</li>
</ul>
<p>In our coffee shop:</p>
<ul>
<li><p><strong>Send</strong>: A discount can be passed from one part of the program to another</p>
</li>
<li><p><strong>Sync</strong>: Multiple customers can use the discount rules at the exact same time</p>
</li>
</ul>
<p><strong>The magic</strong>: Rust checks this automatically. If you mess up, it won't even let your code compile!</p>
<p><strong>Why do we need this for our coffee shop?</strong></p>
<p>Our web server (Axum) uses async/await and might handle requests on different threads. When a request comes in:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Thread 1:</span> <span class="hljs-string">Customer</span> <span class="hljs-string">orders</span> <span class="hljs-string">coffee</span> <span class="hljs-string">→</span> <span class="hljs-string">Creates</span> <span class="hljs-string">StudentDiscount</span> <span class="hljs-string">strategy</span>
<span class="hljs-attr">Thread 2:</span> <span class="hljs-string">Processes</span> <span class="hljs-string">the</span> <span class="hljs-string">order</span> <span class="hljs-string">→</span> <span class="hljs-string">Uses</span> <span class="hljs-string">that</span> <span class="hljs-string">same</span> <span class="hljs-string">strategy</span>
</code></pre>
<p>Without <code>Send + Sync</code>, the compiler would reject our code because it can't guarantee thread safety!</p>
<p><strong>Visual example of thread safety:</strong></p>
<pre><code class="lang-rust"><span class="hljs-comment">// This is SAFE because DiscountStrategy is Send + Sync.</span>
<span class="hljs-keyword">use</span> std::sync::Arc;
<span class="hljs-keyword">use</span> std::thread;

<span class="hljs-keyword">let</span> strategy = Arc::new(StudentDiscount);  <span class="hljs-comment">// Arc = Atomically Reference Counted.</span>

<span class="hljs-comment">// Thread 1 can use it.</span>
<span class="hljs-keyword">let</span> strategy_clone = strategy.clone();
<span class="hljs-keyword">let</span> handle1 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Thread 1: {}"</span>, strategy_clone.apply_discount(<span class="hljs-number">10.0</span>));
});

<span class="hljs-comment">// Thread 2 can also use it simultaneously.</span>
<span class="hljs-keyword">let</span> strategy_clone2 = strategy.clone();
<span class="hljs-keyword">let</span> handle2 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Thread 2: {}"</span>, strategy_clone2.apply_discount(<span class="hljs-number">15.0</span>));
});

handle1.join().unwrap();
handle2.join().unwrap();
</code></pre>
<p><strong>Wait, what is Arc?</strong></p>
<p>Let's break down <code>Arc::new(StudentDiscount)</code> because it's crucial for multithreaded applications:</p>
<p><strong>Arc stands for "Atomically Reference Counted"</strong> - that's a mouthful, but here's what it means in simple terms:</p>
<ul>
<li><p><strong>Reference Counted</strong>: It keeps track of HOW MANY parts of your code are using this data. When the count reaches zero, the data is cleaned up automatically.</p>
</li>
<li><p><strong>Atomically</strong>: The counting is done in a thread-safe way. Multiple threads can safely update the count at the same time without corrupting it.</p>
</li>
</ul>
<p><strong>Why do we need Arc instead of just Box?</strong></p>
<p>Remember <code>Box&lt;T&gt;</code> from earlier? It's great for single ownership. But here's the problem with threads:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// This WON'T work!</span>
<span class="hljs-keyword">let</span> strategy = <span class="hljs-built_in">Box</span>::new(StudentDiscount);
<span class="hljs-keyword">let</span> handle1 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-comment">// strategy moved here - Thread 1 owns it now.</span>
});
<span class="hljs-keyword">let</span> handle2 = thread::spawn(<span class="hljs-keyword">move</span> || {
    <span class="hljs-comment">// ERROR! Can't move strategy again - Thread 1 already took it!</span>
});
</code></pre>
<p>With <code>Box</code>, once Thread 1 takes ownership (via <code>move</code>), Thread 2 can't use it. <strong>But we need BOTH threads to access the same discount!</strong></p>
<p><strong>Arc solves this by allowing shared ownership. This is the beauty of Rust</strong>: <mark>If your code compiles, you know it's thread-safe. No data races, no mysterious bugs!</mark></p>
<p><strong>Alex</strong>: "So Rust won't even let me compile code that could have threading issues?"</p>
<p><strong>Sarah</strong>: "Exactly! The compiler is like a really pedantic code reviewer who never gets tired. It might feel strict at first, but it saves you from countless debugging nightmares."</p>
<h2 id="heading-the-solution">The Solution</h2>
<p>Alright! Let's build this thing. We'll start simple and gradually improve.</p>
<blockquote>
<p><strong><mark>Want to see the complete working code?</mark></strong> Check out the full implementation on GitHub: <a target="_blank" href="https://github.com/kartikmehta8/coffee-shop-api">coffee-shop-api repository</a></p>
<p>Feel free to clone it and follow along!</p>
</blockquote>
<h3 id="heading-setting-up-the-project">Setting Up the Project</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new Rust project.</span>
cargo new coffee-shop-api
<span class="hljs-built_in">cd</span> coffee-shop-api
</code></pre>
<p>Open <code>Cargo.toml</code> and add dependencies:</p>
<pre><code class="lang-toml"><span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"coffee-shop-api"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2024"</span>

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">axum</span> = <span class="hljs-string">"0.7"</span>
<span class="hljs-attr">tokio</span> = { version = <span class="hljs-string">"1"</span>, features = [<span class="hljs-string">"full"</span>] }
<span class="hljs-attr">serde</span> = { version = <span class="hljs-string">"1.0"</span>, features = [<span class="hljs-string">"derive"</span>] }
<span class="hljs-attr">serde_json</span> = <span class="hljs-string">"1.0"</span>
<span class="hljs-attr">tower</span> = <span class="hljs-string">"0.4"</span>
<span class="hljs-attr">tower-http</span> = { version = <span class="hljs-string">"0.5"</span>, features = [<span class="hljs-string">"cors"</span>] }
</code></pre>
<p><strong>Why these dependencies?</strong></p>
<ul>
<li><p><code>axum</code>: Web framework (built on top of tokio and hyper)</p>
</li>
<li><p><code>tokio</code>: Async runtime for handling concurrent requests</p>
</li>
<li><p><code>serde</code>: Serialization/deserialization for JSON</p>
</li>
<li><p><code>tower</code> &amp; <code>tower-http</code>: Middleware utilities</p>
</li>
</ul>
<h3 id="heading-define-the-strategy-trait-the-contract">Define the Strategy Trait (The Contract)</h3>
<p>Create <code>src/strategies.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// This is the heart of the Strategy Pattern.</span>
<span class="hljs-comment">// Every discount strategy MUST implement this trait.</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span>: <span class="hljs-built_in">Send</span> + <span class="hljs-built_in">Sync</span> {
    <span class="hljs-comment">/// Calculate the final price after applying the discount.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// # Arguments</span>
    <span class="hljs-comment">/// * `base_price` - The original price before discount.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// # Returns</span>
    <span class="hljs-comment">/// The final price after discount is applied.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;

    <span class="hljs-comment">/// Get the name of this discount strategy.</span>
    <span class="hljs-comment">/// Useful for logging and displaying to users.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;

    <span class="hljs-comment">/// Get a description of how this discount works.</span>
    <span class="hljs-comment">/// Helps customers understand what discount they're getting.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;
}
</code></pre>
<p><strong>What we just did:</strong></p>
<ul>
<li><p>Defined a contract: any discount strategy must implement these three methods</p>
</li>
<li><p>Added <code>Send + Sync</code> for thread-safety (required by Axum)</p>
</li>
</ul>
<h3 id="heading-implement-our-first-strategy-student-discount">Implement Our First Strategy - Student Discount</h3>
<p>Still in <code>src/strategies.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">/// Student Discount: 20% off all drinks.</span>
<span class="hljs-comment">/// Requires valid student ID verification.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StudentDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> StudentDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-comment">// Students get 20% off.</span>
        <span class="hljs-comment">// So they pay 80% of the original price.</span>
        base_price * <span class="hljs-number">0.80</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Student Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"20% off all drinks with valid student ID"</span>
    }
}
</code></pre>
<p><strong>Key points:</strong></p>
<ul>
<li><p><code>pub struct StudentDiscount;</code> - This is a zero-sized type (no data)</p>
</li>
<li><p>We implement the trait we defined</p>
</li>
<li><p>The calculation is simple and localized</p>
</li>
<li><p>This strategy doesn't need any configuration or state</p>
</li>
</ul>
<p><strong>Let's test it:</strong></p>
<pre><code class="lang-rust"><span class="hljs-meta">#[cfg(test)]</span>
<span class="hljs-keyword">mod</span> tests {
    <span class="hljs-keyword">use</span> super::*;

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_student_discount_calculation</span></span>() {
        <span class="hljs-keyword">let</span> strategy = StudentDiscount;
        <span class="hljs-keyword">let</span> base_price = <span class="hljs-number">10.0</span>;
        <span class="hljs-keyword">let</span> final_price = strategy.apply_discount(base_price);

        <span class="hljs-built_in">assert_eq!</span>(final_price, <span class="hljs-number">8.0</span>); <span class="hljs-comment">// 20% off means pay 80%.</span>
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_student_discount_name</span></span>() {
        <span class="hljs-keyword">let</span> strategy = StudentDiscount;
        <span class="hljs-built_in">assert_eq!</span>(strategy.name(), <span class="hljs-string">"Student Discount"</span>);
    }
}
</code></pre>
<p>Run the test:</p>
<pre><code class="lang-bash">cargo <span class="hljs-built_in">test</span>
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">running</span> <span class="hljs-number">2</span> <span class="hljs-string">tests</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_student_discount_calculation</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_student_discount_name</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
</code></pre>
<h3 id="heading-implement-more-strategies">Implement More Strategies</h3>
<p>Let's add the rest of our discount strategies:</p>
<pre><code class="lang-rust"><span class="hljs-comment">/// Senior Discount: 15% off all drinks.</span>
<span class="hljs-comment">/// For customers 65 years or older.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SeniorDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> SeniorDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.85</span>  <span class="hljs-comment">// 15% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Senior Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"15% off all drinks for seniors (65+)"</span>
    }
}

<span class="hljs-comment">/// Loyalty Card Discount: 10% off all drinks.</span>
<span class="hljs-comment">/// Plus earns points for future rewards (not implemented here, can be extended).</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LoyaltyDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> LoyaltyDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.90</span>  <span class="hljs-comment">// 10% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Loyalty Card Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"10% off all drinks + earn points towards free drinks"</span>
    }
}

<span class="hljs-comment">/// No Discount: Full price.</span>
<span class="hljs-comment">/// For regular customers without any discount eligibility.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">NoDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> NoDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price  <span class="hljs-comment">// No discount, pay full price</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Regular Price"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Standard pricing with no discount"</span>
    }
}
</code></pre>
<p><strong>Notice the pattern?</strong> Each strategy:</p>
<ol>
<li><p>Is a simple struct</p>
</li>
<li><p>Implements the <code>DiscountStrategy</code> trait</p>
</li>
<li><p>Has its own calculation logic</p>
</li>
<li><p>Is completely independent of other strategies</p>
</li>
</ol>
<p><strong>Sarah</strong>: "See how clean this is? Each discount strategy is self-contained. If the student discount changes, you only modify <code>StudentDiscount</code>. Nothing else."</p>
<h3 id="heading-creating-the-context-coffeeorder">Creating the Context (CoffeeOrder)</h3>
<p>Create <code>src/order.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::models::DiscountType;
<span class="hljs-keyword">use</span> crate::strategies::{
    DiscountStrategy, LoyaltyDiscount, NoDiscount, SeniorDiscount, StudentDiscount,
};

<span class="hljs-comment">/// Represents a coffee order with an applied discount strategy.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// This is the "Context" in Strategy Pattern terminology.</span>
<span class="hljs-comment">/// It uses a strategy without knowing which specific one it is.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CoffeeOrder</span></span> {
    <span class="hljs-keyword">pub</span> coffee_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> base_price: <span class="hljs-built_in">f64</span>,
    <span class="hljs-comment">// This is the magic! It can be ANY strategy.</span>
    discount_strategy: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt;,
}
</code></pre>
<p><strong>What's happening here?</strong></p>
<ul>
<li><p><code>CoffeeOrder</code> holds a reference to a discount strategy</p>
</li>
<li><p>It doesn't know or care which specific strategy it is</p>
</li>
<li><p><code>Box&lt;dyn DiscountStrategy&gt;</code> allows runtime strategy selection</p>
</li>
</ul>
<p>Now let's add methods:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> CoffeeOrder {
    <span class="hljs-comment">/// Create a new coffee order with the specified discount strategy.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// This is a Factory Method that creates the appropriate strategy</span>
    <span class="hljs-comment">/// based on the customer type.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(
        coffee_type: <span class="hljs-built_in">String</span>,
        base_price: <span class="hljs-built_in">f64</span>,
        discount_type: DiscountType
    ) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-comment">// FACTORY METHOD: Select the right strategy based on discount type.</span>
        <span class="hljs-keyword">let</span> discount_strategy: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-keyword">match</span> discount_type {
            DiscountType::Student =&gt; <span class="hljs-built_in">Box</span>::new(StudentDiscount),
            DiscountType::Senior =&gt; <span class="hljs-built_in">Box</span>::new(SeniorDiscount),
            DiscountType::Loyalty =&gt; <span class="hljs-built_in">Box</span>::new(LoyaltyDiscount),
            DiscountType::<span class="hljs-literal">None</span> =&gt; <span class="hljs-built_in">Box</span>::new(NoDiscount),
        };

        <span class="hljs-keyword">Self</span> {
            coffee_type,
            base_price,
            discount_strategy,
        }
    }

    <span class="hljs-comment">/// Calculate the final price by applying the discount strategy.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// THIS IS THE KEY METHOD!</span>
    <span class="hljs-comment">/// It delegates to the strategy without knowing which one it is.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_final_price</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-comment">// Ask the strategy to calculate the discount.</span>
        <span class="hljs-comment">// We don't know if it's student, senior, or loyalty.</span>
        <span class="hljs-comment">// We don't care! That's the power of the pattern.</span>
        <span class="hljs-keyword">self</span>.discount_strategy.apply_discount(<span class="hljs-keyword">self</span>.base_price)
    }

    <span class="hljs-comment">/// Get the discount amount (convenience method).</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_discount_amount</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-keyword">self</span>.base_price - <span class="hljs-keyword">self</span>.calculate_final_price()
    }

    <span class="hljs-comment">/// Get the name of the applied discount strategy.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_discount_name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-keyword">self</span>.discount_strategy.name()
    }

    <span class="hljs-comment">/// Get the description of the applied discount strategy.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_discount_description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-keyword">self</span>.discount_strategy.description()
    }
}
</code></pre>
<p><strong>The magic moment</strong>: Look at <code>calculate_final_price()</code>. It just calls <code>self.discunt_strategy.apply_discount()</code>. It doesn't have any if-else logic. It doesn't know which discount type it is. <strong>That's polymorphism in action!</strong></p>
<h3 id="heading-define-data-models">Define Data Models</h3>
<p>Create <code>src/models.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> serde::{Deserialize, Serialize};

<span class="hljs-comment">/// Request body for creating an order.</span>
<span class="hljs-meta">#[derive(Debug, Deserialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OrderRequest</span></span> {
    <span class="hljs-keyword">pub</span> coffee_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> customer_type: <span class="hljs-built_in">String</span>, <span class="hljs-comment">// "student", "senior", "loyalty", or "none".</span>
    <span class="hljs-keyword">pub</span> base_price: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-comment">/// Response body for order calculation.</span>
<span class="hljs-meta">#[derive(Debug, Serialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OrderResponse</span></span> {
    <span class="hljs-keyword">pub</span> coffee_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> base_price: <span class="hljs-built_in">f64</span>,
    <span class="hljs-keyword">pub</span> discount_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> discount_description: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> discount_amount: <span class="hljs-built_in">f64</span>,
    <span class="hljs-keyword">pub</span> final_price: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-comment">/// Enum representing different discount types.</span>
<span class="hljs-meta">#[derive(Debug, Clone, Copy)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">DiscountType</span></span> {
    Student,
    Senior,
    Loyalty,
    <span class="hljs-literal">None</span>,
}

<span class="hljs-keyword">impl</span> DiscountType {
    <span class="hljs-comment">/// Parse a string into a DiscountType.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_string</span></span>(s: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">match</span> s.to_lowercase().as_str() {
            <span class="hljs-string">"student"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Student),
            <span class="hljs-string">"senior"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Senior),
            <span class="hljs-string">"loyalty"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Loyalty),
            <span class="hljs-string">"none"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::<span class="hljs-literal">None</span>),
            _ =&gt; <span class="hljs-literal">None</span>,
        }
    }
}
</code></pre>
<h3 id="heading-create-api-handlers">Create API Handlers</h3>
<p>Create <code>src/handlers.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> axum::{http::StatusCode, Json};
<span class="hljs-keyword">use</span> crate::models::{DiscountInfo, DiscountsResponse, OrderRequest, OrderResponse};
<span class="hljs-keyword">use</span> crate::order::CoffeeOrder;
<span class="hljs-keyword">use</span> crate::models::DiscountType;

<span class="hljs-comment">/// Handler for POST /order.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// This endpoint:</span>
<span class="hljs-comment">/// 1. Receives order details from the client</span>
<span class="hljs-comment">/// 2. Validates the input</span>
<span class="hljs-comment">/// 3. Creates a CoffeeOrder with appropriate strategy</span>
<span class="hljs-comment">/// 4. Calculates the final price</span>
<span class="hljs-comment">/// 5. Returns the result</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_order</span></span>(
    Json(payload): Json&lt;OrderRequest&gt;,
) -&gt; <span class="hljs-built_in">Result</span>&lt;Json&lt;OrderResponse&gt;, StatusCode&gt; {
    <span class="hljs-comment">// Validation: Check if base price is valid.</span>
    <span class="hljs-keyword">if</span> payload.base_price &lt;= <span class="hljs-number">0.0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(StatusCode::BAD_REQUEST);
    }

    <span class="hljs-comment">// Parse the customer type string into a DiscountType enum.</span>
    <span class="hljs-keyword">let</span> discount_type = DiscountType::from_string(&amp;payload.customer_type)
        .ok_or(StatusCode::BAD_REQUEST)?;

    <span class="hljs-comment">// Create the order with the appropriate discount strategy.</span>
    <span class="hljs-comment">// This is where the Factory Method pattern comes in.</span>
    <span class="hljs-keyword">let</span> order = CoffeeOrder::new(
        payload.coffee_type.clone(),
        payload.base_price,
        discount_type,
    );

    <span class="hljs-comment">// Calculate prices using the strategy.</span>
    <span class="hljs-keyword">let</span> final_price = order.calculate_final_price();
    <span class="hljs-keyword">let</span> discount_amount = order.get_discount_amount();

    <span class="hljs-comment">// Build response.</span>
    <span class="hljs-keyword">let</span> response = OrderResponse {
        coffee_type: payload.coffee_type,
        base_price: payload.base_price,
        discount_type: order.get_discount_name().to_string(),
        discount_description: order.get_discount_description().to_string(),
        discount_amount,
        final_price,
    };

    <span class="hljs-literal">Ok</span>(Json(response))
}
</code></pre>
<p><strong><mark>Flow visualisation:</mark></strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766130565592/8f62a15c-dc4c-4e5a-bcdb-19d74fd08279.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-set-up-the-web-server">Set Up the Web Server</h3>
<p>Update <code>src/main.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> axum::{
    routing::{get, post},
    Router,
};
<span class="hljs-keyword">use</span> tower_http::cors::CorsLayer;

<span class="hljs-keyword">mod</span> handlers;
<span class="hljs-keyword">mod</span> models;
<span class="hljs-keyword">mod</span> order;
<span class="hljs-keyword">mod</span> strategies;

<span class="hljs-meta">#[tokio::main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Build our application with routes.</span>
    <span class="hljs-keyword">let</span> app = Router::new()
        .route(<span class="hljs-string">"/order"</span>, post(handlers::calculate_order))
        .route(<span class="hljs-string">"/discounts"</span>, get(handlers::list_discounts))
        .layer(CorsLayer::permissive());

    <span class="hljs-comment">// Run the server.</span>
    <span class="hljs-keyword">let</span> listener = tokio::net::TcpListener::bind(<span class="hljs-string">"127.0.0.1:3000"</span>)
        .<span class="hljs-keyword">await</span>
        .unwrap();

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Rusty Bean Coffee Shop API running on http://localhost:3000"</span>);

    axum::serve(listener, app).<span class="hljs-keyword">await</span>.unwrap();
}
</code></pre>
<h2 id="heading-the-implementation">The Implementation</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766169330432/f2c51107-41f2-4fb1-baad-13dd8732677d.png" alt class="image--center mx-auto" /></p>
<p>Let's see the complete files:</p>
<p><strong>Complete</strong> <code>src/strategies.rs</code></p>
<pre><code class="lang-rust"><span class="hljs-comment">// The Strategy Pattern: Each discount strategy implements this trait.</span>
<span class="hljs-comment">// This allows us to swap discount algorithms at runtime without changing the order logic.</span>

<span class="hljs-comment">/// The core trait that all discount strategies must implement.</span>
<span class="hljs-comment">/// This is the "Strategy" in Strategy Pattern.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DiscountStrategy</span></span>: <span class="hljs-built_in">Send</span> + <span class="hljs-built_in">Sync</span> {
    <span class="hljs-comment">/// Calculate the final price after applying the discount.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;

    <span class="hljs-comment">/// Get the name of this discount strategy.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;

    <span class="hljs-comment">/// Get a description of how this discount works.</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span>;
}

<span class="hljs-comment">// ============================================================================</span>
<span class="hljs-comment">// CONCRETE STRATEGIES - Each one is a different discount algorithm</span>
<span class="hljs-comment">// ============================================================================</span>

<span class="hljs-comment">/// Student Discount: 20% off all drinks.</span>
<span class="hljs-comment">/// Used for customers with valid student ID.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StudentDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> StudentDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.80</span> <span class="hljs-comment">// 20% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Student Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"20% off all drinks with valid student ID"</span>
    }
}

<span class="hljs-comment">/// Senior Discount: 15% off all drinks.</span>
<span class="hljs-comment">/// Used for customers 65 years or older.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SeniorDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> SeniorDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.85</span> <span class="hljs-comment">// 15% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Senior Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"15% off all drinks for seniors (65+)"</span>
    }
}

<span class="hljs-comment">/// Loyalty Card Discount: 10% off all drinks.</span>
<span class="hljs-comment">/// Also earns points for future rewards (can be extended for this feature).</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LoyaltyDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> LoyaltyDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.90</span> <span class="hljs-comment">// 10% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Loyalty Card Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"10% off all drinks + earn points towards free drinks"</span>
    }
}

<span class="hljs-comment">/// No Discount: Regular price.</span>
<span class="hljs-comment">/// Used for customers without any discount eligibility.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">NoDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> NoDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price <span class="hljs-comment">// No discount applied.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Regular Price"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Standard pricing with no discount"</span>
    }
}

<span class="hljs-comment">// ============================================================================</span>
<span class="hljs-comment">// BONUS: Happy Hour Discount (Added easily thanks to Strategy Pattern!)</span>
<span class="hljs-comment">// ============================================================================</span>

<span class="hljs-comment">/// Happy Hour Discount: 25% off all drinks.</span>
<span class="hljs-comment">/// Only available during happy hour (3-5 PM).</span>
<span class="hljs-comment">/// This shows how easy it is to add new strategies!</span>
<span class="hljs-meta">#[allow(dead_code)]</span> <span class="hljs-comment">// We're not using this in the API yet, but it's here to demonstrate extensibility.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">HappyHourDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> HappyHourDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.75</span> <span class="hljs-comment">// 25% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Happy Hour Discount"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"25% off all drinks during happy hour (3-5 PM)"</span>
    }
}

<span class="hljs-meta">#[cfg(test)]</span>
<span class="hljs-keyword">mod</span> tests {
    <span class="hljs-keyword">use</span> super::*;

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_student_discount</span></span>() {
        <span class="hljs-keyword">let</span> strategy = StudentDiscount;
        <span class="hljs-built_in">assert_eq!</span>(strategy.apply_discount(<span class="hljs-number">10.0</span>), <span class="hljs-number">8.0</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_senior_discount</span></span>() {
        <span class="hljs-keyword">let</span> strategy = SeniorDiscount;
        <span class="hljs-built_in">assert_eq!</span>(strategy.apply_discount(<span class="hljs-number">10.0</span>), <span class="hljs-number">8.5</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_loyalty_discount</span></span>() {
        <span class="hljs-keyword">let</span> strategy = LoyaltyDiscount;
        <span class="hljs-built_in">assert_eq!</span>(strategy.apply_discount(<span class="hljs-number">10.0</span>), <span class="hljs-number">9.0</span>);
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_no_discount</span></span>() {
        <span class="hljs-keyword">let</span> strategy = NoDiscount;
        <span class="hljs-built_in">assert_eq!</span>(strategy.apply_discount(<span class="hljs-number">10.0</span>), <span class="hljs-number">10.0</span>);
    }
}
</code></pre>
<h3 id="heading-testing-our-solution">Testing Our Solution</h3>
<p><strong>Running the Server:</strong></p>
<pre><code class="lang-bash">cargo run
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">Rusty</span> <span class="hljs-string">Bean</span> <span class="hljs-string">Coffee</span> <span class="hljs-string">Shop</span> <span class="hljs-string">API</span> <span class="hljs-string">running</span> <span class="hljs-string">on</span> <span class="hljs-string">http://localhost:3000</span>
    <span class="hljs-string">Compiling</span> <span class="hljs-string">coffee-shop-api</span> <span class="hljs-string">v0.1.0</span>
    <span class="hljs-string">Finished</span> <span class="hljs-string">dev</span> [<span class="hljs-string">unoptimized</span> <span class="hljs-string">+</span> <span class="hljs-string">debuginfo</span>] <span class="hljs-string">target(s)</span> <span class="hljs-string">in</span> <span class="hljs-number">3.</span><span class="hljs-string">21s</span>
     <span class="hljs-string">Running</span> <span class="hljs-string">`target/debug/coffee-shop-api`</span>
</code></pre>
<p>Success! The server is running. Now let's test it.</p>
<h3 id="heading-testing-with-curl">Testing with curl</h3>
<p>Let's test our API with some real requests:</p>
<p><strong>Test 1: Student orders a latte</strong></p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/order \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "coffee_type": "Latte",
    "customer_type": "student",
    "base_price": 5.00
  }'</span>
</code></pre>
<p><strong>Response:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"coffee_type"</span>: <span class="hljs-string">"Latte"</span>,
  <span class="hljs-attr">"base_price"</span>: <span class="hljs-number">5.0</span>,
  <span class="hljs-attr">"discount_type"</span>: <span class="hljs-string">"Student Discount"</span>,
  <span class="hljs-attr">"discount_description"</span>: <span class="hljs-string">"20% off all drinks with valid student ID"</span>,
  <span class="hljs-attr">"discount_amount"</span>: <span class="hljs-number">1.0</span>,
  <span class="hljs-attr">"final_price"</span>: <span class="hljs-number">4.0</span>
}
</code></pre>
<p>The student gets 20% off their $5.00 latte, paying only $4.00.</p>
<p><strong>Test 2: Senior orders a cappuccino</strong></p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/order \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "coffee_type": "Cappuccino",
    "customer_type": "senior", 
    "base_price": 4.50
  }'</span>
</code></pre>
<p><strong>Response:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"coffee_type"</span>: <span class="hljs-string">"Cappuccino"</span>,
  <span class="hljs-attr">"base_price"</span>: <span class="hljs-number">4.5</span>,
  <span class="hljs-attr">"discount_type"</span>: <span class="hljs-string">"Senior Discount"</span>,
  <span class="hljs-attr">"discount_description"</span>: <span class="hljs-string">"15% off all drinks for seniors (65+)"</span>,
  <span class="hljs-attr">"discount_amount"</span>: <span class="hljs-number">0.675</span>,
  <span class="hljs-attr">"final_price"</span>: <span class="hljs-number">3.825</span>
}
</code></pre>
<p>The senior citizen gets 15% off.</p>
<p><strong>Test 3: Regular customer orders an espresso</strong></p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/order \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "coffee_type": "Espresso",
    "customer_type": "none",
    "base_price": 3.00
  }'</span>
</code></pre>
<p><strong>Response:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"coffee_type"</span>: <span class="hljs-string">"Espresso"</span>,
  <span class="hljs-attr">"base_price"</span>: <span class="hljs-number">3.0</span>,
  <span class="hljs-attr">"discount_type"</span>: <span class="hljs-string">"Regular Price"</span>,
  <span class="hljs-attr">"discount_description"</span>: <span class="hljs-string">"Standard pricing with no discount"</span>,
  <span class="hljs-attr">"discount_amount"</span>: <span class="hljs-number">0.0</span>,
  <span class="hljs-attr">"final_price"</span>: <span class="hljs-number">3.0</span>
}
</code></pre>
<p>Regular customers pay full price.</p>
<h3 id="heading-missing-pieces">Missing Pieces</h3>
<p>Let's add the missing handler for listing available discounts:</p>
<p><strong>Complete</strong> <code>src/handlers.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> crate::models::DiscountType;
<span class="hljs-keyword">use</span> crate::models::{DiscountInfo, DiscountsResponse, OrderRequest, OrderResponse};
<span class="hljs-keyword">use</span> crate::order::CoffeeOrder;
<span class="hljs-keyword">use</span> crate::strategies::{
    DiscountStrategy, LoyaltyDiscount, NoDiscount, SeniorDiscount, StudentDiscount,
};
<span class="hljs-keyword">use</span> axum::{Json, http::StatusCode};

<span class="hljs-comment">/// Calculates the final price with the selected discount strategy.</span>
<span class="hljs-comment">/// REQUEST METHOD: POST</span>
<span class="hljs-comment">/// REQUEST URL: /order</span>
<span class="hljs-comment">/// REQUEST BODY: { "coffee_type": "latte", "customer_type": "student", "base_price": 5.0 }</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_order</span></span>(
    Json(payload): Json&lt;OrderRequest&gt;,
) -&gt; <span class="hljs-built_in">Result</span>&lt;Json&lt;OrderResponse&gt;, StatusCode&gt; {
    <span class="hljs-keyword">if</span> payload.base_price &lt;= <span class="hljs-number">0.0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(StatusCode::BAD_REQUEST);
    }

    <span class="hljs-keyword">let</span> discount_type =
        DiscountType::from_string(&amp;payload.customer_type).ok_or(StatusCode::BAD_REQUEST)?;
    <span class="hljs-keyword">let</span> order = CoffeeOrder::new(
        payload.coffee_type.clone(),
        payload.base_price,
        discount_type,
    );

    <span class="hljs-keyword">let</span> final_price = order.calculate_final_price();
    <span class="hljs-keyword">let</span> discount_amount = order.get_discount_amount();

    <span class="hljs-keyword">let</span> response = OrderResponse {
        coffee_type: payload.coffee_type,
        base_price: payload.base_price,
        discount_type: order.get_discount_name().to_string(),
        discount_description: order.get_discount_description().to_string(),
        discount_amount,
        final_price,
    };

    <span class="hljs-literal">Ok</span>(Json(response))
}

<span class="hljs-comment">/// Lists all available discount strategies.</span>
<span class="hljs-comment">/// REQUEST METHOD: GET</span>
<span class="hljs-comment">/// REQUEST URL: /discounts</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">list_discounts</span></span>() -&gt; Json&lt;DiscountsResponse&gt; {
    <span class="hljs-keyword">let</span> student: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-built_in">Box</span>::new(StudentDiscount);
    <span class="hljs-keyword">let</span> senior: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-built_in">Box</span>::new(SeniorDiscount);
    <span class="hljs-keyword">let</span> loyalty: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-built_in">Box</span>::new(LoyaltyDiscount);
    <span class="hljs-keyword">let</span> none: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-built_in">Box</span>::new(NoDiscount);

    <span class="hljs-keyword">let</span> discounts = <span class="hljs-built_in">vec!</span>[
        DiscountInfo {
            code: <span class="hljs-string">"student"</span>.to_string(),
            name: student.name().to_string(),
            description: student.description().to_string(),
        },
        DiscountInfo {
            code: <span class="hljs-string">"senior"</span>.to_string(),
            name: senior.name().to_string(),
            description: senior.description().to_string(),
        },
        DiscountInfo {
            code: <span class="hljs-string">"loyalty"</span>.to_string(),
            name: loyalty.name().to_string(),
            description: loyalty.description().to_string(),
        },
        DiscountInfo {
            code: <span class="hljs-string">"none"</span>.to_string(),
            name: none.name().to_string(),
            description: none.description().to_string(),
        },
    ];

    Json(DiscountsResponse {
        available_discounts: discounts,
    })
}
</code></pre>
<p><strong>Complete</strong> <code>src/models.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> serde::{Deserialize, Serialize};

<span class="hljs-comment">/// Request body for creating an order.</span>
<span class="hljs-meta">#[derive(Debug, Deserialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OrderRequest</span></span> {
    <span class="hljs-keyword">pub</span> coffee_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> customer_type: <span class="hljs-built_in">String</span>, <span class="hljs-comment">// "student", "senior", "loyalty", or "none".</span>
    <span class="hljs-keyword">pub</span> base_price: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-comment">/// Response body for order calculation.</span>
<span class="hljs-meta">#[derive(Debug, Serialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OrderResponse</span></span> {
    <span class="hljs-keyword">pub</span> coffee_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> base_price: <span class="hljs-built_in">f64</span>,
    <span class="hljs-keyword">pub</span> discount_type: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> discount_description: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> discount_amount: <span class="hljs-built_in">f64</span>,
    <span class="hljs-keyword">pub</span> final_price: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-comment">/// Response for listing available discounts.</span>
<span class="hljs-meta">#[derive(Debug, Serialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DiscountInfo</span></span> {
    <span class="hljs-keyword">pub</span> code: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> name: <span class="hljs-built_in">String</span>,
    <span class="hljs-keyword">pub</span> description: <span class="hljs-built_in">String</span>,
}

<span class="hljs-comment">/// Response body for listing all available discounts.</span>
<span class="hljs-meta">#[derive(Debug, Serialize)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DiscountsResponse</span></span> {
    <span class="hljs-keyword">pub</span> available_discounts: <span class="hljs-built_in">Vec</span>&lt;DiscountInfo&gt;,
}

<span class="hljs-comment">/// Enum representing different discount types.</span>
<span class="hljs-comment">/// This is used to select which strategy to use.</span>
<span class="hljs-meta">#[derive(Debug, Clone, Copy)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">DiscountType</span></span> {
    Student,
    Senior,
    Loyalty,
    <span class="hljs-literal">None</span>,
}

<span class="hljs-keyword">impl</span> DiscountType {
    <span class="hljs-comment">/// Parse a string into a DiscountType.</span>
    <span class="hljs-comment">/// Returns None if the string doesn't match any known type.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_string</span></span>(s: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">match</span> s.to_lowercase().as_str() {
            <span class="hljs-string">"student"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Student),
            <span class="hljs-string">"senior"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Senior),
            <span class="hljs-string">"loyalty"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Loyalty),
            <span class="hljs-string">"none"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::<span class="hljs-literal">None</span>),
            _ =&gt; <span class="hljs-literal">None</span>,
        }
    }

    <span class="hljs-comment">/// Convert DiscountType to a string code.</span>
    <span class="hljs-meta">#[allow(dead_code)]</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">to_string</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span> {
            DiscountType::Student =&gt; <span class="hljs-string">"student"</span>,
            DiscountType::Senior =&gt; <span class="hljs-string">"senior"</span>,
            DiscountType::Loyalty =&gt; <span class="hljs-string">"loyalty"</span>,
            DiscountType::<span class="hljs-literal">None</span> =&gt; <span class="hljs-string">"none"</span>,
        }
    }
}

<span class="hljs-meta">#[cfg(test)]</span>
<span class="hljs-keyword">mod</span> tests {
    <span class="hljs-keyword">use</span> super::*;

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_discount_type_parsing</span></span>() {
        <span class="hljs-built_in">assert!</span>(matches!(
            DiscountType::from_string(<span class="hljs-string">"student"</span>),
            <span class="hljs-literal">Some</span>(DiscountType::Student)
        ));
        <span class="hljs-built_in">assert!</span>(matches!(
            DiscountType::from_string(<span class="hljs-string">"SENIOR"</span>),
            <span class="hljs-literal">Some</span>(DiscountType::Senior)
        ));
        <span class="hljs-built_in">assert!</span>(DiscountType::from_string(<span class="hljs-string">"invalid"</span>).is_none());
    }
}
</code></pre>
<p><strong>Test the discounts endpoint:</strong></p>
<pre><code class="lang-bash">curl http://localhost:3000/discounts
</code></pre>
<p><strong>Response:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"discounts"</span>: [
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Student Discount"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"20% off all drinks with valid student ID"</span>,
      <span class="hljs-attr">"percentage"</span>: <span class="hljs-string">"20%"</span>,
      <span class="hljs-attr">"example_savings"</span>: <span class="hljs-string">"$2.00 off on a $10.00 drink"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Senior Discount"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"15% off all drinks for seniors (65+)"</span>,
      <span class="hljs-attr">"percentage"</span>: <span class="hljs-string">"15%"</span>,
      <span class="hljs-attr">"example_savings"</span>: <span class="hljs-string">"$1.50 off on a $10.00 drink"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Loyalty Card Discount"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"10% off all drinks + earn points towards free drinks"</span>,
      <span class="hljs-attr">"percentage"</span>: <span class="hljs-string">"10%"</span>,
      <span class="hljs-attr">"example_savings"</span>: <span class="hljs-string">"$1.00 off on a $10.00 drink"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Regular Price"</span>,
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Standard pricing with no discount"</span>,
      <span class="hljs-attr">"percentage"</span>: <span class="hljs-string">"0%"</span>,
      <span class="hljs-attr">"example_savings"</span>: <span class="hljs-string">"$0.00 off on a $10.00 drink"</span>
    }
  ]
}
</code></pre>
<p>Let's run our unit tests to make sure everything works:</p>
<pre><code class="lang-bash">cargo <span class="hljs-built_in">test</span>
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">running</span> <span class="hljs-number">7</span> <span class="hljs-string">tests</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_student_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_senior_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_loyalty_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">strategies::tests::test_no_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">order::tests::test_order_with_student_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">order::tests::test_order_with_no_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>
<span class="hljs-string">test</span> <span class="hljs-string">order::tests::test_order_with_senior_discount</span> <span class="hljs-string">...</span> <span class="hljs-string">ok</span>

<span class="hljs-attr">test result:</span> <span class="hljs-string">ok.</span> <span class="hljs-number">7</span> <span class="hljs-string">passed;</span> <span class="hljs-number">0</span> <span class="hljs-string">failed;</span> <span class="hljs-number">0</span> <span class="hljs-string">ignored;</span> <span class="hljs-number">0</span> <span class="hljs-string">measured;</span> <span class="hljs-number">0</span> <span class="hljs-string">filtered</span> <span class="hljs-string">out;</span> <span class="hljs-string">finished</span> <span class="hljs-string">in</span> <span class="hljs-number">0.</span><span class="hljs-string">00s</span>
</code></pre>
<p>Our implementation is solid.</p>
<p><strong>Alex leans back in the chair, a smile spreading across their face</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766131216865/046e48f7-dbac-43c4-bb69-6609180bda79.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-resolution">The Resolution</h2>
<p>Three weeks later. Friday evening, 7:15 PM.</p>
<p>Alex stands behind the counter, watching the last customers of the day leave. The laptop is open, showing a dashboard of the day's sales. Everything has been running smoothly since implementing the Strategy Pattern.</p>
<p>Sarah walks in, ordering her usual cappuccino.</p>
<p><strong>Sarah</strong>: "So? How's the new system working out?"</p>
<p><strong>Alex</strong> <em>(beaming)</em>: "Sarah, you saved my business. Remember that Happy Hour discount that Jamie wanted? The one that would've taken me a week to implement with the old code?"</p>
<p><strong>Sarah</strong>: "The one that was going to ruin your life?"</p>
<p><strong>Alex</strong>: "I implemented it in 45 minutes."</p>
<p><strong>Sarah</strong>: <em>(raising her eyebrows)</em> "Seriously?"</p>
<p><strong>Alex</strong>: "Watch this."</p>
<p>Alex opens the laptop and added a new strategy in <code>src/strategies.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">/// Happy Hour Discount: 25% off all drinks.</span>
<span class="hljs-comment">/// Valid from 3 PM to 5 PM on weekdays.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">HappyHourDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> HappyHourDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * <span class="hljs-number">0.75</span> <span class="hljs-comment">// 25% off.</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Happy Hour Special"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"25% off all drinks (3 PM - 5 PM weekdays)"</span>
    }
}
</code></pre>
<p>Then adds one line to <code>models.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">DiscountType</span></span> {
    Student,
    Senior,
    Loyalty,
    HappyHour,  <span class="hljs-comment">// &lt;- Just this!</span>
    <span class="hljs-literal">None</span>,
}
</code></pre>
<p>And updates the parser:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> DiscountType {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_string</span></span>(s: &amp;<span class="hljs-built_in">str</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">match</span> s.to_lowercase().as_str() {
            <span class="hljs-string">"student"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Student),
            <span class="hljs-string">"senior"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Senior),
            <span class="hljs-string">"loyalty"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::Loyalty),
            <span class="hljs-string">"happyhour"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::HappyHour),  <span class="hljs-comment">// &lt;- And this!</span>
            <span class="hljs-string">"none"</span> =&gt; <span class="hljs-literal">Some</span>(DiscountType::<span class="hljs-literal">None</span>),
            _ =&gt; <span class="hljs-literal">None</span>,
        }
    }
}
</code></pre>
<p>Finally, updates the factory in <code>order.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> discount_strategy: <span class="hljs-built_in">Box</span>&lt;<span class="hljs-keyword">dyn</span> DiscountStrategy&gt; = <span class="hljs-keyword">match</span> discount_type {
    DiscountType::Student =&gt; <span class="hljs-built_in">Box</span>::new(StudentDiscount),
    DiscountType::Senior =&gt; <span class="hljs-built_in">Box</span>::new(SeniorDiscount),
    DiscountType::Loyalty =&gt; <span class="hljs-built_in">Box</span>::new(LoyaltyDiscount),
    DiscountType::HappyHour =&gt; <span class="hljs-built_in">Box</span>::new(HappyHourDiscount),  <span class="hljs-comment">// &lt;- Just this line!</span>
    DiscountType::<span class="hljs-literal">None</span> =&gt; <span class="hljs-built_in">Box</span>::new(NoDiscount),
};
</code></pre>
<p><strong>Alex</strong>: "That's it. Three small changes. <mark>No touching the existing strategies. No modifying the order calculation logic. No hunting through 12 different files. Just add the new strategy and plug it in.</mark>"</p>
<p><strong>Sarah</strong>: "And it just... works?"</p>
<p><strong>Alex</strong>: "It just works. And you know what the best part is?"</p>
<p><strong>Sarah</strong>: "What?"</p>
<p><strong>Alex</strong>: "Last month, Jamie asked for a 'Buy One Get One Free' discount for couples. With the old code, I would've panicked. But now?"</p>
<p>Alex shows Sarah another strategy:</p>
<pre><code class="lang-rust"><span class="hljs-comment">/// BOGO Discount for couples: Buy one drink, get second 50% off.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CoupleDiscount</span></span>;

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> CoupleDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-comment">// First drink: full price.</span>
        <span class="hljs-comment">// Second drink: 50% off.</span>
        <span class="hljs-comment">// Average per drink: 75% of normal price.</span>
        base_price * <span class="hljs-number">0.75</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Couples Special"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Buy one drink, get second 50% off"</span>
    }
}
</code></pre>
<p><strong>Alex</strong>: "Fifteen minutes. Implemented, tested, and deployed."</p>
<p><strong>Sarah</strong> <em>(genuinely impressed)</em>: "You've really internalised this pattern."</p>
<p><strong>Alex</strong>: "You know what's crazy? I was up until 2 AM for three weeks straight, fighting with that if-else nightmare. Now I close on time, the code is clean, and I actually <em>enjoy</em> adding new features. I even started a blog about it!"</p>
<p><strong>Sarah</strong>: "A blog?"</p>
<p><strong>Alex</strong>: "Yeah, 'The Coffee Shop Crisis' Figured other people might be struggling with the same stuff. Got 500 readers in the first week."</p>
<p><strong>Sarah smiles, picking up her cappuccino</strong>: "That's the power of good software design, Alex. It doesn't just make your code better, it makes your <em>life</em> better."</p>
<p><strong>Alex</strong>: "Thank you, Sarah. Really. You didn't just help me fix my code. You taught me how to think about problems differently."</p>
<p>As Sarah heads out, Alex looks at the clean, organised codebase on the screen. Three months ago, this coffee shop was a dream filled with stress. Now it's a dream that works.</p>
<p>And it's all because Alex learned to think in patterns.</p>
<h2 id="heading-the-use-and-no-use">The Use (And No Use)</h2>
<p>The Strategy Pattern is powerful, but it's not always the right tool. Here's when to use it and when to avoid it.</p>
<h3 id="heading-use-strategy-pattern-when">Use Strategy Pattern When:</h3>
<h4 id="heading-1-you-have-family-of-algorithms">1. <strong>You Have Family of Algorithms</strong></h4>
<p>When you have multiple ways to accomplish the same task:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Good use case: Different payment methods.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PaymentStrategy</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process_payment</span></span>(&amp;<span class="hljs-keyword">self</span>, amount: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;(), <span class="hljs-built_in">String</span>&gt;;
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CreditCardPayment</span></span>;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">PayPalPayment</span></span>;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CryptoPayment</span></span>;
</code></pre>
<h4 id="heading-2-behaviour-needs-to-change-at-runtime">2. <strong>Behaviour Needs to Change at Runtime</strong></h4>
<p>When you don't know which algorithm to use until runtime:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// The discount applied depends on customer data we fetch at runtime.</span>
<span class="hljs-keyword">let</span> customer = fetch_customer_from_database(customer_id);
<span class="hljs-keyword">let</span> discount = <span class="hljs-keyword">match</span> customer.status {
    CustomerStatus::New =&gt; <span class="hljs-built_in">Box</span>::new(WelcomeDiscount),
    CustomerStatus::VIP =&gt; <span class="hljs-built_in">Box</span>::new(VIPDiscount),
    CustomerStatus::Regular =&gt; <span class="hljs-built_in">Box</span>::new(NoDiscount),
};
</code></pre>
<h4 id="heading-3-you-want-to-avoid-complex-conditionals">3. <strong>You Want to Avoid Complex Conditionals</strong></h4>
<p>When you have this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// DON'T DO THIS</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_shipping</span></span>(method: &amp;<span class="hljs-built_in">str</span>, weight: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
    <span class="hljs-keyword">if</span> method == <span class="hljs-string">"standard"</span> {
        <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">5.0</span> {
            <span class="hljs-number">5.99</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">20.0</span> {
            <span class="hljs-number">12.99</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-number">25.99</span>
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> method == <span class="hljs-string">"express"</span> {
        <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">5.0</span> {
            <span class="hljs-number">15.99</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">20.0</span> {
            <span class="hljs-number">28.99</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-number">50.99</span>
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> method == <span class="hljs-string">"overnight"</span> {
        <span class="hljs-comment">// ... you get the idea.</span>
    }
}
</code></pre>
<p>Do this instead:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// DO THIS.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">ShippingStrategy</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_cost</span></span>(&amp;<span class="hljs-keyword">self</span>, weight: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StandardShipping</span></span>;
<span class="hljs-keyword">impl</span> ShippingStrategy <span class="hljs-keyword">for</span> StandardShipping {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_cost</span></span>(&amp;<span class="hljs-keyword">self</span>, weight: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">5.0</span> { <span class="hljs-number">5.99</span> }
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> weight &lt; <span class="hljs-number">20.0</span> { <span class="hljs-number">12.99</span> }
        <span class="hljs-keyword">else</span> { <span class="hljs-number">25.99</span> }
    }
}
</code></pre>
<h4 id="heading-4-openclosed-principle-matters">4. <strong>Open/Closed Principle Matters</strong></h4>
<p>When you want to add new behaviours without modifying existing code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Adding a new discount doesn't require touching existing code.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SeasonalDiscount</span></span> {
    percentage: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-keyword">impl</span> DiscountStrategy <span class="hljs-keyword">for</span> SeasonalDiscount {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(&amp;<span class="hljs-keyword">self</span>, base_price: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
        base_price * (<span class="hljs-number">1.0</span> - <span class="hljs-keyword">self</span>.percentage)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">name</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Seasonal Sale"</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;<span class="hljs-built_in">str</span> {
        <span class="hljs-string">"Limited time seasonal discount"</span>
    }
}
</code></pre>
<h3 id="heading-dont-use-strategy-pattern-when">Don't Use Strategy Pattern When:</h3>
<h4 id="heading-1-you-only-have-one-algorithm">1. <strong>You Only Have One Algorithm</strong></h4>
<p>If there's only one way to do something, don't over-engineer:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// BAD: Unnecessary abstraction.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">EmailSender</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">send_email</span></span>(&amp;<span class="hljs-keyword">self</span>, to: &amp;<span class="hljs-built_in">str</span>, body: &amp;<span class="hljs-built_in">str</span>);
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SmtpEmailSender</span></span>;  <span class="hljs-comment">// Only one implementation.</span>

<span class="hljs-comment">// GOOD: Just write the function.</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">send_email</span></span>(to: &amp;<span class="hljs-built_in">str</span>, body: &amp;<span class="hljs-built_in">str</span>) {
    <span class="hljs-comment">// Send email using SMTP.</span>
}
</code></pre>
<h4 id="heading-2-the-algorithms-are-simple-and-rarely-change">2. <strong>The Algorithms are Simple and Rarely Change</strong></h4>
<pre><code class="lang-rust"><span class="hljs-comment">// BAD: Over-engineering a simple calculation.</span>
<span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">TaxStrategy</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_tax</span></span>(&amp;<span class="hljs-keyword">self</span>, amount: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span>;
}

<span class="hljs-comment">// GOOD: Simple function is fine.</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_tax</span></span>(amount: <span class="hljs-built_in">f64</span>, tax_rate: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">f64</span> {
    amount * tax_rate
}
</code></pre>
<h4 id="heading-3-performance-is-critical-and-predictable">3. <strong>Performance is Critical and Predictable</strong></h4>
<p>If you need maximum performance and know the algorithm at compile time:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Use generics (static dispatch) instead of trait objects (dynamic dispatch).</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process</span></span>&lt;T: Strategy&gt;(strategy: T, data: &amp;Data) {
    strategy.execute(data)  <span class="hljs-comment">// No vtable lookup, can be inlined.</span>
}
</code></pre>
<h4 id="heading-4-the-strategies-share-a-lot-of-common-code">4. <strong>The Strategies Share a Lot of Common Code</strong></h4>
<p>If implementations are mostly the same with small variations:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Instead of Strategy Pattern, use configuration</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DiscountConfig</span></span> {
    percentage: <span class="hljs-built_in">f64</span>,
    min_purchase: <span class="hljs-built_in">f64</span>,
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_discount</span></span>(price: <span class="hljs-built_in">f64</span>, config: &amp;DiscountConfig) -&gt; <span class="hljs-built_in">f64</span> {
    <span class="hljs-keyword">if</span> price &gt;= config.min_purchase {
        price * (<span class="hljs-number">1.0</span> - config.percentage)
    } <span class="hljs-keyword">else</span> {
        price
    }
}
</code></pre>
<h2 id="heading-the-conclusion">The Conclusion</h2>
<p>We started with Alex's nightmare: a tangled mess of if-else statements that made every change painful. Through the Strategy Pattern, we transformed that chaos into clean, maintainable code.</p>
<p><strong>What we learned:</strong></p>
<ol>
<li><p><strong>Strategy Pattern separates WHAT from HOW</strong>: The order doesn't know how discounts are calculated, it just knows to apply them</p>
</li>
<li><p><strong>Traits enable polymorphism in Rust</strong>: Different types sharing the same interface</p>
</li>
<li><p><strong>Box enables runtime flexibility</strong>: Choose algorithms dynamically</p>
</li>
<li><p><strong>Open/Closed Principle in action</strong>: Add new discounts without modifying existing code</p>
</li>
<li><p><strong>Thread safety with Send + Sync</strong>: Our strategies work seamlessly in async contexts</p>
</li>
</ol>
<p>But more importantly, we learned that good software design isn't just about the code, it's about making your life easier. Alex went from drowning in technical debt to enjoying feature development. That's the real power of design patterns.</p>
<p><em>Written with ❤️ by a developer who's been in Alex's shoes. We've all had our 2 AM debugging sessions. Design patterns are the coffee that keeps our code awake and healthy.</em></p>
<p><em>Special thanks to the Rust community for building such an amazing language and ecosystem.</em></p>
<p><strong>Stay caffeinated, stay rusty! 🦀</strong></p>
]]></content:encoded></item><item><title><![CDATA[Self-Secured Folders with Electron]]></title><description><![CDATA[You've got sensitive files on your laptop. Medical records, financial documents, private photos, confidential work files. Sure, you could password-protect them, encrypt them with traditional methods, or hide them in obscure folders. But here's the th...]]></description><link>https://writer.mrmehta.in/self-secured-folders-with-electron</link><guid isPermaLink="true">https://writer.mrmehta.in/self-secured-folders-with-electron</guid><category><![CDATA[Electron]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Self-Protocol]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 21 Nov 2025 19:40:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763724290819/ca8ed94b-29d5-46a6-88ed-a308a789bdf0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You've got sensitive files on your laptop. Medical records, financial documents, private photos, confidential work files. Sure, you could password-protect them, encrypt them with traditional methods, or hide them in obscure folders. But here's the thing, these approaches all have the same fundamental flaw: <strong>they can't verify <em>who</em> you actually are</strong>.</p>
<p>And it's not just personal computers. Think about enterprise environments, NAS (Network-Attached Storage) devices, Active Directory file shares, departmental folders on shared drives. <mark>Multiple people have access, but how do you ensure only the </mark> <em><mark>right</mark></em> <mark>people with the </mark> <em><mark>right</mark></em> <mark>verified attributes can decrypt specific folders?</mark> You can't just rely on usernames and passwords when compliance requires proof of age, sanctions screening, or other identity attributes.</p>
<p>That's when it hit me. What if I could gate access to files not just with a password, but with actual identity verification? What if I could set rules like "only decrypt this if the user is over 18" or "only if they're not on any sanctions lists"? And most importantly, <strong>what if I could do all this without compromising privacy?</strong></p>
<blockquote>
<p><strong><em>Want to follow along? All the code from this article is available on</em></strong> <a target="_blank" href="https://github.com/kartikmehta8/self-secure-folders"><strong><em>GitHub</em></strong></a><strong><em>. Feel free to clone, experiment, and build upon it!</em></strong></p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/self-secure-folders">https://github.com/kartikmehta8/self-secure-folders</a></div>
<p> </p>
<h2 id="heading-what-are-we-building-here">What Are We Building Here?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763725272532/4ab177dc-8a31-4f50-a6cc-3c3b7625ed5d.png" alt class="image--center mx-auto" /></p>
<p>I created a desktop application that lets you:</p>
<ol>
<li><p><strong>Encrypt entire folders</strong> with military-grade AES-256-GCM encryption</p>
</li>
<li><p><strong>Set custom access rules</strong> per folder (minimum age, gender, OFAC sanctions clearance)</p>
</li>
<li><p><strong>Verify your identity</strong> using the Self protocol before decrypting anything</p>
</li>
<li><p><strong>Keep everything local</strong> and private on your machine</p>
</li>
</ol>
<p><mark>The folders literally disappear from your file system (they get hidden and encrypted), and the only way to access them is through the app after proving you meet the specific requirements you set.</mark></p>
<h2 id="heading-why-self-protocol">Why Self Protocol?</h2>
<p><a target="_blank" href="https://self.xyz"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763725401075/4d85dc52-7a0a-4698-89a8-1fb78297bcd1.png" alt class="image--center mx-auto" /></a></p>
<p>Traditional identity verification is broken. When you verify your age on a website, you typically upload your entire ID or passport. They see your name, address, date of birth, ID number, everything. <em>You're giving away the farm just to prove you're 21</em>.</p>
<p>Self flips this on its head with <strong>zero-knowledge proofs</strong>. Here's how it works:</p>
<ol>
<li><p>You scan your passport with your phone's NFC reader</p>
</li>
<li><p>Self generates cryptographic proofs about you</p>
</li>
<li><p>When an app needs verification, you share only what's required</p>
</li>
<li><p>The app gets a mathematical proof that you're over 18 (for example) without ever seeing your actual birthdate</p>
</li>
</ol>
<p><mark>It's like proving you can unlock a door without showing the key itself. Mind-blowing stuff.</mark></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763725550513/8d00331e-f617-42ea-aa4e-b22431642256.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-architecture">The Architecture</h2>
<p>This application is split into three distinct parts that work together seamlessly:</p>
<h3 id="heading-the-electron-main-process">The Electron Main Process</h3>
<p>This is where the magic happens. The Electron main process handles all the heavy lifting:</p>
<ul>
<li><p>File system operations</p>
</li>
<li><p>Encryption/decryption with AES-256-GCM</p>
</li>
<li><p>Storing folder configurations</p>
</li>
<li><p>Managing encryption keys</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763725638904/b12ed6e4-1eae-4762-bbb9-4a437074ed7b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-nextjs-frontend">The Next.js Frontend</h3>
<p>Built with Next.js and React, this provides a clean, modern interface for:</p>
<ul>
<li><p>Browsing and managing encrypted folders</p>
</li>
<li><p>Configuring folder access rules</p>
</li>
<li><p>Displaying Self QR codes for verification</p>
</li>
<li><p>Showing verification status in real-time</p>
</li>
</ul>
<h3 id="heading-the-express-backend">The Express Backend</h3>
<p>A lightweight Express server that:</p>
<ul>
<li><p>Integrates with Self protocol's backend verifier</p>
</li>
<li><p>Manages per-folder verification configurations</p>
</li>
<li><p>Validates zero-knowledge proofs</p>
</li>
<li><p>Checks age, gender, and OFAC requirements</p>
</li>
</ul>
<blockquote>
<p>Checkout: <a target="_blank" href="https://github.com/kartikmehta8/self-secure-folders">kartikmehta8/self-secure-folders</a></p>
</blockquote>
<h2 id="heading-how-encryption-actually-works">How Encryption Actually Works</h2>
<p>Let's peek under the hood. When you add a folder to encrypt, here's what happens:</p>
<h3 id="heading-step-1-generate-a-random-encryption-key">Step 1: Generate a Random Encryption Key</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> id = crypto.randomUUID();
<span class="hljs-keyword">const</span> key = crypto.randomBytes(<span class="hljs-number">32</span>); <span class="hljs-comment">// 256-bit key for AES.</span>
<span class="hljs-keyword">const</span> keyBase64 = key.toString(<span class="hljs-string">"base64"</span>);
</code></pre>
<p>Each folder gets its own unique 256-bit encryption key. This key never leaves your machine and is stored securely in Electron's user data directory.</p>
<h3 id="heading-step-2-recursively-encrypt-every-file">Step 2: Recursively Encrypt Every File</h3>
<p>The app walks through your entire folder structure and encrypts each file individually:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encryptFile</span>(<span class="hljs-params">sourcePath, targetPath, key</span>) </span>{
  <span class="hljs-keyword">const</span> iv = crypto.randomBytes(<span class="hljs-number">12</span>); <span class="hljs-comment">// 12-byte initialization vector.</span>
  <span class="hljs-keyword">const</span> cipher = crypto.createCipheriv(<span class="hljs-string">"aes-256-gcm"</span>, key, iv);

  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fsPromises.readFile(sourcePath);
  <span class="hljs-keyword">const</span> encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
  <span class="hljs-keyword">const</span> authTag = cipher.getAuthTag(); <span class="hljs-comment">// For integrity verification.</span>

  <span class="hljs-comment">// Store: IV + Auth Tag + Encrypted Data.</span>
  <span class="hljs-keyword">const</span> payload = Buffer.concat([iv, authTag, encrypted]);
  <span class="hljs-keyword">await</span> fsPromises.writeFile(targetPath, payload);
}
</code></pre>
<p>Why AES-256-GCM? Two reasons:</p>
<ol>
<li><p><strong>AES-256</strong> is the gold standard for symmetric encryption, the same encryption used by governments and militaries worldwide</p>
</li>
<li><p><strong>GCM (Galois/Counter Mode)</strong> provides authenticated encryption, meaning it prevents tampering. If someone modifies even a single bit of your encrypted file, decryption will fail</p>
</li>
</ol>
<h3 id="heading-step-3-hide-the-original-folder">Step 3: Hide the Original Folder</h3>
<p>On macOS and Linux, the app automatically renames your source folder with a leading dot (like <code>.my-secret-folder</code>), making it hidden from normal view:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (process.platform !== <span class="hljs-string">"win32"</span>) {
  <span class="hljs-keyword">const</span> parentDir = path.dirname(input.sourcePath);
  <span class="hljs-keyword">const</span> baseName = path.basename(input.sourcePath);

  <span class="hljs-keyword">if</span> (!baseName.startsWith(<span class="hljs-string">"."</span>)) {
    <span class="hljs-keyword">const</span> hiddenPath = path.join(parentDir, <span class="hljs-string">`.<span class="hljs-subst">${baseName}</span>`</span>);
    <span class="hljs-keyword">await</span> fsPromises.rename(input.sourcePath, hiddenPath);
  }
}
</code></pre>
<p>Your encrypted version lives in Electron's user data directory, completely separate from your normal file system.</p>
<h2 id="heading-the-self-integration">The Self Integration</h2>
<p>This is where things get really interesting. When you click "Open" on an encrypted folder, you're not just entering a password, you're proving your identity.</p>
<h3 id="heading-setting-up-verification-rules">Setting Up Verification Rules</h3>
<p>First, you configure what requirements must be met to access a specific folder:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> conditions: FolderConditions = {
  minAge: <span class="hljs-number">18</span>,              <span class="hljs-comment">// Must be at least 18 years old.</span>
  gender: <span class="hljs-string">"female"</span>,        <span class="hljs-comment">// Optional: specific gender requirement.</span>
  requireOfacClear: <span class="hljs-literal">true</span>   <span class="hljs-comment">// Must not be on OFAC sanctions list.</span>
};
</code></pre>
<p>These rules get sent to the Express backend and stored in memory:</p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">"/api/config"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { configId, minimumAge, excludedCountries, ofac } = req.body;

  <span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">minimumAge</span>: minimumAge || <span class="hljs-number">18</span>,
    <span class="hljs-attr">excludedCountries</span>: excludedCountries || [],
    <span class="hljs-attr">ofac</span>: <span class="hljs-built_in">Boolean</span>(ofac),
  };

  <span class="hljs-keyword">await</span> configStore.setConfig(configId, config);
});
</code></pre>
<h3 id="heading-generating-the-qr-code">Generating the QR Code</h3>
<p>When you attempt to open a folder, the app generates a Self verification session:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> SelfAppBuilder({
  version: <span class="hljs-number">2</span>,
  appName: <span class="hljs-string">"Self Secure Folders"</span>,
  scope: process.env.NEXT_PUBLIC_SELF_SCOPE,
  endpoint: process.env.NEXT_PUBLIC_SELF_ENDPOINT,
  userId: ethers.ZeroAddress,
  endpointType: <span class="hljs-string">"staging_https"</span>,
  userDefinedData: configId, <span class="hljs-comment">// Links verification to specific folder.</span>
  disclosures: {
    minimumAge: folder.conditions.minAge,
    ofac: folder.conditions.requireOfacClear,
    nationality: <span class="hljs-literal">true</span>,
    gender: <span class="hljs-literal">true</span>,
  },
}).build();

setUniversalLink(getUniversalLink(app));
</code></pre>
<p>This creates a QR code that encodes all the verification requirements. When you scan it with the Self mobile app, it knows exactly what to prove.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763725963279/277a7c2f-1d97-4666-a3ff-1e3784e0e286.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-verification-flow">The Verification Flow</h3>
<p>Here's what happens when you scan that QR code:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763726024293/f89e61e4-e4d4-46d4-a7b2-15d95afaee33.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-backend-verification">Backend Verification</h3>
<p>The Express server receives the proof and validates it:</p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">"/api/verify"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { attestationId, proof, publicSignals, userContextData } = req.body;

  <span class="hljs-comment">// Verify the zero-knowledge proof.</span>
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verify(
    attestationId,
    proof,
    publicSignals,
    userContextData
  );

  <span class="hljs-keyword">const</span> { isValid, isMinimumAgeValid, isOfacValid } = result.isValidDetails;

  <span class="hljs-comment">// Check all requirements.</span>
  <span class="hljs-keyword">if</span> (!isValid || !isMinimumAgeValid || isOfacValid) {
    <span class="hljs-keyword">return</span> res.json({
      <span class="hljs-attr">status</span>: <span class="hljs-string">"error"</span>,
      <span class="hljs-attr">result</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">reason</span>: <span class="hljs-string">"Verification failed"</span>
    });
  }

  <span class="hljs-keyword">return</span> res.json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">result</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">credentialSubject</span>: result.discloseOutput
  });
});
</code></pre>
<p>The backend sees that you're over 18 (or whatever the requirement is), but it never sees your actual birthdate. It sees that you're not on OFAC sanctions lists, but it doesn't need your full identity document.</p>
<h3 id="heading-frontend-requirement-checking">Frontend Requirement Checking</h3>
<p>Once verification succeeds, the frontend does a final check against the folder's specific requirements:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSuccessfulVerification = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(debugEndpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
  <span class="hljs-keyword">const</span> result = data.verificationResult;

  <span class="hljs-comment">// Check age requirement.</span>
  <span class="hljs-keyword">if</span> (folder.conditions.minAge &amp;&amp; !result.isValidDetails.isMinimumAgeValid) {
    setStatus(<span class="hljs-string">"Minimum age requirement not satisfied"</span>);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// Check gender requirement (if specified).</span>
  <span class="hljs-keyword">if</span> (folder.conditions.gender) {
    <span class="hljs-keyword">const</span> genderFromProof = result.discloseOutput.gender.toLowerCase();
    <span class="hljs-keyword">const</span> required = folder.conditions.gender.toLowerCase();

    <span class="hljs-keyword">if</span> (!matchesGender(genderFromProof, required)) {
      setStatus(<span class="hljs-string">"Gender requirement not satisfied"</span>);
      <span class="hljs-keyword">return</span>;
    }
  }

  <span class="hljs-comment">// Check OFAC requirement.</span>
  <span class="hljs-keyword">if</span> (folder.conditions.requireOfacClear &amp;&amp; result.isValidDetails.isOfacValid) {
    setStatus(<span class="hljs-string">"OFAC check failed. Access denied."</span>);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// All checks passed - decrypt the folder!</span>
  <span class="hljs-keyword">await</span> <span class="hljs-built_in">window</span>.electronAPI.openFolder(folder.id);
};
</code></pre>
<h2 id="heading-the-decryption-process">The Decryption Process</h2>
<p>Once all verification checks pass, it's time to decrypt. The app creates a temporary decrypted copy of your folder:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">decryptFile</span>(<span class="hljs-params">sourcePath, targetPath, key</span>) </span>{
  <span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">await</span> fsPromises.readFile(sourcePath);

  <span class="hljs-comment">// Extract components from stored payload.</span>
  <span class="hljs-keyword">const</span> iv = payload.subarray(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>);
  <span class="hljs-keyword">const</span> authTag = payload.subarray(<span class="hljs-number">12</span>, <span class="hljs-number">28</span>);
  <span class="hljs-keyword">const</span> ciphertext = payload.subarray(<span class="hljs-number">28</span>);

  <span class="hljs-comment">// Decrypt.</span>
  <span class="hljs-keyword">const</span> decipher = crypto.createDecipheriv(<span class="hljs-string">"aes-256-gcm"</span>, key, iv);
  decipher.setAuthTag(authTag);

  <span class="hljs-keyword">const</span> decrypted = Buffer.concat([
    decipher.update(ciphertext),
    decipher.final()  <span class="hljs-comment">// This will throw if the auth tag doesn't match.</span>
  ]);

  <span class="hljs-keyword">await</span> fsPromises.writeFile(targetPath, decrypted);
}
</code></pre>
<p>The decrypted folder is placed in a temporary location, and your system's file explorer automatically opens it. You can work with your files normally.</p>
<h2 id="heading-the-ipc-bridge">The IPC Bridge</h2>
<p>Electron apps have a unique challenge: <mark>the frontend (renderer process) and backend (main process) run in separate contexts for security. They communicate through IPC (Inter-Process Communication).</mark></p>
<h3 id="heading-the-preload-script">The Preload Script</h3>
<p>This secure bridge exposes only specific functionality to the frontend:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// electron/preload.js.</span>
<span class="hljs-keyword">const</span> { contextBridge, ipcRenderer } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"electron"</span>);

contextBridge.exposeInMainWorld(<span class="hljs-string">"electronAPI"</span>, {
  <span class="hljs-attr">listFolders</span>: <span class="hljs-function">() =&gt;</span> ipcRenderer.invoke(<span class="hljs-string">"folders:list"</span>),
  <span class="hljs-attr">selectSourceFolder</span>: <span class="hljs-function">() =&gt;</span> ipcRenderer.invoke(<span class="hljs-string">"folders:selectSource"</span>),
  <span class="hljs-attr">createFolder</span>: <span class="hljs-function">(<span class="hljs-params">input</span>) =&gt;</span> ipcRenderer.invoke(<span class="hljs-string">"folders:create"</span>, input),
  <span class="hljs-attr">updateFolder</span>: <span class="hljs-function">(<span class="hljs-params">id, updates</span>) =&gt;</span>
    ipcRenderer.invoke(<span class="hljs-string">"folders:update"</span>, { id, updates }),
  <span class="hljs-attr">deleteFolder</span>: <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> ipcRenderer.invoke(<span class="hljs-string">"folders:delete"</span>, { id }),
  <span class="hljs-attr">openFolder</span>: <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> ipcRenderer.invoke(<span class="hljs-string">"folders:open"</span>, { id }),
});
</code></pre>
<h3 id="heading-using-it-in-react">Using It in React</h3>
<p>In your React components, you can now call these functions naturally:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleCreateFolder = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> created = <span class="hljs-keyword">await</span> <span class="hljs-built_in">window</span>.electronAPI.createFolder({
    label: <span class="hljs-string">"My Secret Files"</span>,
    sourcePath: <span class="hljs-string">"/Users/me/Documents/secret"</span>,
    conditions: {
      minAge: <span class="hljs-number">21</span>,
      gender: <span class="hljs-literal">null</span>,
      requireOfacClear: <span class="hljs-literal">true</span>
    }
  });

  setFolders(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> [...prev, created]);
};
</code></pre>
<p>This pattern keeps your Electron app secure while maintaining a clean, React-friendly API.</p>
<h2 id="heading-performance-considerations">Performance Considerations</h2>
<h3 id="heading-handling-large-folders">Handling Large Folders</h3>
<p>Encrypting thousands of files can take time. I implemented several optimizations:</p>
<p><strong>1. File Count Limiting</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> MAX_ENCRYPTED_FILES = <span class="hljs-number">5000</span>;
<span class="hljs-keyword">let</span> encryptedFileCount = <span class="hljs-number">0</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encryptFile</span>(<span class="hljs-params">sourcePath, targetPath, key</span>) </span>{
  <span class="hljs-keyword">if</span> (encryptedFileCount &gt;= MAX_ENCRYPTED_FILES) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Folder is too large to encrypt"</span>);
  }
  encryptedFileCount += <span class="hljs-number">1</span>;
  <span class="hljs-comment">// ... encryption logic.</span>
}
</code></pre>
<p><strong>2. Smart Directory Skipping</strong></p>
<p>Common large directories that don't need encryption are automatically skipped:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (entry.isDirectory() &amp;&amp; (
  entry.name === <span class="hljs-string">"node_modules"</span> ||
  entry.name === <span class="hljs-string">".git"</span> ||
  entry.name === <span class="hljs-string">".next"</span> ||
  entry.name === <span class="hljs-string">"dist"</span> ||
  entry.name === <span class="hljs-string">"build"</span>
)) {
  <span class="hljs-keyword">continue</span>;
}
</code></pre>
<p><strong>3. Async File Operations</strong></p>
<p>All file operations use Node's promise-based APIs for better performance:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fsPromises = fs.promises;

<span class="hljs-comment">// Instead of synchronous:</span>
<span class="hljs-comment">// fs.readFileSync(path).</span>

<span class="hljs-comment">// We use:</span>
<span class="hljs-keyword">await</span> fsPromises.readFile(path)
</code></pre>
<h2 id="heading-real-world-use-cases"><mark>Real-World Use Cases</mark></h2>
<p>Now that we understand how it works, let's talk about where this could actually be useful.</p>
<h3 id="heading-1-healthcare-records-management">1. Healthcare Records Management</h3>
<p>Imagine a medical practice that needs to ensure only adults can access certain medical files:</p>
<pre><code class="lang-typescript">{
  label: <span class="hljs-string">"Adult Patient Records"</span>,
  conditions: {
    minAge: <span class="hljs-number">18</span>,
    gender: <span class="hljs-literal">null</span>,
    requireOfacClear: <span class="hljs-literal">false</span>
  }
}
</code></pre>
<h3 id="heading-2-financial-compliance">2. Financial Compliance</h3>
<p>A financial advisor could use this to gate access to client files, ensuring anyone accessing them isn't on sanctions lists:</p>
<pre><code class="lang-typescript">{
  label: <span class="hljs-string">"Client Portfolio Files"</span>,
  conditions: {
    minAge: <span class="hljs-number">21</span>,
    gender: <span class="hljs-literal">null</span>,
    requireOfacClear: <span class="hljs-literal">true</span>  <span class="hljs-comment">// Must not be on OFAC list.</span>
  }
}
</code></pre>
<h3 id="heading-3-age-gated-content">3. Age-Gated Content</h3>
<p>Content creators could distribute files that self-enforce age restrictions:</p>
<pre><code class="lang-typescript">{
  label: <span class="hljs-string">"Age-Restricted Content"</span>,
  conditions: {
    minAge: <span class="hljs-number">21</span>,
    gender: <span class="hljs-literal">null</span>,
    requireOfacClear: <span class="hljs-literal">false</span>
  }
}
</code></pre>
<h3 id="heading-4-multi-user-shared-workstations">4. Multi-User Shared Workstations</h3>
<p>In environments where multiple people share computers, this ensures only authorized individuals can decrypt sensitive folders based on their verified identity attributes.</p>
<h3 id="heading-5-enterprise-nas-and-network-shares">5. Enterprise NAS and Network Shares</h3>
<p>This pattern extends beautifully to enterprise environments with Network-Attached Storage (NAS) devices:</p>
<pre><code class="lang-typescript">{
  label: <span class="hljs-string">"HR Confidential Documents"</span>,
  conditions: {
    minAge: <span class="hljs-number">21</span>,
    gender: <span class="hljs-literal">null</span>,
    requireOfacClear: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p><mark>Imagine a NAS device where encrypted folders are stored, but decryption only happens after Self verification. Multiple employees can have physical access to the network share, but only those who verify their identity attributes can decrypt specific folders.</mark> This works great for:</p>
<ul>
<li><p><strong>HR departments</strong> restricting access to employee records based on age verification</p>
</li>
<li><p><strong>Finance teams</strong> ensuring OFAC compliance before accessing client data</p>
</li>
<li><p><strong>Legal departments</strong> gating access to sensitive case files</p>
</li>
<li><p><strong>Research labs</strong> controlling access to confidential study data</p>
</li>
</ul>
<p>The same approach could integrate with Active Directory or LDAP systems, users authenticate with their domain credentials, but decryption requires additional Self verification. It's defense-in-depth with privacy preservation.</p>
<h2 id="heading-why-this-is-different">Why This Is Different?</h2>
<p>Let's look at how this stacks up against traditional solutions:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Approach</td><td>How It Works</td><td>Privacy</td><td>Identity Verification</td><td>Granular Rules</td></tr>
</thead>
<tbody>
<tr>
<td><strong>OS Encryption</strong> (FileVault, BitLocker)</td><td>Full disk encryption</td><td>Good</td><td>None (password only)</td><td>No</td></tr>
<tr>
<td><strong>Password-Protected Archives</strong></td><td>ZIP/RAR with password</td><td>Good</td><td>None</td><td>No</td></tr>
<tr>
<td><strong>Cloud Storage with 2FA</strong></td><td>Files on cloud with MFA</td><td>Poor (cloud provider sees all)</td><td>Basic (phone/email)</td><td>Limited</td></tr>
<tr>
<td><strong>Traditional Document Verification</strong></td><td>Upload ID copies</td><td>Terrible</td><td>Yes, but over-shares</td><td>Limited</td></tr>
<tr>
<td><strong>Self + Encryption (This App)</strong></td><td>Local encryption + ZK proofs</td><td>Excellent</td><td>Yes, privacy-preserving</td><td>Yes, per-folder</td></tr>
</tbody>
</table>
</div><h2 id="heading-building-with-self">Building with Self</h2>
<p>From a developer perspective, integrating Self was surprisingly straightforward.</p>
<h3 id="heading-frontend-integration">Frontend Integration</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { SelfAppBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/qrcode"</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> SelfAppBuilder({
  version: <span class="hljs-number">2</span>,
  appName: <span class="hljs-string">"My App"</span>,
  scope: <span class="hljs-string">"my-scope"</span>,
  endpoint: <span class="hljs-string">"https://my-server.com/verify"</span>,
  userId: userAddress,
  endpointType: <span class="hljs-string">"staging_https"</span>,
  disclosures: {
    minimumAge: <span class="hljs-number">18</span>,
    ofac: <span class="hljs-literal">true</span>,
  },
}).build();
</code></pre>
<h3 id="heading-backend-integration">Backend Integration</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { SelfBackendVerifier, InMemoryConfigStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/core"</span>;

<span class="hljs-keyword">const</span> configStore = <span class="hljs-keyword">new</span> InMemoryConfigStore();
<span class="hljs-keyword">const</span> verifier = <span class="hljs-keyword">new</span> SelfBackendVerifier(
  scope,
  endpoint,
  <span class="hljs-literal">true</span>,
  AllIds,
  configStore,
  <span class="hljs-string">"hex"</span>
);

app.post(<span class="hljs-string">"/verify"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> verifier.verify(
    req.body.attestationId,
    req.body.proof,
    req.body.publicSignals,
    req.body.userContextData
  );

  res.json({ <span class="hljs-attr">success</span>: result.isValidDetails.isValid });
});
</code></pre>
<p>That's it. Self handles all the complex cryptography, proof verification, and passport validation.</p>
<h2 id="heading-development-setup">Development Setup</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/613364f7ff5944caa34903314fc19c56">https://www.loom.com/share/613364f7ff5944caa34903314fc19c56</a></div>
<p> </p>
<p>Here's how to get this running on your machine:</p>
<h3 id="heading-1-install-dependencies">1. Install Dependencies</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Root workspace.</span>
npm install

<span class="hljs-comment"># Backend.</span>
<span class="hljs-built_in">cd</span> server
npm install

<span class="hljs-comment"># Frontend.</span>
<span class="hljs-built_in">cd</span> client
npm install
</code></pre>
<h3 id="heading-2-configure-environment-variables">2. Configure Environment Variables</h3>
<p><strong>Backend (.env):</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">PORT=3001</span>
<span class="hljs-string">SELF_SCOPE=your-scope-name</span>
<span class="hljs-string">SELF_ENDPOINT=https://your-ngrok-url.ngrok-free.app/api/verify</span>
</code></pre>
<p><strong>Frontend (.env.local):</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">NEXT_PUBLIC_SELF_APP_NAME=Self</span> <span class="hljs-string">Secure</span> <span class="hljs-string">Folders</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_SCOPE=your-scope-name</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_ENDPOINT=https://your-ngrok-url.ngrok-free.app/api/verify</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_DEBUG_ENDPOINT=http://localhost:3001/debug/last-result</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_CONFIG_ENDPOINT=http://localhost:3001/api/config</span>
</code></pre>
<h3 id="heading-3-start-everything">3. Start Everything</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Terminal 1: Backend.</span>
<span class="hljs-built_in">cd</span> server
npm run dev

<span class="hljs-comment"># Terminal 2: Expose backend via ngrok.</span>
ngrok http 3001

<span class="hljs-comment"># Terminal 3: Frontend.</span>
<span class="hljs-built_in">cd</span> client
npm run dev

<span class="hljs-comment"># Terminal 4: Electron.</span>
npm run dev:electron
</code></pre>
<h3 id="heading-4-use-the-self-mobile-app">4. Use the Self Mobile App</h3>
<p>Download the Self mobile app, create mock passports, and you're ready to test the verification flow.</p>
<h2 id="heading-the-bigger-picture">The Bigger Picture</h2>
<p>This proof of concept demonstrates something important: <strong>identity verification doesn't have to compromise privacy.</strong></p>
<p>We live in a world where showing your ID means revealing everything about yourself. Want to prove you're 21? Here's your full name, address, date of birth, ID number, and photo. It's absurd.</p>
<p>Zero-knowledge proofs change this paradigm entirely. You can prove:</p>
<ul>
<li><p>You're over 18 without revealing your birthdate</p>
</li>
<li><p>You're not on a sanctions list without showing your full identity</p>
</li>
<li><p>You're a resident of a country without showing your address</p>
</li>
<li><p>You're unique without revealing who you are</p>
</li>
</ul>
<p>When combined with strong encryption and native apps, this opens up entirely new possibilities:</p>
<p><strong>Compliance Without Surveillance:</strong> Companies can meet regulatory requirements (age verification, sanctions screening) without collecting personal data.</p>
<p><strong>Privacy-First Access Control:</strong> You can gate access to resources based on verified attributes while respecting user privacy.</p>
<p><strong>Decentralized Identity:</strong> Users maintain control of their identity data instead of fragmenting it across dozens of services.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Building this project opened my eyes to what's possible when we combine modern cryptography with user-friendly applications. The code is open source, the protocol is transparent, and the possibilities are endless. Whether you're building compliance tools, privacy applications, or just want to experiment with zero-knowledge proofs, I hope this walkthrough inspires you to explore what's possible.</p>
<p><strong>Feel free to fork the repo, build on it, break it, improve it. That's what POCs are for.</strong></p>
<p><em>This project is a proof of concept demonstrating Self protocol integration with Electron. It is not intended for production use without proper security hardening and auditing.</em></p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Creating a Privacy-First Site Blocker Using Self Protocol]]></title><description><![CDATA[You know that moment when you promise yourself "just five minutes" on Twitter, and suddenly an hour has vanished? Or when you're trying to focus, but Instagram is just right there?
I've always been fascinated by the idea of parental controls, not jus...]]></description><link>https://writer.mrmehta.in/creating-a-privacy-first-site-blocker-using-self-protocol</link><guid isPermaLink="true">https://writer.mrmehta.in/creating-a-privacy-first-site-blocker-using-self-protocol</guid><category><![CDATA[Self-Protocol]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[chrome extension]]></category><category><![CDATA[vite]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 21 Nov 2025 06:33:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763661116144/32f71a3f-ec65-45a9-89e3-f01c76b8da15.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You know that moment when you promise yourself "just five minutes" on Twitter, and suddenly an hour has vanished? Or when you're trying to focus, but Instagram is just <em>right there</em>?</p>
<p><mark>I've always been fascinated by the idea of parental controls, not just for kids, but for... well, ourselves.</mark> We all need a little digital discipline sometimes. But here's the thing, most website blockers are either too easy to bypass (just disable the extension, right?) or they're heavy-handed apps that feel like you're grounding yourself.</p>
<p>That's how <strong>Self Lock</strong> was born.</p>
<p><strong>Want to follow along?</strong> Check out the complete source code here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/self-lock-chrome-extension">https://github.com/kartikmehta8/self-lock-chrome-extension</a></div>
<p> </p>
<h2 id="heading-what-is-self-lock">What Is Self Lock?</h2>
<p><mark>Self Lock is a Chrome extension that puts your most distracting websites behind a verification wall powered by Self Protocol.</mark> Think of it as a digital safe for your attention, you can lock away sites like Twitter, Instagram, Reddit, or any website that pulls you away from what matters.</p>
<p>But here's where it gets interesting: to unlock these sites, you need to verify your identity using the Self mobile app. Once verified, you get a time-limited session (say, 20 minutes) where all protected sites are accessible. When the timer runs out, everything locks again automatically, <strong>even if the tabs are still open</strong>.</p>
<p>It's like having a responsible friend who taps you on the shoulder and says, "Hey, time's up."</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763663076204/03fd0d63-d127-4677-9b43-fff79a7273af.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-self-protocol">Why Self Protocol?</h2>
<p>Before we dive into the technical implementation, let me explain why Self Protocol was perfect for this project.</p>
<p><a target="_blank" href="https://self.xyz"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763663170738/c1cff71f-8606-496d-9971-e98d8aa8e867.png" alt class="image--center mx-auto" /></a></p>
<p>Self is a privacy-first identity verification protocol that uses <strong>zero-knowledge proofs</strong>. In simple terms, it lets you prove you're a real person who meets certain criteria (like being over 18) without revealing your actual passport data.</p>
<p>Here's how it works:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763663446460/a65220f1-1817-4e28-9f49-59bc01466a53.png" alt class="image--center mx-auto" /></p>
<p>In simple steps:</p>
<ol>
<li><p>You scan your passport using NFC on your phone</p>
</li>
<li><p>Self creates a cryptographic proof about your identity</p>
</li>
<li><p>You can selectively share information (like "I'm over 18" or "I'm not on the OFAC sanctions list")</p>
</li>
<li><p>Applications verify the proof without ever seeing your raw passport data</p>
</li>
</ol>
<h2 id="heading-the-vision">The Vision</h2>
<p>While I built this as a focus tool, <mark>the use cases are much broader</mark>:</p>
<ol>
<li><p><strong>For Parents</strong>: Create a safe browsing environment for kids. Lock adult content or time-wasting sites behind your verification. Kids can't bypass it without your phone and passport.</p>
</li>
<li><p><strong>For Focus</strong>: ADHD warriors and anyone who struggles with digital distractions can set boundaries. During work hours, social media stays locked unless you deliberately choose to break the seal.</p>
</li>
<li><p><strong>For Privacy Advocates</strong>: Demonstrate how Web3 identity solutions can work in everyday applications, not just crypto trading.</p>
</li>
<li><p><strong>For Developers</strong>: <strong>A complete reference implementation showing how to integrate Self Protocol into a real-world application with a clean UX.</strong></p>
</li>
</ol>
<p>This is just a <strong>proof-of-concept</strong>, the blocked sites are stored in local storage, not a database. In production, you'd want server-side management, family sharing features, and more. But it demonstrates the core concept beautifully.</p>
<h2 id="heading-how-i-built-it">How I Built It</h2>
<h3 id="heading-chrome-extension-basics">Chrome Extension Basics</h3>
<p>If you've never built a Chrome extension, the architecture might seem a bit mysterious at first. Chrome extensions have several moving parts:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763663860809/96d394d5-d92a-41f6-b125-ecbfa93d1067.png" alt class="image--center mx-auto" /></p>
<p>The key components:</p>
<ol>
<li><p><strong>Manifest file</strong> (<code>manifest.json</code>): The blueprint that tells Chrome what your extension can do</p>
</li>
<li><p><strong>Background script</strong>: A service worker that runs invisibly, managing state and logic</p>
</li>
<li><p><strong>Content scripts</strong>: JavaScript injected into web pages to interact with the DOM</p>
</li>
<li><p><strong>Popup UI</strong>: The interface users see when they click your extension icon</p>
</li>
</ol>
<p>For Self Lock, I went with <strong>Manifest V3</strong>, the latest standard that Chrome is pushing everyone toward.</p>
<p>Here's what the manifest looks like:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"manifest_version"</span>: <span class="hljs-number">3</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Self Lock"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.1.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Lock websites behind Self Protocol verification"</span>,
  <span class="hljs-attr">"permissions"</span>: [<span class="hljs-string">"storage"</span>, <span class="hljs-string">"tabs"</span>, <span class="hljs-string">"scripting"</span>],
  <span class="hljs-attr">"host_permissions"</span>: [<span class="hljs-string">"&lt;all_urls&gt;"</span>],
  <span class="hljs-attr">"background"</span>: {
    <span class="hljs-attr">"service_worker"</span>: <span class="hljs-string">"background.js"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"module"</span>
  },
  <span class="hljs-attr">"content_scripts"</span>: [{
    <span class="hljs-attr">"matches"</span>: [<span class="hljs-string">"&lt;all_urls&gt;"</span>],
    <span class="hljs-attr">"js"</span>: [<span class="hljs-string">"contentScript.js"</span>],
    <span class="hljs-attr">"run_at"</span>: <span class="hljs-string">"document_idle"</span>
  }],
  <span class="hljs-attr">"action"</span>: {
    <span class="hljs-attr">"default_popup"</span>: <span class="hljs-string">"index.html"</span>,
    <span class="hljs-attr">"default_icon"</span>: <span class="hljs-string">"self-icon.png"</span>
  }
}
</code></pre>
<p>The permissions are straightforward:</p>
<ul>
<li><p><strong>storage</strong>: To save which sites are protected and session data</p>
</li>
<li><p><strong>tabs</strong>: To monitor when you navigate to protected sites</p>
</li>
<li><p><strong>scripting</strong>: To inject the blur overlay on locked pages</p>
</li>
</ul>
<h3 id="heading-why-i-chose-vite">Why I Chose Vite</h3>
<p>Most Chrome extension tutorials use Webpack, but I went with <a target="_blank" href="https://vite.dev">Vite</a> because it's significantly faster and the config is much cleaner.</p>
<p>Here's the beauty of Vite for Chrome extensions, you can define multiple entry points (background script, content script, popup UI) and it bundles them all correctly:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// vite.config.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [react()],
  build: {
    outDir: <span class="hljs-string">'dist'</span>,
    rollupOptions: {
      input: {
        popup: resolve(__dirname, <span class="hljs-string">'index.html'</span>),
        background: resolve(__dirname, <span class="hljs-string">'src/background.ts'</span>),
        contentScript: resolve(__dirname, <span class="hljs-string">'src/contentScript.ts'</span>),
      },
      output: {
        entryFileNames: <span class="hljs-string">'[name].js'</span>,
        chunkFileNames: <span class="hljs-string">'assets/[name].js'</span>,
        assetFileNames: <span class="hljs-string">'assets/[name].[ext]'</span>
      }
    }
  }
});
</code></pre>
<p>When you run <code>npm run build</code>, Vite compiles everything to a <code>dist/</code> folder that's ready to load into Chrome. The whole build takes seconds, not minutes like Webpack.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763664356082/1f20802b-f01f-4960-98f0-3504be00fa29.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-part-1-the-background-service-worker">Part 1: The Background Service Worker</h3>
<p>The background script is the brain of the extension. It runs invisibly and manages all the core logic.</p>
<p>Here's what it does:</p>
<p><strong>1. Monitors tab updates</strong> to check if you're navigating to a protected site:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// background.ts.</span>
chrome.tabs.onUpdated.addListener(<span class="hljs-keyword">async</span> (tabId, changeInfo, tab) =&gt; {
  <span class="hljs-keyword">if</span> (changeInfo.status !== <span class="hljs-string">"complete"</span> || !tab.url) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">const</span> hostname = hostnameFromUrl(tab.url);
  <span class="hljs-keyword">const</span> { protectedSites, session } = <span class="hljs-keyword">await</span> getStorage();

  <span class="hljs-keyword">const</span> isProtected = protectedSites.includes(hostname);
  <span class="hljs-keyword">const</span> hasValidSession = isSessionValid(session);

  <span class="hljs-comment">// Decide: should this page be locked?</span>
  <span class="hljs-keyword">if</span> (isProtected &amp;&amp; !hasValidSession) {
    chrome.tabs.sendMessage(tabId, { <span class="hljs-keyword">type</span>: <span class="hljs-string">"LOCK_SITE"</span> });
  } <span class="hljs-keyword">else</span> {
    chrome.tabs.sendMessage(tabId, { <span class="hljs-keyword">type</span>: <span class="hljs-string">"UNLOCK_SITE"</span> });
  }
});
</code></pre>
<p><strong>2. Handles messages</strong> from the popup and content scripts:</p>
<pre><code class="lang-typescript">chrome.runtime.onMessage.addListener(<span class="hljs-function">(<span class="hljs-params">message, sender, sendResponse</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (message?.type === <span class="hljs-string">"SESSION_UPDATED"</span>) {
    <span class="hljs-comment">// Session changed, update all open tabs.</span>
    updateAllTabs();
  }

  <span class="hljs-keyword">if</span> (message?.type === <span class="hljs-string">"GET_STATUS_FOR_URL"</span>) {
    <span class="hljs-comment">// Popup asking: "Is this site locked?"</span>
    <span class="hljs-keyword">const</span> { hostname } = message;
    <span class="hljs-keyword">const</span> isLocked = <span class="hljs-comment">/* check logic */</span>;
    sendResponse({ locked: isLocked });
  }
});
</code></pre>
<p><strong>3. Stores data</strong> using Chrome's storage API:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// storage.ts.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStorage</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">ExtensionStorage</span>&gt; </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> chrome.storage.local.get(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">return</span> {
    protectedSites: data.protectedSites || [],
    session: data.session || <span class="hljs-literal">null</span>,
    sessionDurationMinutes: data.sessionDurationMinutes || <span class="hljs-number">20</span>
  };
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setStorage</span>(<span class="hljs-params">updates: Partial&lt;ExtensionStorage&gt;</span>) </span>{
  <span class="hljs-keyword">await</span> chrome.storage.local.set(updates);
}
</code></pre>
<p>The storage schema is simple:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> ExtensionStorage = {
  protectedSites: <span class="hljs-built_in">string</span>[];           <span class="hljs-comment">// ["twitter.com", "instagram.com"]</span>
  session: {
    verified: <span class="hljs-built_in">boolean</span>;
    expiresAt: <span class="hljs-built_in">number</span>;                <span class="hljs-comment">// Unix timestamp.</span>
    verificationSummary: <span class="hljs-built_in">string</span>;      <span class="hljs-comment">// "Nationality US · Age ≥ 18".</span>
  } | <span class="hljs-literal">null</span>;
  sessionDurationMinutes: <span class="hljs-built_in">number</span>;     <span class="hljs-comment">// Default: 20.</span>
};
</code></pre>
<p><strong>Storage Architecture:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763664932788/f7083eb3-c545-4597-a363-44f3a7a20e84.png" alt class="image--center mx-auto" /></p>
<p>Everything lives in <code>chrome.storage.local</code>, which persists across browser restarts. This means if you set up protected sites and close Chrome, they're still protected when you reopen.</p>
<h3 id="heading-part-2-the-content-script">Part 2: The Content Script</h3>
<p>The content script runs on <em>every page</em> you visit. Its job is simple: show or hide the blur overlay based on messages from the background script.</p>
<p>Here's the overlay creation logic:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// contentScript.ts.</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createOverlay</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"self-lock-overlay"</span>)) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">const</span> overlay = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
  overlay.id = <span class="hljs-string">"self-lock-overlay"</span>;
  overlay.style.cssText = <span class="hljs-string">`
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: linear-gradient(to bottom, rgba(15,23,42,0.95), rgba(30,41,59,0.98));
    backdrop-filter: blur(14px);
    z-index: 2147483647;
    display: flex;
    align-items: center;
    justify-content: center;
  `</span>;

  <span class="hljs-keyword">const</span> card = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
  card.innerHTML = <span class="hljs-string">`
    &lt;div style="background: rgba(30,41,59,0.8); padding: 2rem; border-radius: 1rem; max-width: 500px; text-align: center;"&gt;
      &lt;h1 style="font-size: 1.5rem; font-weight: bold; color: white; margin-bottom: 1rem;"&gt;
        🔒 Self Lock Enabled
      &lt;/h1&gt;
      &lt;p style="color: #94a3b8; margin-bottom: 1.5rem;"&gt;
        This website is protected. Open the Self Lock extension and verify your identity to access it.
      &lt;/p&gt;
      &lt;span style="background: #1e293b; color: #60a5fa; padding: 0.5rem 1rem; border-radius: 0.5rem; font-size: 0.875rem;"&gt;
        Protected Site
      &lt;/span&gt;
    &lt;/div&gt;
  `</span>;

  overlay.appendChild(card);
  <span class="hljs-built_in">document</span>.body.appendChild(overlay);
}
</code></pre>
<p>The beauty of using inline styles is avoiding CSS conflicts with the page you're blocking.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763665038316/6fd905a8-7ea6-47a6-b1e3-30e166b5a28a.png" alt class="image--center mx-auto" /></p>
<p><strong>But here's the cool part</strong>: When the session expires, the content script automatically re-locks the page <em>without a refresh</em>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setupExpiryTimer</span>(<span class="hljs-params">session: SelfSession</span>) </span>{
  <span class="hljs-keyword">const</span> remaining = session.expiresAt - <span class="hljs-built_in">Date</span>.now();

  expiryTimeoutId = <span class="hljs-built_in">window</span>.setTimeout(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// Time's up! Re-check session validity.</span>
    <span class="hljs-keyword">const</span> { session: latestSession } = <span class="hljs-keyword">await</span> getStorage();

    <span class="hljs-keyword">if</span> (!isSessionValid(latestSession)) {
      createOverlay(); <span class="hljs-comment">// Lock the page again.</span>
    }
  }, remaining + <span class="hljs-number">500</span>); <span class="hljs-comment">// 500ms buffer.</span>
}
</code></pre>
<p><mark>Imagine you're scrolling Twitter, your 20-minute timer expires, and </mark> <em><mark>boom</mark></em><mark>, the page blurs right there. No navigation needed. That's the magic of content scripts with timers.</mark></p>
<h3 id="heading-part-3-the-popup-ui">Part 3: The Popup UI</h3>
<p>For the popup interface, I wanted something modern and clean. The popup has three main sections:</p>
<h4 id="heading-1-current-site-card">1. Current Site Card</h4>
<p>Shows the site you're on and lets you quickly protect/unprotect it:</p>
<pre><code class="lang-tsx">// App.tsx.
const isCurrentSiteProtected = state.protectedSites.includes(state.activeHost || '');

&lt;div className="bg-slate-800 rounded-lg p-4"&gt;
  &lt;h3 className="text-sm font-semibold text-slate-400 mb-2"&gt;Current site&lt;/h3&gt;
  &lt;div className="flex items-center justify-between"&gt;
    &lt;span className="text-white font-mono"&gt;{state.activeHost || 'Unknown'}&lt;/span&gt;
    &lt;button
      onClick={toggleProtectionForCurrent}
      className={isCurrentSiteProtected ? 'bg-blue-500' : 'bg-slate-700'}
    &gt;
      {isCurrentSiteProtected ? 'Protected' : 'Not protected'}
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763703607491/f231c6d7-4492-4fa6-8ffb-261f540e40f7.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-2-session-card">2. Session Card</h4>
<p>This is where the Self Protocol integration happens:</p>
<pre><code class="lang-tsx">const app = new SelfAppBuilder({
  version: 2,
  appName: "Self Lock",
  scope: "demo-scope",
  endpoint: "https://your-server.ngrok.io/api/verify",
  userId: ethers.ZeroAddress,
  endpointType: "staging_https",
  disclosures: {
    minimumAge: 18,
    ofac: true,
    nationality: true,
    gender: true
  }
}).build();

&lt;SelfQRcodeWrapper
  selfApp={app}
  onSuccess={handleSuccessfulVerification}
  onError={handleError}
  type="websocket"
  size={140}
/&gt;
</code></pre>
<p>The <code>SelfQRcodeWrapper</code> component from <code>@selfxyz/qrcode</code> does all the heavy lifting:</p>
<ul>
<li><p>Generates a QR code with a universal link</p>
</li>
<li><p>Manages WebSocket connection for real-time verification</p>
</li>
<li><p>Calls your callback when verification succeeds</p>
</li>
</ul>
<p>When verification succeeds, I fetch the details and create a session:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSuccessfulVerification = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// Fetch verification details from backend.</span>
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'http://localhost:3001/debug/last-result'</span>);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();

  <span class="hljs-comment">// Extract nationality and age.</span>
  <span class="hljs-keyword">const</span> nationality = data.result?.nationality || <span class="hljs-string">'Unknown'</span>;
  <span class="hljs-keyword">const</span> minimumAge = data.result?.minimumAge || <span class="hljs-number">18</span>;

  <span class="hljs-comment">// Create session.</span>
  <span class="hljs-keyword">const</span> newSession: SelfSession = {
    verified: <span class="hljs-literal">true</span>,
    expiresAt: <span class="hljs-built_in">Date</span>.now() + sessionDurationMinutes * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,
    verificationSummary: <span class="hljs-string">`Nationality <span class="hljs-subst">${nationality}</span> · Age ≥ <span class="hljs-subst">${minimumAge}</span>`</span>
  };

  <span class="hljs-keyword">await</span> setStorage({ session: newSession });
  chrome.runtime.sendMessage({ <span class="hljs-keyword">type</span>: <span class="hljs-string">"SESSION_UPDATED"</span> });

  setState(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> ({ ...prev, session: newSession, viewState: <span class="hljs-string">'verified'</span> }));
};
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763703479420/e7faa069-e4f5-4734-b7eb-e477ccad1f87.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-3-protected-sites-manager">3. Protected Sites Manager</h4>
<p>A simple list with add/remove functionality:</p>
<pre><code class="lang-tsx">&lt;div className="bg-slate-800 rounded-lg p-4"&gt;
  &lt;h3 className="text-sm font-semibold text-slate-400 mb-3"&gt;Protected sites&lt;/h3&gt;

  &lt;input
    type="text"
    placeholder="twitter.com"
    value={newSite}
    onChange={(e) =&gt; setNewSite(e.target.value)}
    className="w-full bg-slate-700 text-white rounded px-3 py-2 mb-3"
  /&gt;
  &lt;button onClick={handleAddSite} className="w-full bg-blue-500 text-white rounded py-2"&gt;
    Add Site
  &lt;/button&gt;

  &lt;ul className="mt-3 space-y-2"&gt;
    {state.protectedSites.map(site =&gt; (
      &lt;li key={site} className="flex items-center justify-between"&gt;
        &lt;span className="text-white font-mono"&gt;{site}&lt;/span&gt;
        &lt;button onClick={() =&gt; handleRemoveSite(site)} className="text-red-400"&gt;
          Remove
        &lt;/button&gt;
      &lt;/li&gt;
    ))}
  &lt;/ul&gt;
&lt;/div&gt;
</code></pre>
<p>The UI stays minimal and functional. No clutter, just what you need.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763703666265/614f93ca-ef32-47ed-9db4-60647e00b36f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-part-4-the-backend-verifier">Part 4: The Backend Verifier</h3>
<p>The extension needs a backend to verify Self proofs. I built a simple Express server:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/index.mjs.</span>
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> { SelfBackendVerifier, AllIds } <span class="hljs-keyword">from</span> <span class="hljs-string">'@selfxyz/core'</span>;

<span class="hljs-keyword">const</span> app = express();
app.use(express.json());

<span class="hljs-keyword">const</span> selfBackendVerifier = <span class="hljs-keyword">new</span> SelfBackendVerifier(
  <span class="hljs-string">"demo-scope"</span>,
  <span class="hljs-string">"https://your-ngrok-url.ngrok.io/api/verify"</span>,
  <span class="hljs-literal">true</span>, <span class="hljs-comment">// mockPassport for staging.</span>
  AllIds,
  {
    <span class="hljs-attr">minimumAge</span>: <span class="hljs-number">18</span>,
    <span class="hljs-attr">excludedCountries</span>: [],
    <span class="hljs-attr">ofac</span>: <span class="hljs-literal">true</span>
  },
  <span class="hljs-string">"hex"</span>
);

<span class="hljs-keyword">let</span> lastVerificationResult = <span class="hljs-literal">null</span>;

app.post(<span class="hljs-string">'/api/verify'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { attestationId, proof, publicSignals, userContextData } = req.body;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verifyCredentials(
      attestationId,
      proof,
      publicSignals,
      userContextData
    );

    lastVerificationResult = result;

    <span class="hljs-keyword">const</span> { isValid, isMinimumAgeValid, isOfacValid } = result.isValidDetails;

    <span class="hljs-keyword">if</span> (!isValid || !isMinimumAgeValid || isOfacValid) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
        <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>,
        <span class="hljs-attr">result</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">reason</span>: !isValid ? <span class="hljs-string">'Invalid proof'</span> :
                !isMinimumAgeValid ? <span class="hljs-string">'Under 18'</span> :
                <span class="hljs-string">'OFAC sanctions list'</span>
      });
    }

    res.json({ <span class="hljs-attr">status</span>: <span class="hljs-string">'success'</span>, <span class="hljs-attr">result</span>: <span class="hljs-literal">true</span> });
  } <span class="hljs-keyword">catch</span> (error) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">message</span>: error.message });
  }
});

app.get(<span class="hljs-string">'/debug/last-result'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.json(lastVerificationResult);
});

app.listen(<span class="hljs-number">3001</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server running on port 3001'</span>));
</code></pre>
<p><strong>Important</strong>: <mark>The verification endpoint needs to be publicly accessible for the Self app to reach it.</mark> I used <a target="_blank" href="https://ngrok.com">Ngrok</a> for development:</p>
<pre><code class="lang-bash">ngrok http 3001
<span class="hljs-comment"># Outputs: https://abc123.ngrok-free.app.</span>
</code></pre>
<p>Then update both <code>.env</code> files to use this URL.</p>
<p><strong>The complete verification flow:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763703781452/9a7dedeb-ff62-48e9-b5d2-8babf89dc311.png" alt class="image--center mx-auto" /></p>
<p><strong>Flow breakdown:</strong></p>
<ol>
<li><p>User scans QR code with Self app</p>
</li>
<li><p>Self app sends cryptographic proof to <code>https://abc123.ngrok-free.app/api/verify</code></p>
</li>
<li><p>Backend verifies proof using Self SDK</p>
</li>
<li><p>Backend stores result in memory (for debug endpoint)</p>
</li>
<li><p>Extension polls <code>/debug/last-result</code> to get verification summary</p>
</li>
<li><p>Extension creates session and unlocks sites</p>
</li>
</ol>
<p><strong>In production, you'd want a proper database and webhook-based notifications instead of polling.</strong></p>
<h3 id="heading-the-message-passing-architecture">The Message Passing Architecture</h3>
<p>One of the trickiest parts of building Chrome extensions is getting all the pieces to talk to each other. Here's how messages flow in Self Lock:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763704009598/903b5391-4db8-44f9-9648-964cce9d6a95.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-use-cases">Use Cases</h2>
<h3 id="heading-1-parental-control">1. Parental Control</h3>
<p><mark>Sarah is a parent with two teenagers who share a family computer. She uses Self Lock to protect adult content sites and time-wasting platforms.</mark> Her kids can browse normally, but when they hit a protected site, they need Sarah to scan her passport with the Self app on her phone.</p>
<p>This is way better than:</p>
<ul>
<li><p>Remembering passwords (kids might see you type it)</p>
</li>
<li><p>Using "parental control" software (often buggy and intrusive)</p>
</li>
<li><p>Blocking sites at router level (too broad, affects everyone)</p>
</li>
</ul>
<p>With Self Lock, Sarah can unlock all sites for 30 minutes if a kid needs access for legitimate research, then everything auto-locks again.</p>
<h3 id="heading-2-adhd-focus-tool">2. ADHD Focus Tool</h3>
<p><mark>Marcus has ADHD and works from home. He blocks Twitter, Reddit, YouTube, and news sites during work hours.</mark> When he's tempted to check Twitter "just for a second," he has to:</p>
<ol>
<li><p>Open the Self Lock extension</p>
</li>
<li><p>Pull out his phone</p>
</li>
<li><p>Open the Self app</p>
</li>
<li><p>Scan his passport with NFC</p>
</li>
</ol>
<p>By the time he'd complete these steps, the urge usually passes. And if he <em>does</em> unlock it, the 20-minute timer ensures he can't stay there all day. When time's up, the page blurs, even if he has 10 tabs open.</p>
<p>Marcus says the physical friction is what makes it work. "A password I could type without thinking. This? This makes me stop and ask: 'Do I really want to do this?'"</p>
<h3 id="heading-3-privacy-conscious-users">3. Privacy-Conscious Users</h3>
<p><mark>Emma is a privacy advocate who wants to demonstrate Web3 identity solutions. She uses Self Lock as a conversation starter:</mark> "Check this out, I can prove I'm over 18 and not on a sanctions list without showing you my passport."</p>
<p>She's even thinking of forking the project to add features like:</p>
<ul>
<li><p><strong>Shared family accounts</strong>: Multiple people can unlock with their own Self verification</p>
</li>
<li><p><strong>Time-of-day rules</strong>: Auto-protect sites during work hours</p>
</li>
<li><p><strong>Usage analytics</strong>: How much time you spend on unlocked sites</p>
</li>
<li><p><strong>Remote management</strong>: Parents can manage kids' settings from a web dashboard</p>
</li>
</ul>
<h2 id="heading-advantages-of-building-with-self-protocol">Advantages of Building with Self Protocol</h2>
<p>After building this project, I'm convinced Self Protocol is underrated for consumer apps. Here's why:</p>
<h3 id="heading-1-privacy-by-design">1. Privacy by Design</h3>
<p>Traditional identity systems require you to upload documents, take selfies, or connect social accounts. Self uses zero-knowledge proofs, your data never leaves your phone. The extension only sees:</p>
<pre><code class="lang-yaml">{
  <span class="hljs-attr">verified:</span> <span class="hljs-literal">true</span>,
  <span class="hljs-attr">nationality:</span> <span class="hljs-string">"US"</span>,
  <span class="hljs-attr">ageOver18:</span> <span class="hljs-literal">true</span>,
  <span class="hljs-attr">ofacClear:</span> <span class="hljs-literal">true</span>
}
</code></pre>
<p>No names, no passport numbers, no photos.</p>
<h3 id="heading-2-sybil-resistance">2. Sybil Resistance</h3>
<p>Because Self uses NFC passport scanning, it's extremely difficult to create fake identities. One passport = one identity. This makes Self Lock useful for:</p>
<ul>
<li><p><strong>Fair access systems</strong>: Ensure one person = one unlock session</p>
</li>
<li><p><strong>Anti-bot protection</strong>: Prove you're human without CAPTCHA</p>
</li>
<li><p><strong>Compliance</strong>: Meet age verification requirements for adult content</p>
</li>
</ul>
<h3 id="heading-3-developer-experience">3. Developer Experience</h3>
<p>The Self SDK is remarkably easy to use. From zero to working QR code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { SelfAppBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">'@selfxyz/qrcode'</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> SelfAppBuilder({
  appName: <span class="hljs-string">"My App"</span>,
  scope: <span class="hljs-string">"my-scope"</span>,
  endpoint: <span class="hljs-string">"https://my-server.com/verify"</span>,
  disclosures: { minimumAge: <span class="hljs-number">18</span> }
}).build();

&lt;SelfQRcodeWrapper selfApp={app} onSuccess={handleSuccess} /&gt;
</code></pre>
<p>That's it. The SDK handles universal links, WebSocket connections, proof formats, all abstracted away.</p>
<h3 id="heading-4-future-proof">4. Future-Proof</h3>
<p>As Web3 identity solutions mature, Self is positioned as a credible player. Self Protocol is:</p>
<ul>
<li><p><strong>Open source</strong>: Auditable code, community-driven</p>
</li>
<li><p><strong>Interoperable</strong>: Works with Ethereum, Celo, and other chains</p>
</li>
<li><p><strong>Standards-compliant</strong>: Follows W3C DID standards</p>
</li>
</ul>
<p>Building with Self now means you're ready for a future where decentralized identity is the norm.</p>
<h2 id="heading-the-poc-nature">The POC Nature</h2>
<p>You might notice this project stores everything in local storage, no database, no server-side state management. This is intentional.</p>
<p><strong>Self Lock is a proof-of-concept</strong> designed to demonstrate:</p>
<ol>
<li><p><strong>How Self Protocol can be integrated into a browser extension</strong></p>
</li>
<li><p>How cryptographic identity verification works in practice</p>
</li>
<li><p>That privacy-preserving parental controls are technically feasible</p>
</li>
</ol>
<p>For a production app, you'd want:</p>
<ul>
<li><p><strong>Database for protected sites</strong>: Allow family sharing, remote management</p>
</li>
<li><p><strong>User accounts</strong>: Multiple people in a household, each with their own Self identity</p>
</li>
<li><p><strong>Webhook-based verification</strong>: Instead of polling <code>/debug/last-result</code>, use webhooks</p>
</li>
<li><p><strong>Cloud sync</strong>: Settings sync across devices</p>
</li>
<li><p><strong>Scheduled rules</strong>: "Block Twitter Monday-Friday 9am-5pm"</p>
</li>
<li><p><strong>Usage reports</strong>: Server-side analytics on unlock frequency and duration</p>
</li>
</ul>
<p>But all of that adds complexity. For showing "here's how Self can power a real-world app," local storage is perfect. It keeps the codebase clean and focused on the core concept.</p>
<h2 id="heading-how-to-use-self-lock">How to Use Self Lock?</h2>
<p>Ready to try it? Here's the full setup:</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p><strong>Node.js 18+</strong> (Self SDK recommends Node 22)</p>
</li>
<li><p><strong>Chrome browser</strong> (or any Chromium-based browser)</p>
</li>
<li><p><strong>Self mobile app</strong> (iOS/Android, available in app stores)</p>
</li>
<li><p><strong>ngrok</strong> (free account for public URL)</p>
</li>
</ul>
<h3 id="heading-step-1-clone-the-repository">Step 1: Clone the Repository</h3>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/kartikmehta8/self-lock-chrome-extension.git
<span class="hljs-built_in">cd</span> self-lock-chrome-extension
</code></pre>
<h3 id="heading-step-2-set-up-the-backend">Step 2: Set Up the Backend</h3>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> server
npm install
</code></pre>
<p>Create <code>server/.env</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-string">PORT=3001</span>
<span class="hljs-string">SELF_SCOPE=demo-scope</span>
<span class="hljs-string">SELF_ENDPOINT=https://YOUR_NGROK_URL.ngrok-free.app/api/verify</span>
</code></pre>
<p>Start the backend:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>In a separate terminal, expose it with ngrok:</p>
<pre><code class="lang-bash">ngrok http 3001
</code></pre>
<p>Copy the ngrok URL (e.g., <code>https://abc123.ngrok-free.app</code>) and update <code>SELF_ENDPOINT</code> in <code>server/.env</code>.</p>
<h3 id="heading-step-3-set-up-the-extension">Step 3: Set Up the Extension</h3>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> chrome-extension
npm install
</code></pre>
<p>Create <code>chrome-extension/.env</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-string">VITE_SELF_APP_NAME=Self</span> <span class="hljs-string">Lock</span>
<span class="hljs-string">VITE_SELF_SCOPE=demo-scope</span>
<span class="hljs-string">VITE_SELF_ENDPOINT=https://abc123.ngrok-free.app/api/verify</span>
<span class="hljs-string">VITE_SELF_DEBUG_ENDPOINT=http://localhost:3001/debug/last-result</span>
<span class="hljs-string">VITE_SESSION_MINUTES=20</span>
</code></pre>
<p><strong>Important</strong>: <code>VITE_SELF_SCOPE</code> and <code>VITE_SELF_ENDPOINT</code> must match the backend's values.</p>
<p>Build the extension:</p>
<pre><code class="lang-bash">npm run build
</code></pre>
<h3 id="heading-step-4-load-extension-in-chrome">Step 4: Load Extension in Chrome</h3>
<ol>
<li><p>Open Chrome and navigate to <code>chrome://extensions</code></p>
</li>
<li><p>Enable <strong>Developer mode</strong> (toggle in top-right corner)</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763704823132/6b236252-8917-49b1-b3cf-fba9872ee4ef.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Load unpacked</strong></p>
</li>
<li><p>Select the <code>chrome-extension/dist</code> folder</p>
</li>
</ol>
<p>You should see "Self Lock" appear with the Self icon.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763704877341/dfb23048-f5c2-4340-9ede-64fd6f70f485.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p><mark>Self Lock shows that privacy and security don't have to be enemies of good UX.</mark> With the right tools (Self Protocol, modern build systems, thoughtful design), we can create apps that:</p>
<ul>
<li><p>Respect user privacy</p>
</li>
<li><p>Provide real value</p>
</li>
<li><p>Feel delightful to use</p>
</li>
</ul>
<p>That's the kind of software I want to build. I hope this project inspires you to build something cool too.</p>
<p>If you build something with Self Protocol, I'd love to hear about it. And if you fork Self Lock and add features, send a PR! Let's build the privacy-respecting web together.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[How I Fixed Discord's Age Verification Problem]]></title><description><![CDATA[You know that feeling when you're trying to join an 18+ Discord server, and you have to send a photo of your ID to some random moderator? Yeah, that never sat right with me either.
Discord has this native age verification system, but it's... broken. ...]]></description><link>https://writer.mrmehta.in/how-i-fixed-discords-age-verification-problem</link><guid isPermaLink="true">https://writer.mrmehta.in/how-i-fixed-discords-age-verification-problem</guid><category><![CDATA[discord]]></category><category><![CDATA[Self-Protocol]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Wed, 19 Nov 2025 20:11:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763577021911/ef2fdb43-3c4d-43ed-81d4-28a7466b77d3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><mark>You know that feeling when you're trying to join an 18+ Discord server, and you have to send a photo of your ID to some random moderator? Yeah, that never sat right with me either.</mark></p>
<p>Discord has this native age verification system, but it's... broken. Not technically broken — it works. But here's the catch: when you verify your age through Discord's official flow, your personal data gets sent to Discord's servers, then shared with the server owner. Your real name, birthday, maybe even your address depending on what ID you use. All that just to prove you're over 18 and access some meme channels.</p>
<p>That seemed ridiculous to me. <strong>We have zero-knowledge proofs, we have privacy-preserving cryptography, we have all this amazing tech, and we're still doing age verification like it's 2005?</strong></p>
<p>So I built something better.</p>
<blockquote>
<p><strong><em>Want to follow along? All the code from this article is available on</em></strong> <a target="_blank" href="https://github.com/kartikmehta8/self-discord-age-gated-content"><strong><em>GitHub</em></strong></a><strong><em>. Feel free to clone, experiment, and build upon it!</em></strong></p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/self-discord-age-gated-content">https://github.com/kartikmehta8/self-discord-age-gated-content</a></div>
<p> </p>
<h2 id="heading-the-problem">The Problem</h2>
<p>You're trying to join a gaming Discord with age-restricted channels. The server admins want to make sure everyone's 18+. Fair enough. But here's what happens:</p>
<ol>
<li><p>You submit your government ID to Discord</p>
</li>
<li><p>Discord processes it and confirms your age</p>
</li>
<li><p><strong>Your actual personal information gets shared</strong></p>
</li>
<li><p>That data sits somewhere in their databases forever</p>
</li>
<li><p>You have zero control over what they do with it</p>
</li>
</ol>
<p>That's a lot of personal data floating around in a lot of places. And honestly? Most Discord server owners don't want that responsibility either. They just want to know you're 18+, they don't need your full name and birth date.</p>
<p><mark>This isn't just a privacy issue. It's a liability issue. It's a trust issue.</mark></p>
<h2 id="heading-the-aha-moment">The "Aha!" Moment</h2>
<p>I was reading about <a target="_blank" href="https://self.xyz">Self Protocol</a> one evening (as one does when they're deep in the crypto rabbit hole), and it hit me: <strong>what if we could prove we're over 18 without actually revealing our age?</strong></p>
<p>Zero-knowledge proofs let you prove a statement is true without revealing why it's true. In this case: "I am over 18 years old" without saying "I was born on [specific date]".</p>
<p>The Self Protocol takes this further. It lets you scan your actual passport with NFC, generate a cryptographic proof of whatever claims you want to make (age, nationality, etc.), and share that proof without ever revealing the underlying data.</p>
<p><a target="_blank" href="https://self.xyz"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763577794720/3413a950-6abe-4a06-b2ab-4a6f737e77b0.png" alt class="image--center mx-auto" /></a></p>
<p><em>So the server gets:</em> "This person is verified to be 18+, from an allowed country, and not on any sanctions lists."</p>
<p><em>What the server doesn't get:</em> Your name, birth date, passport number, or anything else.</p>
<p>Perfect.</p>
<h2 id="heading-the-architecture">The Architecture</h2>
<p>Let me walk you through how I built this thing. <strong>Fair warning:</strong> we're going to get technical, but I promise it'll make sense.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763577922357/398cd7c1-3349-4c4b-b667-120e7b119bc8.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-building-this-thing-step-by-step">Building This Thing Step by Step</h2>
<h3 id="heading-step-1-setting-up-the-self-backend-verifier">Step 1: Setting Up the Self Backend Verifier</h3>
<p>First things first: I needed to configure the Self Protocol backend to verify proofs. This is where the magic happens—or at least, where we set up the rules for what "verified" means.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/selfVerifier.mjs.</span>
<span class="hljs-keyword">import</span> { SelfBackendVerifier, AllIds, DefaultConfigStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/core"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selfBackendVerifier = <span class="hljs-keyword">new</span> SelfBackendVerifier(
  SELF_SCOPE,           <span class="hljs-comment">// "demo-scope" - identifies this verification type.</span>
  SELF_ENDPOINT,        <span class="hljs-comment">// Our public HTTPS endpoint.</span>
  <span class="hljs-literal">true</span>,                 <span class="hljs-comment">// Use staging/mock passports for testing.</span>
  AllIds,              <span class="hljs-comment">// Accept all credential types.</span>
  <span class="hljs-keyword">new</span> DefaultConfigStore({
    <span class="hljs-attr">minimumAge</span>: <span class="hljs-number">18</span>,             <span class="hljs-comment">// The golden number.</span>
    <span class="hljs-attr">excludedCountries</span>: [],      <span class="hljs-comment">// No country restrictions.</span>
    <span class="hljs-attr">ofac</span>: <span class="hljs-literal">true</span>,                 <span class="hljs-comment">// Check OFAC sanctions lists.</span>
  }),
  <span class="hljs-string">"hex"</span>                <span class="hljs-comment">// User ID format.</span>
);
</code></pre>
<p><strong>Let me break down what's happening here:</strong></p>
<ul>
<li><p><strong>SELF_SCOPE</strong>: Think of this as a namespace. It's how the Self app knows this verification request is for our Discord bot and not some other application.</p>
</li>
<li><p><strong>SELF_ENDPOINT</strong>: This is where the Self app will send the proof after the user completes verification. It has to be HTTPS and publicly accessible (<strong>no localhost</strong>). This is why we need ngrok.</p>
</li>
<li><p><strong>minimumAge: 18</strong>: This is the requirement. The Self Protocol will generate a proof that checks if <code>user_age &gt;= 18</code> without revealing the actual age.</p>
</li>
<li><p><strong>ofac: true</strong>: This checks the user against the <a target="_blank" href="https://ofac.treasury.gov/">Office of Foreign Assets Control</a> sanctions list. Because even in crypto, we have to play by some rules.</p>
</li>
<li><p><strong>"hex"</strong>: This tells the verifier we're using hexadecimal format for user IDs. This becomes important when we link Discord user IDs to verification proofs.</p>
</li>
</ul>
<p>The beauty here is that I'm using Self's staging environment with <strong>mock passports</strong>. This means during development and testing, users don't need real passports, they can create test credentials. Perfect for demos and development.</p>
<h3 id="heading-step-2-building-the-discord-bot">Step 2: Building the Discord Bot</h3>
<p>Now comes the fun part: making Discord actually talk to our verification system.</p>
<h4 id="heading-initialising-the-bot">Initialising the Bot</h4>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/discordBot.mjs.</span>
<span class="hljs-keyword">import</span> { Client, GatewayIntentBits, Partials, SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
  <span class="hljs-attr">intents</span>: [
    GatewayIntentBits.Guilds,           <span class="hljs-comment">// See servers.</span>
    GatewayIntentBits.GuildMembers,     <span class="hljs-comment">// See member info.</span>
    GatewayIntentBits.GuildMessages,    <span class="hljs-comment">// Read messages.</span>
    GatewayIntentBits.DirectMessages,   <span class="hljs-comment">// Send DMs.</span>
    GatewayIntentBits.MessageContent,   <span class="hljs-comment">// Read message content.</span>
  ],
  <span class="hljs-attr">partials</span>: [Partials.Channel],         <span class="hljs-comment">// Needed for DMs.</span>
});
</code></pre>
<p>Discord's bot permissions system is... particular. You need to explicitly request each "intent" (permission). <em>I need guild access to see the server, member access to assign roles, message access to respond to commands, and DM access to send the QR codes privately.</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763578465639/de7d1fb2-c000-4e8e-8576-86626c135f9e.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-the-verify-command">The <code>/verify</code> Command</h4>
<p>This is where users start their journey. They type <code>/verify</code> in any channel, and the bot springs into action.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleVerifyCommand</span>(<span class="hljs-params">interaction</span>) </span>{
  <span class="hljs-keyword">const</span> { user, guild } = interaction;

  <span class="hljs-comment">// Make sure they're in a server (not DMing the bot).</span>
  <span class="hljs-keyword">if</span> (!guild) {
    <span class="hljs-keyword">await</span> interaction.reply({
      <span class="hljs-attr">content</span>: <span class="hljs-string">"This command can only be used inside a server."</span>,
      <span class="hljs-attr">flags</span>: MessageFlags.Ephemeral,
    });
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// Check if they're already verified.</span>
  <span class="hljs-keyword">const</span> member = <span class="hljs-keyword">await</span> guild.members.fetch(user.id);
  <span class="hljs-keyword">if</span> (DISCORD_VERIFIED_ROLE_ID &amp;&amp; member.roles.cache.has(DISCORD_VERIFIED_ROLE_ID)) {
    <span class="hljs-keyword">await</span> interaction.reply({
      <span class="hljs-attr">content</span>: <span class="hljs-string">"You are already verified and should see the restricted channels."</span>,
      <span class="hljs-attr">flags</span>: MessageFlags.Ephemeral,
    });
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// Generate a unique session ID for this verification.</span>
  <span class="hljs-keyword">const</span> sessionId = crypto.randomUUID();

  <span class="hljs-comment">// Let them know we're working on it.</span>
  <span class="hljs-keyword">await</span> interaction.reply({
    <span class="hljs-attr">content</span>: <span class="hljs-string">"Generating your Self verification QR… I'll DM it to you shortly."</span>,
    <span class="hljs-attr">flags</span>: MessageFlags.Ephemeral,
  });

  <span class="hljs-comment">// Create the QR code.</span>
  <span class="hljs-keyword">const</span> qr = <span class="hljs-keyword">await</span> createSelfVerificationQr(sessionId, user);

  <span class="hljs-comment">// Store this pending verification.</span>
  pendingVerifications.set(sessionId, {
    <span class="hljs-attr">discordUserId</span>: user.id,
    <span class="hljs-attr">guildId</span>: guild.id,
    <span class="hljs-attr">createdAt</span>: <span class="hljs-built_in">Date</span>.now(),
    <span class="hljs-attr">qrPath</span>: qr.filePath,
  });

  <span class="hljs-comment">// DM them the QR code.</span>
  <span class="hljs-keyword">const</span> dm = <span class="hljs-keyword">await</span> user.createDM();
  <span class="hljs-keyword">await</span> dm.send({
    <span class="hljs-attr">content</span>: <span class="hljs-string">"Scan this QR code with the Self app (staging/mock passports) to verify your age/identity."</span>,
    <span class="hljs-attr">files</span>: [<span class="hljs-keyword">new</span> AttachmentBuilder(qr.filePath)],
  });
}
</code></pre>
<p><strong>The clever bit here is the session management.</strong> Each verification attempt gets a unique UUID. This session ID gets embedded in the QR code, sent to the Self app, included in the proof, sent back to our backend, and used to match the verified proof back to the original Discord user. It's like a claim ticket at a coat check.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763578592596/c7a78799-e2eb-4965-8e71-e966bc796ad0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-3-generating-the-qr-code">Step 3: Generating the QR Code</h3>
<p>This is where things get really interesting. We need to create a QR code that:</p>
<ol>
<li><p>The Self app can read and understand</p>
</li>
<li><p>Contains our verification requirements (age, OFAC check, etc.)</p>
</li>
<li><p>Includes our session ID so we can match it back to the Discord user</p>
</li>
<li><p>Links the verification to a specific Discord user ID</p>
</li>
</ol>
<p>Here's how:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createSelfVerificationQr</span>(<span class="hljs-params">sessionId, discordUser</span>) </span>{
  <span class="hljs-comment">// Convert Discord user ID (19-digit number) to hex format.</span>
  <span class="hljs-comment">// This creates a cryptographic commitment linking Discord user to proof.</span>
  <span class="hljs-keyword">const</span> hexUserId = BigInt(discordUser.id).toString(<span class="hljs-number">16</span>).padStart(<span class="hljs-number">40</span>, <span class="hljs-string">"0"</span>);
  <span class="hljs-keyword">const</span> userId = <span class="hljs-string">`0x<span class="hljs-subst">${hexUserId.slice(<span class="hljs-number">0</span>, <span class="hljs-number">40</span>)}</span>`</span>;

  <span class="hljs-comment">// Build the Self app configuration.</span>
  <span class="hljs-keyword">const</span> selfApp = <span class="hljs-keyword">new</span> SelfAppBuilder({
    <span class="hljs-attr">version</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">appName</span>: SELF_APP_NAME,
    <span class="hljs-attr">scope</span>: SELF_SCOPE,
    <span class="hljs-attr">endpoint</span>: SELF_ENDPOINT,
    <span class="hljs-attr">logoBase64</span>: SELF_LOGO_URL,
    userId,                              <span class="hljs-comment">// Hex-encoded Discord user ID.</span>
    <span class="hljs-attr">endpointType</span>: <span class="hljs-string">"staging_https"</span>,       <span class="hljs-comment">// Staging environment.</span>
    <span class="hljs-attr">userIdType</span>: <span class="hljs-string">"hex"</span>,                   <span class="hljs-comment">// User ID is hex format.</span>
    <span class="hljs-attr">userDefinedData</span>: <span class="hljs-built_in">JSON</span>.stringify({
      <span class="hljs-attr">kind</span>: <span class="hljs-string">"discord-self-verification"</span>,
      sessionId,                         <span class="hljs-comment">// Our session UUID.</span>
      <span class="hljs-attr">discordUserId</span>: discordUser.id,     <span class="hljs-comment">// Original Discord ID.</span>
      <span class="hljs-attr">guildId</span>: DISCORD_GUILD_ID,         <span class="hljs-comment">// Which server this is for.</span>
    }),
    <span class="hljs-attr">disclosures</span>: {
      <span class="hljs-attr">minimumAge</span>: <span class="hljs-number">18</span>,                    <span class="hljs-comment">// What we require.</span>
      <span class="hljs-attr">excludedCountries</span>: [],             <span class="hljs-comment">// No restrictions.</span>
      <span class="hljs-attr">ofac</span>: <span class="hljs-literal">true</span>,                        <span class="hljs-comment">// Enable OFAC check.</span>
      <span class="hljs-attr">nationality</span>: <span class="hljs-literal">true</span>,                 <span class="hljs-comment">// Request nationality.</span>
      <span class="hljs-attr">gender</span>: <span class="hljs-literal">true</span>,                      <span class="hljs-comment">// Request gender.</span>
    },
  }).build();

  <span class="hljs-comment">// Generate the Self universal link.</span>
  <span class="hljs-keyword">const</span> universalLink = getUniversalLink(selfApp);

  <span class="hljs-comment">// Create QR code as PNG.</span>
  <span class="hljs-keyword">const</span> filename = <span class="hljs-string">`self-qr-<span class="hljs-subst">${sessionId}</span>.png`</span>;
  <span class="hljs-keyword">const</span> filePath = path.join(qrOutputDir, filename);

  <span class="hljs-keyword">await</span> QRCode.toFile(filePath, universalLink, {
    <span class="hljs-attr">width</span>: <span class="hljs-number">512</span>,
    <span class="hljs-attr">errorCorrectionLevel</span>: <span class="hljs-string">"H"</span>,          <span class="hljs-comment">// High error correction.</span>
  });

  <span class="hljs-keyword">return</span> { universalLink, filename, filePath };
}
</code></pre>
<p><strong><mark>Let me explain the hex user ID thing</mark></strong> <mark>because it's genuinely clever:</mark></p>
<p>Discord user IDs are massive numbers (like <code>1439487858766512249</code>). The Self Protocol expects user IDs in hex format. So we convert the Discord ID to hexadecimal, pad it to 40 characters, and prefix it with <code>0x</code>.</p>
<p>Why? Because this creates a <strong>cryptographic commitment</strong>. The Self app will include this user ID in the proof. When we verify the proof, we can be absolutely certain it was generated for this specific Discord user—<strong>no one else could have generated a valid proof with this user ID</strong>.</p>
<p><mark>It's like signing a document that says "I am Discord user #1439487858766512249 and I am over 18" but in a way that's cryptographically unforgeable.</mark></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763578806300/c31396ac-eb9d-43d8-8511-f233157d78fe.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-4-the-verification-endpoint">Step 4: The Verification Endpoint</h3>
<p>After the user scans the QR code with the Self app and completes the verification flow on their phone, the Self app generates a zero-knowledge proof and sends it to our <code>/api/verify</code> endpoint.</p>
<p>This is where we validate everything:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// index.mjs.</span>
app.post(<span class="hljs-string">"/api/verify"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { attestationId, proof, publicSignals, userContextData } = req.body;

  <span class="hljs-comment">// Verify the proof using Self's backend verifier.</span>
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verify(
    attestationId,
    proof,
    publicSignals,
    userContextData
  );

  <span class="hljs-comment">// Check the verification results.</span>
  <span class="hljs-keyword">const</span> { isValid, isMinimumAgeValid, isOfacValid } = result.isValidDetails;

  <span class="hljs-keyword">if</span> (!isValid || !isMinimumAgeValid || isOfacValid) {
    <span class="hljs-keyword">let</span> reason = <span class="hljs-string">"Verification failed"</span>;
    <span class="hljs-keyword">if</span> (!isMinimumAgeValid) {
      reason = <span class="hljs-string">"Minimum age verification failed"</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (isOfacValid) {
      reason = <span class="hljs-string">"User is in OFAC sanctions list"</span>;
    }

    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
      <span class="hljs-attr">status</span>: <span class="hljs-string">"error"</span>,
      <span class="hljs-attr">result</span>: <span class="hljs-literal">false</span>,
      reason,
    });
  }

  <span class="hljs-comment">// Verification succeeded! Extract the session ID from the proof.</span>
  <span class="hljs-keyword">const</span> parsed = decodeUserDefinedDataHex(result.userData?.userDefinedData);

  <span class="hljs-keyword">if</span> (parsed &amp;&amp; parsed.kind === <span class="hljs-string">"discord-self-verification"</span> &amp;&amp; parsed.sessionId) {
    <span class="hljs-comment">// Match this proof back to the Discord user and assign their role.</span>
    <span class="hljs-keyword">await</span> handleDiscordVerificationSuccess(parsed.sessionId);
  }

  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">result</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">credentialSubject</span>: result.discloseOutput,
  });
});
</code></pre>
<p><strong>Here's what's happening under the hood:</strong></p>
<ol>
<li><p><strong>Proof Verification</strong>: The <code>selfBackendVerifier.verify()</code> call does all the heavy cryptographic lifting. It checks:</p>
<ul>
<li><p>Is the proof mathematically valid?</p>
</li>
<li><p>Does it prove the user is ≥18 years old?</p>
</li>
<li><p>Is the user on the OFAC sanctions list?</p>
</li>
<li><p>Does the proof match the expected scope and user ID?</p>
</li>
</ul>
</li>
<li><p><strong>OFAC Check</strong>: Note the inverted logic here, <code>isOfacValid</code> being true means the user IS on the sanctions list (bad). If it's false, they're clear (good). Confusing naming, but that's how the SDK works, haha.</p>
</li>
<li><p><strong>Session Matching</strong>: The proof includes our <code>userDefinedData</code> (remember that from the QR code?). We decode it from hex, extract the session ID, and match it back to our pending verifications.</p>
</li>
<li><p><strong>Zero Knowledge</strong>: Here's the beautiful part, the proof tells us <strong>"this person is over 18"</strong> but the actual proof data doesn't contain their birth date. It's a mathematical proof that the statement is true without revealing why it's true.</p>
</li>
</ol>
<h3 id="heading-step-5-assigning-the-role">Step 5: Assigning the Role</h3>
<p>Once we've verified the proof and matched it to a Discord user, we assign them the <strong>"Verified"</strong> role. This is the key that unlocks the age-restricted channels.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleDiscordVerificationSuccess</span>(<span class="hljs-params">sessionId</span>) </span>{
  <span class="hljs-comment">// Look up the pending verification.</span>
  <span class="hljs-keyword">const</span> entry = pendingVerifications.get(sessionId);
  <span class="hljs-keyword">if</span> (!entry) {
    logEvent(<span class="hljs-string">"verification.unknown_session"</span>, <span class="hljs-string">"Unknown session"</span>, { sessionId });
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// Clean up the pending verification.</span>
  pendingVerifications.delete(sessionId);

  <span class="hljs-keyword">const</span> { discordUserId, guildId } = entry;

  <span class="hljs-comment">// Fetch the Discord guild and member.</span>
  <span class="hljs-keyword">const</span> guild = <span class="hljs-keyword">await</span> discordClient.guilds.fetch(guildId);
  <span class="hljs-keyword">const</span> member = <span class="hljs-keyword">await</span> guild.members.fetch(discordUserId);

  <span class="hljs-comment">// Fetch the verified role.</span>
  <span class="hljs-keyword">const</span> role = <span class="hljs-keyword">await</span> guild.roles.fetch(DISCORD_VERIFIED_ROLE_ID);

  <span class="hljs-comment">// Assign it.</span>
  <span class="hljs-keyword">await</span> member.roles.add(role);

  <span class="hljs-comment">// Send them a success DM.</span>
  <span class="hljs-keyword">const</span> dm = <span class="hljs-keyword">await</span> member.createDM();
  <span class="hljs-keyword">await</span> dm.send(
    <span class="hljs-string">"✅ Your Self verification succeeded. You now have access to the restricted channels."</span>
  );

  logEvent(<span class="hljs-string">"verification.role_assigned"</span>, <span class="hljs-string">"Assigned verified role"</span>, {
    <span class="hljs-attr">guildId</span>: guild.id,
    discordUserId,
    <span class="hljs-attr">roleId</span>: role.id,
  });
}
</code></pre>
<p>And just like that, the user can see the restricted channels. No personal data shared. No screenshots of IDs. No trust issues. Just pure cryptographic proof.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763579020040/f2121d93-bea9-4a16-add0-d736d52bc072.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-ngrok-config">The Ngrok Config</h2>
<p>The Self app needs to send the proof to your backend. That means your backend needs a public HTTPS URL. Not HTTP. Not localhost. Not a local IP. A real, public, HTTPS URL that the Self app can reach from anywhere.</p>
<p>For local development, that means using a tunneling service like <a target="_blank" href="https://ngrok.com">ngrok</a>.</p>
<p>Here's the setup:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Terminal 1: Start the backend.</span>
<span class="hljs-built_in">cd</span> server
npm run dev

<span class="hljs-comment"># Terminal 2: Expose it with ngrok.</span>
ngrok http 3001
</code></pre>
<p>Ngrok gives you a URL like <code>https://abc123.ngrok-free.app</code>. You copy that, append <code>/api/verify</code>, and put it in your <code>.env</code> file as <code>SELF_ENDPOINT</code>.</p>
<p>The problem? <strong>Every time you restart Ngrok, you get a new URL.</strong> Free tier limitations. So you have to update your <code>.env</code> and restart your backend.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763579143911/f139dfed-27bb-424f-8a8d-7d4aa24cd5d8.png" alt class="image--center mx-auto" /></p>
<p><strong>Pro tip:</strong> If you're deploying this for real (not just development), host it on Render, Railway, or anywhere that gives you a stable HTTPS endpoint. <strong>Your future self will thank you</strong>.</p>
<h2 id="heading-setting-up-discord-permissions">Setting Up Discord Permissions</h2>
<p>Here's where a lot of people trip up: Discord's permission system is hierarchical and picky.</p>
<p>You need to:</p>
<ol>
<li><p><strong>Create a "Verified" role</strong> in your server</p>
</li>
<li><p><strong>Position your bot's role ABOVE the Verified role</strong> in the role hierarchy</p>
</li>
<li><p><strong>Give the bot "Manage Roles" permission</strong></p>
</li>
<li><p><strong>Set up channel permissions so only the Verified role can see restricted channels</strong></p>
</li>
</ol>
<p><mark>If your bot's role is below the Verified role, it can't assign it. Discord will silently fail, and you'll be scratching your head wondering why the code isn't working.</mark></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763579416021/4347571e-d111-4fc7-a34b-e4df75de176c.png" alt class="image--center mx-auto" /></p>
<p>Here's how you set it up:</p>
<p><strong>Step 1: Create the Verified Role</strong></p>
<ul>
<li><p>Server Settings → Roles → Create Role</p>
</li>
<li><p>Name it "Verified" (or whatever you want)</p>
</li>
<li><p>Copy the Role ID (right-click → Copy Role ID with Developer Mode enabled)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763579497147/214ecae7-4d40-4cb1-a53f-4fe5f20d28e3.png" alt class="image--center mx-auto" /></p>
<p><strong>Step 2: Position Your Bot's Role</strong></p>
<ul>
<li>Drag your bot's role ABOVE the Verified role in the role list</li>
</ul>
<p><strong>Step 3: Create Restricted Channels</strong></p>
<ul>
<li><p>Create a category called "18+ Only" (or whatever)</p>
</li>
<li><p>Edit Category → Permissions</p>
<ul>
<li><p><code>@everyone</code>: Disable "View Channel"</p>
</li>
<li><p><code>Verified</code> role: Enable "View Channel"</p>
</li>
</ul>
</li>
</ul>
<p>Now only verified users can see those channels. Beautiful.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763580369772/2215173f-337a-4a7d-a1f3-c640dcbcc9d6.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763580396466/a5492d6a-9f00-46aa-bb67-d2dc47538e49.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-actually-gets-shared">What Actually Gets Shared</h2>
<p>This is important, so let me be crystal clear:</p>
<h3 id="heading-what-the-discord-server-owner-gets">What the Discord Server Owner Gets:</h3>
<ul>
<li><p>✅ Confirmation that the user is ≥18 years old</p>
</li>
<li><p>✅ Confirmation that the user passed OFAC sanctions check</p>
</li>
<li><p>✅ Optionally: nationality (if disclosed)</p>
</li>
<li><p>✅ Optionally: gender (if disclosed)</p>
</li>
</ul>
<h3 id="heading-what-the-discord-server-owner-does-not-get">What the Discord Server Owner DOES NOT Get:</h3>
<ul>
<li><p>❌ User's actual age or birth date</p>
</li>
<li><p>❌ User's real name</p>
</li>
<li><p>❌ Passport number</p>
</li>
<li><p>❌ Photo of ID</p>
</li>
<li><p>❌ Address</p>
</li>
<li><p>❌ Any personally identifiable information</p>
</li>
</ul>
<h3 id="heading-what-gets-stored-on-my-server">What Gets Stored on My Server:</h3>
<ul>
<li><p>Session IDs (random UUIDs)</p>
</li>
<li><p>Discord user IDs (already public)</p>
</li>
<li><p>QR code images (temporary, can be auto-deleted)</p>
</li>
<li><p>Verification logs (session ID, timestamp, success/failure)</p>
</li>
</ul>
<h3 id="heading-what-gets-stored-in-the-proof">What Gets Stored in the Proof:</h3>
<ul>
<li><p>Cryptographic proof of age ≥18 (mathematical proof, not actual age)</p>
</li>
<li><p>OFAC status (boolean: sanctioned or not)</p>
</li>
<li><p>Disclosed attributes (if user chose to share nationality/gender)</p>
</li>
<li><p>Session ID (UUID we generated)</p>
</li>
<li><p>Hex-encoded Discord user ID (for linking proof to user)</p>
</li>
</ul>
<p>The proof itself is essentially a mathematical statement: <strong>"I possess a valid passport that proves I am ≥18 years old"</strong> without revealing what's on the passport.</p>
<h2 id="heading-how-zero-knowledge-proofs-work-here">How Zero-Knowledge Proofs Work Here</h2>
<p><mark>For the nerds in the audience (I see you)</mark>, let me explain what's actually happening cryptographically.</p>
<h3 id="heading-the-proof-generation-on-the-users-phone">The Proof Generation (On the User's Phone)</h3>
<p>When someone scans their passport with the Self app:</p>
<ol>
<li><p><strong>NFC Read</strong>: The app reads the passport's NFC chip. Modern passports have digitally signed data (called "passive authentication").</p>
</li>
<li><p><strong>Data Extraction</strong>: The app extracts relevant fields:</p>
<ul>
<li><p>Birth date</p>
</li>
<li><p>Nationality</p>
</li>
<li><p>Document number</p>
</li>
<li><p>Issuing country</p>
</li>
<li><p>Expiration date</p>
</li>
</ul>
</li>
<li><p><strong>Proof Circuit Execution</strong>: The Self app runs a zero-knowledge proof circuit that:</p>
<ul>
<li><p>Verifies the passport signature is valid (proves it's a real passport)</p>
</li>
<li><p>Computes <code>current_date - birth_date &gt;= 18 years</code></p>
</li>
<li><p>Checks nationality against excluded countries list (empty in our case)</p>
</li>
<li><p>Checks against OFAC sanctions database</p>
</li>
<li><p>All without revealing the actual values</p>
</li>
</ul>
</li>
<li><p><strong>Public Signals</strong>: The proof includes public outputs:</p>
<ul>
<li><p><code>minimumAgeValid</code>: true/false (whether age ≥18)</p>
</li>
<li><p><code>ofacValid</code>: true/false (whether on sanctions list)</p>
</li>
<li><p><code>userId</code>: The hex-encoded Discord user ID we provided</p>
</li>
<li><p><code>scope</code>: Our verification scope ("demo-scope")</p>
</li>
</ul>
</li>
<li><p><strong>Private Inputs</strong>: These stay secret in the proof:</p>
<ul>
<li><p>Actual birth date</p>
</li>
<li><p>Full name</p>
</li>
<li><p>Passport number</p>
</li>
<li><p>Full passport data</p>
</li>
</ul>
</li>
</ol>
<p>The magic is that the proof is mathematically verifiable but reveals nothing about the private inputs.</p>
<h3 id="heading-the-verification-on-our-backend">The Verification (On Our Backend)</h3>
<p>When our backend receives the proof:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verify(
  attestationId,    <span class="hljs-comment">// Unique proof identifier.</span>
  proof,            <span class="hljs-comment">// The actual ZK proof (array of field elements).</span>
  publicSignals,    <span class="hljs-comment">// Public outputs from the circuit.</span>
  userContextData   <span class="hljs-comment">// Metadata about the credential.</span>
);
</code></pre>
<p>The verifier checks:</p>
<ol>
<li><p><strong>Proof Validity</strong>: Does the proof mathematically check out?</p>
</li>
<li><p><strong>Public Signal Matching</strong>: Do the public outputs match our requirements?</p>
<ul>
<li><p><code>minimumAge</code> in signals ≥ 18</p>
</li>
<li><p><code>scope</code> matches our configured scope</p>
</li>
<li><p><code>userId</code> matches the expected Discord user ID</p>
</li>
</ul>
</li>
<li><p><strong>Timestamp Checks</strong>: Is the proof fresh? (Prevents replay attacks)</p>
</li>
<li><p><strong>Circuit Hash</strong>: Does the proof come from the expected circuit? (Prevents proof substitution)</p>
</li>
</ol>
<p>If all checks pass, we know with cryptographic certainty that:</p>
<ul>
<li><p>The user has a valid passport</p>
</li>
<li><p>The passport proves they're ≥18</p>
</li>
<li><p>The proof was generated for this specific Discord user</p>
</li>
<li><p>The proof hasn't been tampered with or replayed</p>
</li>
</ul>
<p>All without ever seeing the passport data itself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763580809912/8d83403d-fe85-44a1-b71f-0b4f32853932.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-real-world-considerations-and-edge-cases">Real-World Considerations and Edge Cases</h2>
<p>Building this was educational. Here are some gotchas I ran into:</p>
<h3 id="heading-1-session-cleanup">1. Session Cleanup</h3>
<p>Right now, pending verifications stay in memory until the verification completes or the server restarts. For production, you'd want:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Clean up stale sessions after 15 minutes.</span>
<span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> now = <span class="hljs-built_in">Date</span>.now();
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> [sessionId, entry] <span class="hljs-keyword">of</span> pendingVerifications) {
    <span class="hljs-keyword">if</span> (now - entry.createdAt &gt; <span class="hljs-number">15</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>) {
      pendingVerifications.delete(sessionId);
      <span class="hljs-comment">// Optionally delete the QR code file too.</span>
    }
  }
}, <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>); <span class="hljs-comment">// Run every minute</span>
</code></pre>
<h3 id="heading-2-rate-limiting">2. Rate Limiting</h3>
<p>Someone could spam <code>/verify</code> and generate tons of QR codes. Add rate limiting:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> verifyAttempts = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>(); <span class="hljs-comment">// userId -&gt; timestamp of last attempt.</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleVerifyCommand</span>(<span class="hljs-params">interaction</span>) </span>{
  <span class="hljs-keyword">const</span> lastAttempt = verifyAttempts.get(interaction.user.id);
  <span class="hljs-keyword">if</span> (lastAttempt &amp;&amp; <span class="hljs-built_in">Date</span>.now() - lastAttempt &lt; <span class="hljs-number">60000</span>) {
    <span class="hljs-keyword">await</span> interaction.reply({
      <span class="hljs-attr">content</span>: <span class="hljs-string">"Please wait a minute before trying again."</span>,
      <span class="hljs-attr">flags</span>: MessageFlags.Ephemeral,
    });
    <span class="hljs-keyword">return</span>;
  }

  verifyAttempts.set(interaction.user.id, <span class="hljs-built_in">Date</span>.now());
  <span class="hljs-comment">// ... rest of verification logic.</span>
}
</code></pre>
<h3 id="heading-3-qr-code-storage">3. QR Code Storage</h3>
<p>The QR codes pile up in <code>server/qrcodes/</code>. For production:</p>
<ul>
<li><p>Delete QR codes after successful verification</p>
</li>
<li><p>Or store them in temporary memory instead of disk</p>
</li>
<li><p>Or use S3/Cloud Storage with auto-expiration</p>
</li>
</ul>
<h3 id="heading-4-proof-replay-protection">4. Proof Replay Protection</h3>
<p>Self Protocol includes timestamp checks, but you could add additional protection:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> usedProofs = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>();

app.post(<span class="hljs-string">"/api/verify"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { attestationId } = req.body;

  <span class="hljs-keyword">if</span> (usedProofs.has(attestationId)) {
    <span class="hljs-keyword">return</span> res.json({ <span class="hljs-attr">status</span>: <span class="hljs-string">"error"</span>, <span class="hljs-attr">reason</span>: <span class="hljs-string">"Proof already used"</span> });
  }

  <span class="hljs-comment">// ... verify proof.</span>

  usedProofs.add(attestationId);

  <span class="hljs-comment">// Clean up old proof IDs after 1 hour.</span>
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> usedProofs.delete(attestationId), <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>);
});
</code></pre>
<h3 id="heading-5-error-handling-and-logging">5. Error Handling and Logging</h3>
<p>I built a JSON-line logger that records everything:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/logger.mjs.</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logEvent</span>(<span class="hljs-params">type, message, metadata = {}</span>) </span>{
  <span class="hljs-keyword">const</span> entry = {
    <span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString(),
    type,
    message,
    ...metadata,
  };

  <span class="hljs-keyword">const</span> logLine = <span class="hljs-built_in">JSON</span>.stringify(entry);
  fs.appendFileSync(logFilePath, logLine + <span class="hljs-string">"\n"</span>);
}
</code></pre>
<p>This makes it easy to grep logs, parse them programmatically, or ship them to a logging service:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># See all verification failures.</span>
cat server/logs/discord-verifier.log | grep <span class="hljs-string">"verification.failed"</span>

<span class="hljs-comment"># Count successful verifications.</span>
cat server/logs/discord-verifier.log | grep <span class="hljs-string">"verification.succeeded"</span> | wc -l
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763581962783/e91f1088-20fa-4693-a05c-349ab700ba79.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-deployment-checklist">Deployment Checklist</h2>
<p>If you want to actually deploy this <strong>(and you should, it's cool)</strong>, here's what you need:</p>
<h3 id="heading-environment-variables">Environment Variables</h3>
<pre><code class="lang-yaml"><span class="hljs-comment"># Server.</span>
<span class="hljs-string">PORT=3001</span>

<span class="hljs-comment"># Self Protocol.</span>
<span class="hljs-string">SELF_SCOPE=your-unique-scope</span>
<span class="hljs-string">SELF_ENDPOINT=https://your-domain.com/api/verify</span>
<span class="hljs-string">SELF_APP_NAME=Your</span> <span class="hljs-string">Discord</span> <span class="hljs-string">Verification</span>
<span class="hljs-string">SELF_LOGO_URL=https://your-domain.com/logo.png</span>

<span class="hljs-comment"># Discord.</span>
<span class="hljs-string">DISCORD_BOT_TOKEN=your_bot_token_here</span>
<span class="hljs-string">DISCORD_CLIENT_ID=your_client_id_here</span>
<span class="hljs-string">DISCORD_GUILD_ID=your_server_id_here</span>
<span class="hljs-string">DISCORD_VERIFIED_ROLE_ID=your_verified_role_id_here</span>
</code></pre>
<h3 id="heading-discord-developer-portal-setup">Discord Developer Portal Setup</h3>
<ol>
<li><p>Create application at <a target="_blank" href="https://discord.com/developers/applications">https://discord.com/developers/applications</a></p>
</li>
<li><p>Create bot and copy token</p>
</li>
<li><p>Enable Privileged Gateway Intents:</p>
<ul>
<li><p>Server Members Intent</p>
</li>
<li><p>Message Content Intent</p>
</li>
</ul>
</li>
<li><p>Generate invite URL with scopes:</p>
<ul>
<li><p>bot</p>
</li>
<li><p>applications.commands</p>
</li>
</ul>
</li>
<li><p>Bot permissions:</p>
<ul>
<li><p>View Channels</p>
</li>
<li><p>Send Messages</p>
</li>
<li><p>Manage Roles</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-production-vs-staging">Production vs. Staging</h3>
<p>I built this using Self's <strong>staging environment</strong> with mock passports. <a target="_blank" href="https://docs.self.xyz/frontend-integration/qrcode-sdk-api-reference">For production</a>:</p>
<ol>
<li><p>Change <code>endpointType</code> from <code>"staging_https"</code> to <code>"https"</code></p>
</li>
<li><p>Update the verifier constructor:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">new</span> SelfBackendVerifier(
   SELF_SCOPE,
   SELF_ENDPOINT,
   <span class="hljs-literal">false</span>,  <span class="hljs-comment">// false = use real passports, not mocks.</span>
   AllIds,
   <span class="hljs-comment">// ... rest of config.</span>
 )
</code></pre>
</li>
<li><p>Users will need to scan their real passports via NFC</p>
</li>
</ol>
<p>This means they'll need:</p>
<ul>
<li><p>A phone with NFC capability</p>
</li>
<li><p>A modern passport with an NFC chip (most issued after 2006)</p>
</li>
<li><p>The Self app installed</p>
</li>
</ul>
<h2 id="heading-why-this-matters">Why This Matters</h2>
<p><mark>This isn't just about Discord age verification. It's about a fundamental shift in how we think about identity online.</mark></p>
<p>Right now, the internet runs on trust:</p>
<ul>
<li><p>You trust Discord with your ID</p>
</li>
<li><p>You trust server owners with your data</p>
</li>
<li><p>You trust that databases won't be breached</p>
</li>
<li><p>You trust that people won't misuse your information</p>
</li>
</ul>
<p><strong>But we don't have to. Cryptography gives us a better way.</strong></p>
<p>Zero-knowledge proofs let us verify facts about people without learning those facts. In this case: "Are you 18?" → "Yes" (with mathematical proof) vs. "What's your birth date?" → "January 15, 1995" (requires trust).</p>
<h2 id="heading-try-it-yourself">Try It Yourself</h2>
<p>Seriously, go try this. The setup takes about <strong>20 minutes</strong> if you follow this:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/5e0b567325f444ad8b68de3bc9db4880">https://www.loom.com/share/5e0b567325f444ad8b68de3bc9db4880</a></div>
<p> </p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p><strong>Thanks for reading!</strong> If you build something cool with this, I'd love to hear about it. If you have questions, open an issue on GitHub. If you find this useful, give it a star.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Building Forms That Actually Respect Your Privacy]]></title><description><![CDATA[Look, we've all been there. You fill out a "totally anonymous" survey, click submit, and somehow you're getting targeted ads about your responses the next day. Companies love to slap an "anonymous" badge on their forms while quietly collecting your I...]]></description><link>https://writer.mrmehta.in/building-forms-that-actually-respect-your-privacy</link><guid isPermaLink="true">https://writer.mrmehta.in/building-forms-that-actually-respect-your-privacy</guid><category><![CDATA[Self-Protocol]]></category><category><![CDATA[ZKP]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Wed, 19 Nov 2025 06:44:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763496079004/301c4a52-2587-46b0-bbc7-ccd081edc892.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Look, we've all been there. You fill out a "totally anonymous" survey, click submit, and somehow you're getting targeted ads about your responses the next day. <mark>Companies love to slap an "anonymous" badge on their forms while quietly collecting your IP address, browser fingerprint, device ID, and enough metadata to practically write your biography.</mark></p>
<p>That's the problem I wanted to solve. And honestly? I was tired of the broken promises.</p>
<p><strong>Want to follow along?</strong> The complete source code for this project is available on GitHub:</p>
<p><a target="_blank" href="https://github.com/kartikmehta8/self-truely-anon-forms">https://github.com/kartikmehta8/self-truely-anon-forms</a></p>
<h2 id="heading-the-anonymous-lie-weve-all-been-sold">The "Anonymous" Lie We've All Been Sold</h2>
<p>Here's what really grinds my gears: companies claim their forms are anonymous, but they're actually hoarding data like digital dragons. Every time you submit a form online, here's what's typically getting captured behind the scenes:</p>
<ul>
<li><p><strong>Your IP address</strong> (which can be traced to your location)</p>
</li>
<li><p><strong>Browser fingerprinting</strong> (tracking you across the web)</p>
</li>
<li><p><strong>Device identifiers</strong> (yes, they know it's your phone)</p>
</li>
<li><p><strong>Cookies and tracking pixels</strong> (the internet's favourite stalkers)</p>
</li>
<li><p>Sometimes, <strong>email addresses</strong> for "verification" (spoiler: it's for marketing)</p>
</li>
</ul>
<p><em>And the worst part?</em> They still can't prevent spam bots from flooding their forms with garbage data. So you get the worst of both worlds: <mark>your privacy is violated AND the data quality is terrible.</mark></p>
<p>I wanted to build something different. Something that could prove you're a real human without knowing who you are.</p>
<p>Sounds impossible, right? That's where <strong>Self Protocol</strong> comes in.</p>
<p><a target="_blank" href="https://self.xyz"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763498080333/bbcc8e19-1a8b-46a8-987a-b64ecfc738ef.png" alt class="image--center mx-auto" /></a></p>
<h2 id="heading-what-if-forms-could-verify-humans-without-violating-privacy">What If Forms Could Verify Humans Without Violating Privacy?</h2>
<p>The breakthrough came when I discovered <a target="_blank" href="https://self.xyz">Self Protocol</a> and their approach to identity verification. Instead of collecting personal data, Self uses <strong>zero-knowledge proofs</strong> backed by NFC passport scanning.</p>
<p>Here's the beautiful part: you can prove you're:</p>
<ul>
<li><p>A real human being</p>
</li>
<li><p>Over 18 years old</p>
</li>
<li><p>Not on OFAC sanctions lists</p>
</li>
<li><p>A unique individual (preventing duplicate submissions)</p>
</li>
<li><p><strong>And much more, if you follow along with the SDK</strong></p>
</li>
</ul>
<p>All without revealing your name, nationality, passport number, or any other personally identifiable information. The form owner just gets a cryptographic hash that proves you're legit, nothing more, nothing less.</p>
<p>That's when I decided to build a form builder that puts this technology front and center.</p>
<h2 id="heading-the-self-forms-concept">The “Self Forms” Concept</h2>
<p><strong>What if every form submission required verification, but collected zero personal data beyond what the form explicitly asks for?</strong></p>
<p>No tracking. No fingerprinting. No backdoor data collection. Just verified, authentic responses from real humans.</p>
<p>Here's what I built:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763531103888/ee8bd2a1-c241-405c-9cd5-4b60ec20b8ab.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-part-1-building-the-form-builder">Part 1: Building the Form Builder</h2>
<p>The form builder lives at the root of the application (<code>/</code>). I wanted something clean and intuitive, think Notion-style sidebar with a drag-and-drop-ish feel.</p>
<h3 id="heading-the-interface-sidebar-three-tabs">The Interface: Sidebar + Three Tabs</h3>
<p>I built a responsive layout that adapts beautifully:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763532011365/430298ea-7f92-4026-9e29-f94907adcd49.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-1-form-builder-tab">1. Form Builder Tab</h4>
<p>This is where you design your forms. I kept it deliberately simple:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Form structure - clean and minimal.</span>
<span class="hljs-keyword">interface</span> FormField {
  id: <span class="hljs-built_in">string</span>;              <span class="hljs-comment">// Auto-generated random ID.</span>
  label: <span class="hljs-built_in">string</span>;           <span class="hljs-comment">// "What's your name?".</span>
  <span class="hljs-keyword">type</span>: FieldType;         <span class="hljs-comment">// text | textarea | number.</span>
  required: <span class="hljs-built_in">boolean</span>;       <span class="hljs-comment">// Make it mandatory or not.</span>
}
</code></pre>
<p>The builder lets you:</p>
<ul>
<li><p>Add a title (which auto-generates a URL-friendly slug)</p>
</li>
<li><p>Write an optional description</p>
</li>
<li><p>Add unlimited fields with three types: Short text, Long text, or Number</p>
</li>
<li><p>Mark fields as required or optional</p>
</li>
<li><p>Reorder fields by dragging (okay, I haven't built that yet, but it's on the list!)</p>
</li>
</ul>
<p>When you hit "Save form," here's what happens:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Client sends the form data.</span>
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${API_BASE_URL}</span>/api/forms`</span>, {
  method: <span class="hljs-string">'POST'</span>,
  headers: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
  body: <span class="hljs-built_in">JSON</span>.stringify({
    title: <span class="hljs-string">"Customer Feedback"</span>,
    slug: <span class="hljs-string">"customer-feedback"</span>,  <span class="hljs-comment">// Auto-generated from title.</span>
    description: <span class="hljs-string">"Tell us what you think!"</span>,
    fields: [
      { id: <span class="hljs-string">"abc123"</span>, label: <span class="hljs-string">"Your Name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"text"</span>, required: <span class="hljs-literal">true</span> },
      { id: <span class="hljs-string">"def456"</span>, label: <span class="hljs-string">"Feedback"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"textarea"</span>, required: <span class="hljs-literal">true</span> }
    ]
  })
});
</code></pre>
<p>The server validates it and stores everything in SQLite:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/modules/forms/form.service.mjs.</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createFormFromRequest</span>(<span class="hljs-params">payload</span>) </span>{
  validateFormPayload(payload);  <span class="hljs-comment">// Ensures title, slug, and fields exist.</span>

  <span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">fields</span>: payload.fields
  };

  <span class="hljs-keyword">return</span> createForm({
    <span class="hljs-attr">title</span>: payload.title,
    <span class="hljs-attr">description</span>: payload.description || <span class="hljs-string">""</span>,
    <span class="hljs-attr">slug</span>: payload.slug,
    <span class="hljs-attr">theme</span>: <span class="hljs-string">"light"</span>,
    config
  });
}
</code></pre>
<h4 id="heading-2-live-preview-tab">2. Live Preview Tab</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763532254399/bd2e8e5e-19e9-4c32-b33d-5242141bd93f.png" alt class="image--center mx-auto" /></p>
<p>This shows you exactly what your form will look like to end users. It's rendered using the same components as the public submission page, so what you see is genuinely what you get.</p>
<p><em>No surprises. No "it looks different in production" moments.</em></p>
<h4 id="heading-3-responses-tab">3. Responses Tab</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763532365345/369e1846-bef1-4451-985a-49781b6c637a.png" alt class="image--center mx-auto" /></p>
<p>This is where you see submissions. For each response, you get:</p>
<ul>
<li><p>Timestamp of submission</p>
</li>
<li><p>A verified user identifier (looks like <code>0xf4e9c2b1a8d7...</code>)</p>
</li>
<li><p>All the answers in a clean grid layout</p>
</li>
</ul>
<p>Here's the thing though: <strong>that user identifier is a cryptographic hash, not a real identity</strong>. You can't trace it back to a person. But it's consistent—<mark>so if someone fills out multiple forms, you can see it's the same verified human without knowing who they are.</mark></p>
<h2 id="heading-part-2-the-public-form-experience">Part 2: The Public Form Experience</h2>
<p>Now here's where it gets interesting. When you share a form (like <code>oursite.com/forms/customer-feedback</code>), users go through a multi-step verification flow.</p>
<h3 id="heading-step-1-loading-the-form">Step 1: Loading the Form</h3>
<p>When someone visits your form URL, the page fetches the form definition:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// client/src/app/forms/[slug]/page.tsx.</span>
<span class="hljs-keyword">const</span> loadForm = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${API_BASE_URL}</span>/api/forms/<span class="hljs-subst">${slug}</span>`</span>);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  setForm(data.form);
  setStep(<span class="hljs-string">"verify"</span>);  <span class="hljs-comment">// Move to verification step.</span>
};
</code></pre>
<p>Nothing special yet, just a normal API call.</p>
<h3 id="heading-step-2-self-verification-the-cool-part">Step 2: Self Verification (The Cool Part)</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763532882116/f3168647-c12c-4be9-8feb-caf77911357e.png" alt class="image--center mx-auto" /></p>
<p><em>Scan this QR code with the Self app to prove you're human</em></p>
<p>Here's how I initialise the Self verification:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Build the Self app configuration.</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> SelfAppBuilder({
  version: <span class="hljs-number">2</span>,
  appName: <span class="hljs-string">"Self Forms Verification"</span>,
  scope: <span class="hljs-string">"demo-scope"</span>,               <span class="hljs-comment">// Must match backend.</span>
  endpoint: <span class="hljs-string">"https://[your-ngrok].ngrok-free.app/api/verify"</span>,
  userId: ethers.ZeroAddress,        <span class="hljs-comment">// Anonymous by default.</span>
  endpointType: <span class="hljs-string">"staging_https"</span>,
  userDefinedData: <span class="hljs-string">`form:customer-feedback`</span>,  <span class="hljs-comment">// Embeds form context.</span>
  disclosures: {
    minimumAge: <span class="hljs-number">18</span>,                  <span class="hljs-comment">// Require 18+ verification.</span>
    excludedCountries: [],
    ofac: <span class="hljs-literal">true</span>,                      <span class="hljs-comment">// Check OFAC sanctions.</span>
    nationality: <span class="hljs-literal">false</span>,              <span class="hljs-comment">// Don't disclose nationality.</span>
    gender: <span class="hljs-literal">false</span>                    <span class="hljs-comment">// Don't disclose gender.</span>
  }
}).build();

<span class="hljs-comment">// Render the QR code.</span>
&lt;SelfQRcodeWrapper
  selfApp={selfApp}
  onSuccess={handleSuccessfulVerification}
  onError={handleVerificationError}
  <span class="hljs-keyword">type</span>=<span class="hljs-string">"websocket"</span>
  size={<span class="hljs-number">230</span>}
/&gt;
</code></pre>
<p>Notice the <code>disclosures</code> object? This is where you specify what you want to verify without actually collecting that data. I'm asking for:</p>
<ul>
<li><p>Age verification (must be 18+)</p>
</li>
<li><p>OFAC sanctions check (ensures they're not on government watchlists)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763533118781/472992c5-47f4-4eba-a7ef-f0f367c97727.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-what-happens-behind-the-scenes">What Happens Behind the Scenes</h3>
<p>When a user scans the QR code with their Self mobile app:</p>
<ol>
<li><p><strong>Self app reads the QR</strong> and sees the verification requirements</p>
</li>
<li><p><strong>User authenticates</strong> with biometrics (Face ID, fingerprint, etc.)</p>
</li>
<li><p><strong>Self app generates a zero-knowledge proof</strong> using NFC-scanned passport data</p>
</li>
<li><p><strong>Proof is sent to your backend</strong> at <code>/api/verify</code></p>
</li>
<li><p><strong>Backend validates the proof</strong> cryptographically</p>
</li>
<li><p><strong>Frontend unlocks the form</strong> if verification succeeds</p>
</li>
</ol>
<p>The beautiful part? <mark>At no point does the backend see their passport data. Self Protocol's zero-knowledge proof system ensures that.</mark></p>
<h2 id="heading-part-3-the-backend">Part 3: The Backend</h2>
<p>The server is where I spent the most time getting things right. I wanted it to be modular, secure, and easy to understand.</p>
<h3 id="heading-the-self-verification-endpoint">The Self Verification Endpoint</h3>
<p>This is the heart of the system. When the Self app sends a proof, my backend needs to validate it:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/index.mjs.</span>
<span class="hljs-keyword">const</span> selfBackendVerifier = <span class="hljs-keyword">new</span> SelfBackendVerifier(
  scope,                    <span class="hljs-comment">// "demo-scope".</span>
  endpoint,                 <span class="hljs-comment">// Public ngrok URL.</span>
  <span class="hljs-literal">true</span>,                     <span class="hljs-comment">// mockPassport = true (for testing).</span>
  AllIds,                   <span class="hljs-comment">// Accept all ID types.</span>
  <span class="hljs-keyword">new</span> DefaultConfigStore({
    <span class="hljs-attr">minimumAge</span>: <span class="hljs-number">18</span>,
    <span class="hljs-attr">excludedCountries</span>: [],
    <span class="hljs-attr">ofac</span>: <span class="hljs-literal">true</span>             <span class="hljs-comment">// OFAC sanctions check enabled.</span>
  }),
  <span class="hljs-string">"hex"</span>                    <span class="hljs-comment">// User ID format.</span>
);

app.post(<span class="hljs-string">"/api/verify"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { attestationId, proof, publicSignals, userContextData } = req.body;

  <span class="hljs-comment">// Verify the zero-knowledge proof.</span>
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verify(
    attestationId,
    proof,
    publicSignals,
    userContextData
  );

  <span class="hljs-keyword">const</span> { isValid, isMinimumAgeValid, isOfacValid } = result.isValidDetails;

  <span class="hljs-comment">// Reject if any check fails.</span>
  <span class="hljs-keyword">if</span> (!isValid || !isMinimumAgeValid || isOfacValid) {
    <span class="hljs-keyword">let</span> reason = <span class="hljs-string">"Verification failed"</span>;
    <span class="hljs-keyword">if</span> (!isMinimumAgeValid) {
      reason = <span class="hljs-string">"Minimum age verification failed"</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (isOfacValid) {
      reason = <span class="hljs-string">"User is on OFAC sanctions list"</span>;
    }

    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
      <span class="hljs-attr">status</span>: <span class="hljs-string">"error"</span>,
      <span class="hljs-attr">result</span>: <span class="hljs-literal">false</span>,
      reason
    });
  }

  <span class="hljs-comment">// Store verification result for frontend to retrieve.</span>
  lastVerificationResult = result;

  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">result</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">credentialSubject</span>: result.discloseOutput,
    <span class="hljs-attr">userData</span>: result.userData
  });
});
</code></pre>
<p>This code does three critical checks:</p>
<ol>
<li><p><strong>Proof validity</strong> - Is the cryptographic proof mathematically sound?</p>
</li>
<li><p><strong>Age verification</strong> - Is the user 18 or older based on their passport?</p>
</li>
<li><p><strong>OFAC sanctions</strong> - Is this person on government watchlists?</p>
</li>
</ol>
<p>If all checks pass, the verification succeeds. The frontend polls a debug endpoint to retrieve the result:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">"/debug/last-result"</span>, <span class="hljs-function">(<span class="hljs-params">_req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (!lastVerificationResult) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">status</span>: <span class="hljs-string">"empty"</span> });
  }
  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"ok"</span>,
    <span class="hljs-attr">verificationResult</span>: lastVerificationResult
  });
});
</code></pre>
<h3 id="heading-why-store-in-memory-instead-of-database">Why Store in Memory Instead of Database?</h3>
<p>You might be wondering: "Why store <code>lastVerificationResult</code> in a global variable instead of the database?"</p>
<p>Honest answer: <strong>simplicity for the MVP</strong>.</p>
<p>In a production system with multiple server instances, you'd want to use Redis or store it in the database with a short TTL. But for a starter template meant to be forked and extended, keeping it simple makes the code easier to understand.</p>
<h2 id="heading-part-4-server-architecture">Part 4: Server Architecture</h2>
<p>I wanted the backend to be easy to navigate, so I organized it into layers:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763533558831/559148b6-2f84-47f8-8e58-7f6900c655f1.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-repository-layer-raw-data-access">Repository Layer: Raw Data Access</h3>
<p>The repository handles all database operations:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/modules/forms/form.repository.mjs.</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createResponse</span>(<span class="hljs-params">{ formId, answers, selfVerification }</span>) </span>{
  <span class="hljs-keyword">const</span> stmt = db.prepare(<span class="hljs-string">`
    INSERT INTO form_responses
    (form_id, answers_json, self_verification_json)
    VALUES (?, ?, ?)
  `</span>);

  <span class="hljs-keyword">const</span> info = stmt.run(
    formId,
    <span class="hljs-built_in">JSON</span>.stringify(answers),
    <span class="hljs-built_in">JSON</span>.stringify(selfVerification)
  );

  <span class="hljs-keyword">return</span> info.lastInsertRowid;
}
</code></pre>
<h3 id="heading-service-layer-business-logic">Service Layer: Business Logic</h3>
<p>The service layer validates data and enforces rules:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/modules/forms/form.service.mjs.</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createFormResponse</span>(<span class="hljs-params">slug, payload</span>) </span>{
  <span class="hljs-keyword">const</span> form = getFormBySlug(slug);
  <span class="hljs-keyword">if</span> (!form) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Form not found"</span>);
  }

  <span class="hljs-comment">// CRITICAL: Enforce Self verification requirement.</span>
  <span class="hljs-keyword">if</span> (!payload.selfVerification?.userIdentifier) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
      <span class="hljs-string">"selfVerification with userIdentifier required. "</span> +
      <span class="hljs-string">"Make sure the user completes Self verification before submitting."</span>
    );
  }

  <span class="hljs-keyword">return</span> createResponse({
    <span class="hljs-attr">formId</span>: form.id,
    <span class="hljs-attr">answers</span>: payload.answers,
    <span class="hljs-attr">selfVerification</span>: payload.selfVerification
  });
}
</code></pre>
<p>This is the key security check: <strong>every form submission MUST include valid Self verification data</strong>. No verification? No submission.</p>
<h3 id="heading-controller-amp-routes-express-wrappers">Controller &amp; Routes: Express Wrappers</h3>
<p>Controllers are thin wrappers that call service functions:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/modules/forms/form.controller.mjs.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createResponseHandler</span>(<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { slug } = req.params;
    <span class="hljs-keyword">const</span> response = createFormResponse(slug, req.body);
    res.status(<span class="hljs-number">201</span>).json({ response });
  } <span class="hljs-keyword">catch</span> (error) {
    next(error);
  }
}
</code></pre>
<p>And routes define the API endpoints:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/modules/forms/form.routes.mjs.</span>
router.post(<span class="hljs-string">"/:slug/responses"</span>, createResponseHandler);
</code></pre>
<p>This separation makes the code easy to test, extend, and understand. <em>Want to add email notifications? Add it to the service layer. Want to change how errors are handled? Modify the controller.</em></p>
<h2 id="heading-the-complete-flow">The Complete Flow</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763533826611/fc58b152-ad92-4996-b77d-2a9d549ce614.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-this-matters">Why This Matters?</h2>
<h3 id="heading-the-problem-with-traditional-forms">The Problem with Traditional Forms</h3>
<p>Let's compare traditional form solutions with Self-verified forms:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Traditional Forms</td><td>Self-Verified Forms</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Bot Prevention</strong></td><td>CAPTCHA (annoying, often broken)</td><td>Cryptographic proof of humanity</td></tr>
<tr>
<td><strong>Privacy</strong></td><td>Collects IP, fingerprints, cookies</td><td>Zero tracking beyond form fields</td></tr>
<tr>
<td><strong>Anonymous Claims</strong></td><td>"Trust us, it's anonymous 😉"</td><td>Cryptographically anonymous</td></tr>
<tr>
<td><strong>Age Verification</strong></td><td>"Just check this box you're 18+"</td><td>NFC passport-based proof</td></tr>
<tr>
<td><strong>Duplicate Prevention</strong></td><td>Email/IP blocking (easy to bypass)</td><td>Unique cryptographic identifier</td></tr>
<tr>
<td><strong>Compliance</strong></td><td>Manual KYC (invasive)</td><td>Automatic OFAC checks without PII</td></tr>
<tr>
<td><strong>Data Quality</strong></td><td>High spam rate</td><td>Verified humans only</td></tr>
</tbody>
</table>
</div><h3 id="heading-real-world-use-cases">Real-World Use Cases</h3>
<p>This approach isn't just theoretical. Here's where Self-verified forms shine:</p>
<p><strong>1. Anonymous Whistleblowing</strong></p>
<ul>
<li><p>Employees can report workplace issues</p>
</li>
<li><p>Verified as real employees without revealing identity</p>
</li>
<li><p>Company knows reports are from verified humans, not spam</p>
</li>
</ul>
<p><strong>2. Sensitive Health Surveys</strong></p>
<ul>
<li><p>Research surveys about personal health topics</p>
</li>
<li><p>Age verification ensures legal compliance</p>
</li>
<li><p>No PII collected beyond survey answers</p>
</li>
</ul>
<p><strong>3. Feedback Forms for Regulated Industries</strong></p>
<ul>
<li><p>Financial services customer feedback</p>
</li>
<li><p>OFAC compliance built-in</p>
</li>
<li><p>Genuine customer responses only</p>
</li>
</ul>
<p><strong>4. Community Voting &amp; Polls</strong></p>
<ul>
<li><p>One person, one vote (cryptographically enforced)</p>
</li>
<li><p>No duplicate submissions possible</p>
</li>
<li><p>Completely anonymous but verified</p>
</li>
</ul>
<p><strong>5. Bug Bounty Submissions</strong></p>
<ul>
<li><p>Verify submitter is real person</p>
</li>
<li><p>Prevent spam submissions</p>
</li>
<li><p>Maintain researcher anonymity if desired</p>
</li>
</ul>
<p><strong>6. Product Beta Signups</strong></p>
<ul>
<li><p>Ensure real humans join beta programs</p>
</li>
<li><p>Prevent bot signups</p>
</li>
<li><p>No email required (optional)</p>
</li>
</ul>
<h2 id="heading-getting-started-your-own-self-verified-forms">Getting Started: Your Own Self-Verified Forms</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/7aeb2e1b36ae49b08fb20894a61969ef">https://www.loom.com/share/7aeb2e1b36ae49b08fb20894a61969ef</a></div>
<p> </p>
<p>Want to build on this? Here's how to get started:</p>
<h3 id="heading-installation">Installation</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Clone the repo.</span>
git <span class="hljs-built_in">clone</span> https://github.com/kartikmehta8/self-truely-anon-forms
<span class="hljs-built_in">cd</span> self-truely-anon-forms

<span class="hljs-comment"># Install backend dependencies.</span>
<span class="hljs-built_in">cd</span> server
npm install

<span class="hljs-comment"># Install frontend dependencies.</span>
<span class="hljs-built_in">cd</span> ../client
npm install
</code></pre>
<h3 id="heading-configuration">Configuration</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Backend .env.</span>
<span class="hljs-built_in">cd</span> server
cp .env.example .env
<span class="hljs-comment"># Edit: SELF_SCOPE, SELF_ENDPOINT.</span>

<span class="hljs-comment"># Frontend .env.</span>
<span class="hljs-built_in">cd</span> ../client
cp .env.example .env.local
<span class="hljs-comment"># Edit: Match backend values.</span>
</code></pre>
<h3 id="heading-running">Running</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Terminal 1: Start backend.</span>
<span class="hljs-built_in">cd</span> server
npm run dev

<span class="hljs-comment"># Terminal 2: Start ngrok.</span>
ngrok http 3001
<span class="hljs-comment"># Copy the https URL.</span>

<span class="hljs-comment"># Update .env files with ngrok URL.</span>

<span class="hljs-comment"># Terminal 3: Start frontend.</span>
<span class="hljs-built_in">cd</span> client
npm run dev
</code></pre>
<p>Visit <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a> and start building forms!</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Building this project changed how I think about online forms. We don't have to choose between security and privacy. We don't have to spy on users to verify they're real.</p>
<p><mark>Zero-knowledge proofs aren't just academic curiosities, they're practical tools that developers can use today to build better, more respectful applications.</mark></p>
<p>And honestly? That's pretty exciting.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Integrating Self Protocol with Rust]]></title><description><![CDATA[So there I was, excited to integrate Self, this awesome privacy-first identity verification protocol into my project. Self uses zero-knowledge proofs to verify real human identities without exposing sensitive data. Perfect for preventing bots, protec...]]></description><link>https://writer.mrmehta.in/integrating-self-protocol-with-rust</link><guid isPermaLink="true">https://writer.mrmehta.in/integrating-self-protocol-with-rust</guid><category><![CDATA[Self-Protocol]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[ZKP]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Tue, 18 Nov 2025 05:22:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763404091240/39cdf615-f1de-48ba-a23e-4f057ee52941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So there I was, excited to integrate <a target="_blank" href="https://self.xyz/">Self</a>, this awesome <strong>privacy-first identity verification protocol</strong> into my project. Self uses zero-knowledge proofs to verify real human identities without exposing sensitive data. Perfect for preventing bots, protecting airdrops, and building Sybil-resistant applications.</p>
<p>I also really wanted to build my backend in Rust for performance.</p>
<blockquote>
<p><strong>Want to follow along?</strong> All the code from this article is available on <a target="_blank" href="https://github.com/kartikmehta8/self-offchain-rust-starter">GitHub</a>. Feel free to clone, experiment, and build upon it!</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/self-offchain-rust-starter">https://github.com/kartikmehta8/self-offchain-rust-starter</a></div>
<p> </p>
<h2 id="heading-the-challenge">The Challenge</h2>
<p>Self provides excellent JavaScript/TypeScript SDKs with comprehensive documentation and support. As a Rust developer, this presented an interesting architectural challenge: <mark>how could I leverage Self’s robust JS SDK while building the high-performance Rust backend I wanted?</mark></p>
<p>I had a few options:</p>
<ol>
<li><p><strong>Use Node</strong> - Leverage the official SDK directly</p>
</li>
<li><p><strong>Wait for a Rust SDK</strong> - Maybe I’ll contribute one to the community someday, I’ve really been wanting to. :P</p>
</li>
<li><p><strong>Get creative</strong> - Find a way to use both languages together</p>
</li>
</ol>
<p>Option 1 meant giving up Rust’s benefits. Option 2 would delay my project (and reimplementing cryptographic verification seemed unnecessarily complex when Self already provides a battle-tested implementation).</p>
<p>So I chose option 3: <strong>What if I could combine Rust’s performance with Self’s JavaScript SDK?</strong></p>
<h2 id="heading-the-solution">The Solution</h2>
<p><strong>Here’s the idea:</strong> Rust handles what it’s great at (HTTP, routing, concurrency, type safety), while Node workers leverage Self’s official, well-maintained SDK for verification.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763410129886/94d9dd7f-10be-4ec1-a25e-71c94dde9d10.png" alt class="image--center mx-auto" /></p>
<p>The flow works like this:</p>
<ol>
<li><p><strong>User opens the web app</strong> and sees a QR code</p>
</li>
<li><p><strong>User scans with Self mobile app</strong></p>
</li>
<li><p><strong>Mobile app generates a zero-knowledge proof</strong> and POSTs it to the Rust backend</p>
</li>
<li><p><strong>Rust receives the request</strong> and forwards it to an available Node worker</p>
</li>
<li><p><strong>Node worker calls the Self SDK</strong> to verify the proof</p>
</li>
<li><p><strong>Worker returns the result</strong> to Rust</p>
</li>
<li><p><strong>Rust responds to the mobile app</strong> and caches the result</p>
</li>
<li><p><strong>Frontend polls the debug endpoint</strong> to display verification details</p>
</li>
</ol>
<p>This gives us the best of both worlds: Rust’s performance and type safety for the HTTP layer, and Self’s official, fully-featured SDK for verification, no need to reimplement anything.</p>
<h2 id="heading-the-backend">The Backend</h2>
<p>Let me walk you through the Rust implementation. The core is an <a target="_blank" href="https://github.com/tokio-rs/axum">Axum</a> HTTP server that manages a pool of Node worker processes.</p>
<h3 id="heading-setting-up-the-worker-pool">Setting Up the Worker Pool</h3>
<p>First, I needed a way to spawn and manage Node processes from Rust. That’s where the <a target="_blank" href="https://crates.io/crates/node-workers"><code>node-workers</code></a> crate comes in:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> node_workers::WorkerPool;
<span class="hljs-keyword">use</span> std::sync::{Arc, Mutex};

<span class="hljs-comment">// Create a pool that can manage up to 4 workers.</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pool = WorkerPool::setup(<span class="hljs-string">"worker/worker.mjs"</span>, <span class="hljs-number">4</span>);

<span class="hljs-comment">// Enable debug logging (helpful during development).</span>
pool.with_debug(debug_workers);

<span class="hljs-comment">// Here's the clever part: warm up 2 workers at startup.</span>
<span class="hljs-keyword">let</span> handle = pool.warmup(<span class="hljs-number">2</span>);
<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Err</span>(err) = handle.join() {
    error!(<span class="hljs-string">"Failed to warm up node workers: {:?}"</span>, err);
}
</code></pre>
<p>Why warm up workers at startup? <mark>Latency.</mark></p>
<p>If I waited until the first verification request to spawn a Node process, users would experience a noticeable delay. By pre-spawning 2 workers, they’re already running and ready to handle requests immediately. The pool can still scale up to 4 workers under heavy load.</p>
<h3 id="heading-the-application-state">The Application State</h3>
<p>The Rust server maintains three pieces of shared state:</p>
<pre><code class="lang-rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AppState</span></span> {
    config: Config, <span class="hljs-comment">// Scope and endpoint config.</span>
    pool: Arc&lt;Mutex&lt;WorkerPool&gt;&gt;, <span class="hljs-comment">// The Node worker pool.</span>
    last_result: RwLock&lt;<span class="hljs-built_in">Option</span>&lt;Value&gt;&gt;, <span class="hljs-comment">// Cache for debug endpoint.</span>
}

<span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">SharedState</span></span> = State&lt;Arc&lt;AppState&gt;&gt;;
</code></pre>
<p>This state is shared across all request handlers using Axum’s <code>State</code> extractor.</p>
<h3 id="heading-the-verification-endpoint">The Verification Endpoint</h3>
<p>This is where the magic happens. When the Self mobile app sends a verification proof, this endpoint handles it:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">verify_handler</span></span>(
    State(state): State&lt;SharedState&gt;,
    Json(body): Json&lt;Value&gt;,
) -&gt; <span class="hljs-keyword">impl</span> IntoResponse {
    info!(<span class="hljs-string">"Received verification request"</span>);

    <span class="hljs-comment">// 1. Extract the proof data from the request.</span>
    <span class="hljs-keyword">let</span> attestation_id = body.get(<span class="hljs-string">"attestationId"</span>).cloned();
    <span class="hljs-keyword">let</span> proof = body.get(<span class="hljs-string">"proof"</span>).cloned();
    <span class="hljs-keyword">let</span> public_signals = body.get(<span class="hljs-string">"publicSignals"</span>).cloned();
    <span class="hljs-keyword">let</span> user_context_data = body.get(<span class="hljs-string">"userContextData"</span>).cloned();

    <span class="hljs-comment">// Validate all required fields are present.</span>
    <span class="hljs-keyword">if</span> attestation_id.is_none() || proof.is_none() ||
       public_signals.is_none() || user_context_data.is_none() {
        <span class="hljs-keyword">return</span> error_response(<span class="hljs-string">"Required fields missing"</span>);
    }

    <span class="hljs-comment">// 2. Prepare the payload for the Node worker.</span>
    <span class="hljs-keyword">let</span> payload_for_node = json!({
        <span class="hljs-string">"attestationId"</span>: attestation_id.unwrap(),
        <span class="hljs-string">"proof"</span>: proof.unwrap(),
        <span class="hljs-string">"publicSignals"</span>: public_signals.unwrap(),
        <span class="hljs-string">"userContextData"</span>: user_context_data.unwrap(),
    });

    info!(<span class="hljs-string">"Calling Node worker for verification..."</span>);

    <span class="hljs-comment">// 3. Call the Node worker in a blocking thread.</span>
    <span class="hljs-keyword">let</span> pool_arc = state.pool.clone();
    <span class="hljs-keyword">let</span> node_result = tokio::task::spawn_blocking(<span class="hljs-keyword">move</span> || {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pool = pool_arc.lock().expect(<span class="hljs-string">"worker pool poisoned"</span>);
        pool.perform::&lt;Value, _&gt;(<span class="hljs-string">"verifyProof"</span>, <span class="hljs-built_in">vec!</span>[payload_for_node])
    }).<span class="hljs-keyword">await</span>;

    <span class="hljs-comment">// 4. Extract and process the result.</span>
    <span class="hljs-keyword">let</span> verification_result = <span class="hljs-keyword">match</span> node_result {
        <span class="hljs-literal">Ok</span>(<span class="hljs-literal">Ok</span>(results)) =&gt; {
            results.into_iter().next().flatten()
        }
        <span class="hljs-literal">Ok</span>(<span class="hljs-literal">Err</span>(e)) =&gt; {
            error!(<span class="hljs-string">"Worker error: {:?}"</span>, e);
            <span class="hljs-keyword">return</span> error_response(<span class="hljs-string">"Worker error"</span>);
        }
        <span class="hljs-literal">Err</span>(e) =&gt; {
            error!(<span class="hljs-string">"Join error: {:?}"</span>, e);
            <span class="hljs-keyword">return</span> error_response(<span class="hljs-string">"Internal error"</span>);
        }
    };

    <span class="hljs-keyword">let</span> verification_result = <span class="hljs-keyword">match</span> verification_result {
        <span class="hljs-literal">Some</span>(vr) =&gt; vr,
        <span class="hljs-literal">None</span> =&gt; <span class="hljs-keyword">return</span> error_response(<span class="hljs-string">"No result from worker"</span>),
    };

    <span class="hljs-comment">// 5. Check the verification details.</span>
    <span class="hljs-keyword">let</span> is_valid = verification_result[<span class="hljs-string">"isValidDetails"</span>][<span class="hljs-string">"isValid"</span>]
        .as_bool().unwrap_or(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">let</span> is_min_age_valid = verification_result[<span class="hljs-string">"isValidDetails"</span>][<span class="hljs-string">"isMinimumAgeValid"</span>]
        .as_bool().unwrap_or(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">let</span> is_ofac_valid = verification_result[<span class="hljs-string">"isValidDetails"</span>][<span class="hljs-string">"isOfacValid"</span>]
        .as_bool().unwrap_or(<span class="hljs-literal">false</span>);

    info!(<span class="hljs-string">"Verification result - valid: {}, min_age: {}, ofac: {}"</span>,
          is_valid, is_min_age_valid, is_ofac_valid);

    <span class="hljs-comment">// 6. Store the result for the debug endpoint.</span>
    *state.last_result.write().<span class="hljs-keyword">await</span> = <span class="hljs-literal">Some</span>(verification_result.clone());

    <span class="hljs-comment">// 7. Return success or failure.</span>
    <span class="hljs-comment">// Note: is_ofac_valid=true means user IS on OFAC list (failed check)</span>
    <span class="hljs-keyword">if</span> !is_valid || !is_min_age_valid || is_ofac_valid {
        <span class="hljs-keyword">return</span> error_response_with_details(verification_result);
    }

    Json(json!({
        <span class="hljs-string">"status"</span>: <span class="hljs-string">"success"</span>,
        <span class="hljs-string">"result"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"credentialSubject"</span>: verification_result[<span class="hljs-string">"discloseOutput"</span>],
        <span class="hljs-string">"userData"</span>: verification_result[<span class="hljs-string">"userData"</span>],
    }))
}
</code></pre>
<h3 id="heading-why-spawnblocking">Why <code>spawn_blocking</code>?</h3>
<p>Notice the <code>tokio::task::spawn_blocking</code> wrapper? That’s crucial. The <code>node-workers</code> crate uses synchronous mutex operations that would block the async runtime. <strong>By spawning it in a blocking thread, we prevent it from blocking other async tasks.</strong></p>
<p>This is a common pattern when integrating synchronous libraries into async Rust code.</p>
<h2 id="heading-the-node-worker">The Node Worker</h2>
<p>Now let’s look at the Node side. The worker is a single file that communicates with Rust via stdin/stdout.</p>
<h3 id="heading-the-communication-protocol">The Communication Protocol</h3>
<p>The <code>node-workers</code> crate uses a simple line-based protocol:</p>
<ol>
<li><p>Rust sends: <code>PAYLOAD_CHUNK:&lt;data&gt;</code> (can be multiple chunks)</p>
</li>
<li><p>Rust sends: <code>PAYLOAD_END</code></p>
</li>
<li><p>Node responds: <code>PAYLOAD_OK</code></p>
</li>
<li><p>Rust sends: <code>CMD:verifyProof</code></p>
</li>
<li><p>Node sends: <code>RESULT_CHUNK:&lt;data&gt;</code> (can be multiple chunks)</p>
</li>
<li><p>Node sends: <code>OK</code></p>
</li>
</ol>
<p>Here’s how the worker implements this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> readline <span class="hljs-keyword">from</span> <span class="hljs-string">"readline"</span>;
<span class="hljs-keyword">import</span> { SelfBackendVerifier, AllIds, DefaultConfigStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/core"</span>;
<span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;

dotenv.config();

<span class="hljs-comment">// Initialize the Self SDK verifier.</span>
<span class="hljs-keyword">const</span> scope = process.env.SELF_SCOPE;
<span class="hljs-keyword">const</span> endpoint = process.env.SELF_ENDPOINT;

<span class="hljs-keyword">const</span> selfBackendVerifier = <span class="hljs-keyword">new</span> SelfBackendVerifier(
  scope,
  endpoint,
  <span class="hljs-literal">true</span>,  <span class="hljs-comment">// mockPassport: true for staging/testing.</span>
  AllIds,  <span class="hljs-comment">// Accept all document types (passport, ID, Aadhaar).</span>
  <span class="hljs-keyword">new</span> DefaultConfigStore({
    <span class="hljs-attr">minimumAge</span>: <span class="hljs-number">18</span>,
    <span class="hljs-attr">excludedCountries</span>: [],
    <span class="hljs-attr">ofac</span>: <span class="hljs-literal">true</span>,  <span class="hljs-comment">// Enable OFAC sanctions screening.</span>
  }),
  <span class="hljs-string">"hex"</span>,  <span class="hljs-comment">// User ID format.</span>
);

<span class="hljs-comment">// Set up stdin/stdout communication.</span>
<span class="hljs-keyword">const</span> rl = readline.createInterface({
  <span class="hljs-attr">input</span>: process.stdin,
  <span class="hljs-attr">output</span>: process.stdout,
  <span class="hljs-attr">terminal</span>: <span class="hljs-literal">false</span>,
});

<span class="hljs-keyword">let</span> payloadStr = <span class="hljs-string">""</span>;
<span class="hljs-keyword">let</span> payload = <span class="hljs-literal">null</span>;

<span class="hljs-comment">// Handle incoming commands from Rust.</span>
rl.on(<span class="hljs-string">"line"</span>, <span class="hljs-keyword">async</span> (line) =&gt; {
  <span class="hljs-keyword">switch</span> (line) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"PAYLOAD_END"</span>:
      payload = <span class="hljs-built_in">JSON</span>.parse(payloadStr || <span class="hljs-string">"null"</span>);
      <span class="hljs-keyword">if</span> (payload &amp;&amp; payload._inner_payload) {
        payload = payload._inner_payload;
      }
      payloadStr = <span class="hljs-string">""</span>;
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"PAYLOAD_OK"</span>);
      <span class="hljs-keyword">break</span>;

    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">if</span> (line.startsWith(<span class="hljs-string">"PAYLOAD_CHUNK:"</span>)) {
        payloadStr += line.replace(<span class="hljs-string">"PAYLOAD_CHUNK:"</span>, <span class="hljs-string">""</span>).trim();
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (line.startsWith(<span class="hljs-string">"CMD:"</span>)) {
        <span class="hljs-keyword">const</span> cmd = line.replace(<span class="hljs-string">"CMD:"</span>, <span class="hljs-string">""</span>).trim();
        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">await</span> handleCommand(cmd);
        } <span class="hljs-keyword">catch</span> (err) {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"ERROR:"</span>, err.message);
          process.exit(<span class="hljs-number">1</span>);  <span class="hljs-comment">// Crash the worker, pool will spawn a new one.</span>
        }
      }
  }
});

<span class="hljs-comment">// Signal to Rust that worker is ready.</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"READY"</span>);
</code></pre>
<h3 id="heading-the-verification-handler">The Verification Handler</h3>
<p>When Rust sends the <code>verifyProof</code> command, the worker calls the Self SDK:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleCommand</span>(<span class="hljs-params">cmd</span>) </span>{
  <span class="hljs-keyword">const</span> currentPayload = payload;
  payload = <span class="hljs-literal">null</span>;

  <span class="hljs-keyword">if</span> (cmd === <span class="hljs-string">"verifyProof"</span>) {
    <span class="hljs-keyword">const</span> { attestationId, proof, publicSignals, userContextData } =
      currentPayload ?? {};

    <span class="hljs-keyword">if</span> (!attestationId || !proof || !publicSignals || !userContextData) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Required fields missing in verifyProof"</span>);
    }

    <span class="hljs-comment">// Call the Self SDK to verify the proof.</span>
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> selfBackendVerifier.verify(
      attestationId,
      proof,
      publicSignals,
      userContextData,
    );

    <span class="hljs-comment">// Send result back to Rust in chunks (max 1000 chars each).</span>
    <span class="hljs-keyword">const</span> str = <span class="hljs-built_in">JSON</span>.stringify(result);
    <span class="hljs-keyword">const</span> chunks = str.match(<span class="hljs-regexp">/.{1,1000}/g</span>) || [];
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> chunk <span class="hljs-keyword">of</span> chunks) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`RESULT_CHUNK: <span class="hljs-subst">${chunk}</span>`</span>);
    }
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (cmd === <span class="hljs-string">"ping"</span>) {
    <span class="hljs-comment">// Health check.</span>
    <span class="hljs-keyword">const</span> str = <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">ok</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">ts</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString() });
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`RESULT_CHUNK: <span class="hljs-subst">${str}</span>`</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Task "<span class="hljs-subst">${cmd}</span>" not found`</span>);
  }

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"OK"</span>);
}
</code></pre>
<p>The chunking is important, <mark>large JSON responses need to be split to avoid buffering issues in the IPC mechanism.</mark></p>
<h2 id="heading-the-frontend">The Frontend</h2>
<p>The frontend is relatively straightforward, it’s a <em>Next.js 16</em> app that displays a QR code and shows verification results.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763411594497/70c8f38e-3d73-43d5-9c02-b0333f5d3a18.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-generating-the-qr-code">Generating the QR Code</h3>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { SelfAppBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/core"</span>;
<span class="hljs-keyword">import</span> { SelfQRcodeWrapper } <span class="hljs-keyword">from</span> <span class="hljs-string">"@selfxyz/qrcode"</span>;
<span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [showQr, setShowQr] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [status, setStatus] = useState(<span class="hljs-string">"Scan the QR code with Self app"</span>);
  <span class="hljs-keyword">const</span> [verificationDetails, setVerificationDetails] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-comment">// Build the Self app configuration.</span>
  <span class="hljs-keyword">const</span> selfApp = <span class="hljs-keyword">new</span> SelfAppBuilder({
    version: <span class="hljs-number">2</span>,
    appName: process.env.NEXT_PUBLIC_SELF_APP_NAME || <span class="hljs-string">"Self Demo"</span>,
    scope: process.env.NEXT_PUBLIC_SELF_SCOPE || <span class="hljs-string">"demo-scope"</span>,
    endpoint: process.env.NEXT_PUBLIC_SELF_ENDPOINT ||
              <span class="hljs-string">"http://localhost:3001/api/verify"</span>,
    logoBase64: <span class="hljs-string">"https://i.postimg.cc/mrmVf9hm/self.png"</span>,
    userId: ethers.ZeroAddress,  <span class="hljs-comment">// Placeholder user ID.</span>
    endpointType: <span class="hljs-string">"staging_https"</span>,  <span class="hljs-comment">// Use staging for mock passports.</span>
    userIdType: <span class="hljs-string">"hex"</span>,
    userDefinedData: <span class="hljs-string">"demo-user"</span>,
    disclosures: {
      minimumAge: <span class="hljs-number">18</span>,
      excludedCountries: [],
      ofac: <span class="hljs-literal">true</span>,
      nationality: <span class="hljs-literal">true</span>,
      gender: <span class="hljs-literal">true</span>,
    },
  }).build();

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"app-container"</span>&gt;
      {showQr &amp;&amp; (
        &lt;SelfQRcodeWrapper
          selfApp={selfApp}
          onSuccess={handleSuccessfulVerification}
          onError={handleError}
          <span class="hljs-keyword">type</span>=<span class="hljs-string">"websocket"</span>
          size={<span class="hljs-number">260</span>}
        /&gt;
      )}

      &lt;p className=<span class="hljs-string">"status"</span>&gt;{status}&lt;/p&gt;

      {verificationDetails &amp;&amp; (
        &lt;VerificationResult details={verificationDetails} /&gt;
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<h3 id="heading-fetching-verification-results">Fetching Verification Results</h3>
<p>When the QR code callback fires <code>onSuccess</code>, it means the mobile app completed verification but the frontend doesn’t have the details yet. So it polls the debug endpoint:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSuccessfulVerification = <span class="hljs-keyword">async</span> () =&gt; {
  setStatus(<span class="hljs-string">"Verification succeeded! Loading details..."</span>);
  setShowQr(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> debugEndpoint = process.env.NEXT_PUBLIC_SELF_DEBUG_ENDPOINT || 
        <span class="hljs-string">"http://localhost:3001/debug/last-result"</span>;

    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(debugEndpoint);
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();

    <span class="hljs-keyword">if</span> (data &amp;&amp; data.verificationResult) {
      setVerificationDetails(data.verificationResult);
      setStatus(<span class="hljs-string">"Verification complete!"</span>);
    }
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to fetch verification details:"</span>, err);
    setStatus(<span class="hljs-string">"Verification succeeded but couldn't load details"</span>);
  }
};
</code></pre>
<p>The debug endpoint is simple—it just returns the cached verification result:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">last_result_handler</span></span>(State(state): State&lt;SharedState&gt;) -&gt; <span class="hljs-keyword">impl</span> IntoResponse {
    <span class="hljs-keyword">let</span> last = state.last_result.read().<span class="hljs-keyword">await</span>.clone();

    <span class="hljs-keyword">let</span> body = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(result) = last {
        Json(json!({
            <span class="hljs-string">"status"</span>: <span class="hljs-string">"ok"</span>,
            <span class="hljs-string">"verificationResult"</span>: result,
        }))
    } <span class="hljs-keyword">else</span> {
        Json(json!({ <span class="hljs-string">"status"</span>: <span class="hljs-string">"empty"</span> }))
    };

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> response = body.into_response();
    response.headers_mut().insert(
        header::ACCESS_CONTROL_ALLOW_ORIGIN,
        HeaderValue::from_static(<span class="hljs-string">"*"</span>),
    );
    response
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763411842522/fe01862c-4f2b-41ae-b6a2-2828fecf59f5.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-devil-in-the-details">The Devil in the Details</h2>
<h3 id="heading-environment-configuration-hell">Environment Configuration Hell</h3>
<p>Getting all the environment variables right was surprisingly tricky. Here’s what you need:</p>
<p><strong>Backend</strong> (server/.env):</p>
<pre><code class="lang-yaml"><span class="hljs-string">PORT=3001</span>
<span class="hljs-string">SELF_SCOPE=demo-scope</span>
<span class="hljs-string">SELF_ENDPOINT=https://your-ngrok-url.ngrok-free.app/api/verify</span>
</code></pre>
<p><strong>Frontend</strong> (client/.env):</p>
<pre><code class="lang-yaml"><span class="hljs-string">NEXT_PUBLIC_SELF_APP_NAME=Self</span> <span class="hljs-string">Verification</span> <span class="hljs-string">Demo</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_SCOPE=demo-scope</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_ENDPOINT=https://your-ngrok-url.ngrok-free.app/api/verify</span>
<span class="hljs-string">NEXT_PUBLIC_SELF_DEBUG_ENDPOINT=http://localhost:3001/debug/last-result</span>
</code></pre>
<p><strong>Critical points</strong>:</p>
<ol>
<li><p><strong>Scope must match exactly</strong> between frontend and backend</p>
</li>
<li><p><strong>Endpoint must match exactly</strong> between frontend and backend</p>
</li>
<li><p><strong>Endpoint must be HTTPS</strong> and publicly accessible (hence <em>ngrok</em>)</p>
</li>
<li><p><strong>Debug endpoint can be localhost</strong> (browser → local server)</p>
</li>
</ol>
<h3 id="heading-ngrok">Ngrok</h3>
<p>For local development, you need <em>ngrok</em> to expose your Rust server publicly:</p>
<pre><code class="lang-bash">ngrok  http  3001
</code></pre>
<p>This gives you a URL like <code>https://abc123.ngrok-free.app</code>. You need to:</p>
<ol>
<li><p>Update <code>SELF_ENDPOINT</code> in <code>server/.env</code></p>
</li>
<li><p>Update <code>NEXT_PUBLIC_SELF_ENDPOINT</code> in <code>client/.env</code></p>
</li>
<li><p>Restart both the Rust server and Next.js dev server</p>
</li>
</ol>
<p>Every time <em>ngrok</em> restarts, you get a new URL (unless you pay for a static domain). It’s annoying but necessary for testing with the mobile app.</p>
<h3 id="heading-the-ofac-logic">The OFAC Logic</h3>
<p>This tripped me up initially. The <code>isOfacValid</code> field is <strong>inverted</strong>:</p>
<ul>
<li><p><code>isOfacValid: false</code> = User is <strong>NOT</strong> on OFAC lists (passed the check)</p>
</li>
<li><p><code>isOfacValid: true</code> = User <strong>IS</strong> on OFAC lists (failed the check)</p>
</li>
</ul>
<p>So the verification logic rejects when <code>is_ofac_valid</code> is <code>true</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> !is_valid || !is_min_age_valid || is_ofac_valid {
    <span class="hljs-keyword">return</span> error_response_with_details(verification_result);
}
</code></pre>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-this-architecture-actually-rules">This Architecture Actually Rules</h3>
<p>I went into this thinking “it’s a hacky workaround,” but honestly? <mark>This pattern is brilliant for polyglot backends.</mark> The benefits:</p>
<ul>
<li><p><strong>Clean separation</strong>: Rust handles all HTTP concerns, Node only does SDK work</p>
</li>
<li><p><strong>Official SDK</strong>: We get to use Self’s production-ready, well-tested SDK directly</p>
</li>
<li><p><strong>Best of both worlds</strong>: Rust’s performance for I/O, Self’s comprehensive SDK for verification</p>
</li>
<li><p><strong>Isolation</strong>: Worker crashes don’t take down the server</p>
</li>
<li><p><strong>Scalability</strong>: Pool grows/shrinks with demand</p>
</li>
</ul>
<p>The <code>node-workers</code> crate makes this pattern incredibly simple. The entire Rust-Node bridge is maybe 50 lines of code.</p>
<h3 id="heading-trade-offs">Trade-offs</h3>
<p>Of course, there are downsides:</p>
<ol>
<li><p><strong>Complexity</strong>: Two runtimes to manage, debug, and deploy</p>
</li>
<li><p><strong>Memory</strong>: Each Node worker is ~50-100MB of RAM</p>
</li>
<li><p><strong>IPC overhead</strong>: Serializing/deserializing JSON adds microseconds</p>
</li>
<li><p><strong>Debugging</strong>: Errors can happen in Rust or Node, need to check both logs</p>
</li>
</ol>
<p><mark>For a simple CRUD app, this would be overkill. But for a performance-critical backend that needs JavaScript-only libraries? Perfect fit.</mark></p>
<h2 id="heading-try-it-yourself">Try It Yourself</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/57d0aaea16124e49bcaf082ecec4af61">https://www.loom.com/share/57d0aaea16124e49bcaf082ecec4af61</a></div>
<p> </p>
<p>Want to run this? Here’s the setup:</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p><strong>Node 18+</strong> (for the worker)</p>
</li>
<li><p><strong>Rust</strong> (for the backend)</p>
</li>
<li><p><strong>Ngrok</strong> (for HTTPS tunneling)</p>
</li>
</ul>
<h3 id="heading-installation">Installation</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Clone the repo.</span>
git <span class="hljs-built_in">clone</span> https://github.com/kartikmehta8/self-offchain-rust-starter
<span class="hljs-built_in">cd</span> self-offchain-rust-starter

<span class="hljs-comment"># 2. Install worker dependencies.</span>
<span class="hljs-built_in">cd</span> server/worker
npm install
<span class="hljs-built_in">cd</span> ../..

<span class="hljs-comment"># 3. Install frontend dependencies.</span>
<span class="hljs-built_in">cd</span> client
npm install
<span class="hljs-built_in">cd</span> ..

<span class="hljs-comment"># 4. Build Rust backend (optional, cargo run will build).</span>
cargo build --manifest-path server/Cargo.toml
</code></pre>
<h3 id="heading-running">Running</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Terminal 1: Start Rust backend.</span>
<span class="hljs-built_in">cd</span> server
cargo run

<span class="hljs-comment"># Terminal 2: Start ngrok.</span>
ngrok http 3001
<span class="hljs-comment"># Copy the HTTPS URL and update both .env files.</span>

<span class="hljs-comment"># Terminal 3: Start frontend.</span>
<span class="hljs-built_in">cd</span> client
npm run dev
</code></pre>
<p>Open <code>http://localhost:3000</code>, scan the QR code with the <a target="_blank" href="https://self.xyz/download">Self mobile app</a>, and watch the magic happen!</p>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>Building this taught me that <strong>language boundaries don’t have to limit your choices</strong>. Self’s decision to focus on JavaScript/TypeScript SDKs makes total sense, it’s where most of their developer community is. But with a little creativity, you can still leverage their excellent SDK from any language.</p>
<p>Would I use this in production? Absolutely. With proper monitoring, error handling, and scaling, this architecture could easily handle thousands of verifications per second.</p>
<p>The coolest part? <strong>Self’s zero-knowledge proofs</strong> mean users can prove they’re real humans without doxxing themselves. No centralized identity database, no honeypot of personal data. Just cryptographic proof of humanity. The Self team has done an incredible job making privacy-preserving identity verification accessible to developers.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Why Every Indie Hacker Should Read Million Dollar Weekend]]></title><description><![CDATA[One Quiet Evening
I remember a quiet evening, laptop open, VS Code still running from earlier in the day. A half-finished side project stared back at me, and a cup of chai brewed beside the keyboard. I was stuck in that familiar limbo between ideas –...]]></description><link>https://writer.mrmehta.in/million-dollar-weekend</link><guid isPermaLink="true">https://writer.mrmehta.in/million-dollar-weekend</guid><category><![CDATA[motivation]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 25 Jul 2025 18:46:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753464818182/de156877-428c-46c5-92de-0d2d4ac51e68.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-one-quiet-evening">One Quiet Evening</h1>
<p>I remember a quiet evening, laptop open, VS Code still running from earlier in the day. A half-finished side project stared back at me, and a cup of chai brewed beside the keyboard. I was stuck in that familiar limbo between ideas – too many GitHub repos collecting dust, not enough <em>shipping</em>. On a whim, I cracked open <a target="_blank" href="https://www.flipkart.com/million-dollar-weekend/p/itm49a35350e7494">Million Dollar Weekend</a>. I didn’t know what to expect. A get-rich-quick gimmick? Instead, I found a mirror. Page after page, it felt like <a target="_blank" href="https://noahkagan.com/">Noah Kagan</a> (the author) was peering over my shoulder at my unlaunched projects and saying, “So, are you gonna build something <strong>or what</strong>?”</p>
<p>As a developer who’s always seeking ways to break through self-imposed limits – to scale not just systems but my own life – this book hit different. It wasn’t a generic <strong>“hustle harder”</strong> mantra. It was more like a candid code review for my mindset. In the same way we optimize code by finding bottlenecks, <strong>Million Dollar Weekend</strong> started pointing out the mental bottlenecks that kept me from deploying my ideas. One chapter in, and I already felt like I was having a late-night coffee with a senior dev who’s giving me the tough love I didn’t know I needed.</p>
<h1 id="heading-the-developer-struggle-is-real">The Developer Struggle is Real</h1>
<p>Before I dive into the book’s lessons, let me confess some of my own bugs <strong>(you might recognise a few in your own system)</strong>:</p>
<ul>
<li><p><strong>Perfectionism</strong> – I’d polish a side project endlessly, telling myself <em>“just one more feature and it’ll be launch-ready.”</em> Spoiler: “one more feature” was a never-ending loop.</p>
</li>
<li><p><strong>Shiny Tech Syndrome</strong> – Guilty. I’d jump between the latest JS framework or cloud tool, convinced that <em>this</em> would make my project 10x better. In reality, it just reset my progress to 0.</p>
</li>
<li><p><strong>Too Many Unfinished Projects</strong> – My <code>/projects</code> folder is a graveyard of ideas that never saw the light of day. I’ve started things that never even made it to a 0.1 release because I kept waiting to make them perfect.</p>
</li>
<li><p><strong>Impostor Syndrome</strong> – The voice that says, “Who am I to launch this? There are better developers out there.” So I’d over-prepare and overthink as a stalling tactic.</p>
</li>
<li><p><strong>Fear of Public Launch</strong> – Let’s be honest: hitting “Publish” or opening a repo can be scarier than <code>rm -rf /</code>. What if no one cares? Or worse, what if people do care and judge it?</p>
</li>
</ul>
<p>Reading <strong>Million Dollar Weekend</strong> was like having these struggles refactored in front of me. Kagan doesn’t just identify these traps – he calls them out with no fluff. The need for more research, more polish, more <em>anything</em> before starting is just another form of procrastination that <strong>paralyses</strong> would-be creators. That hit hard, because I saw myself in that line. All my elaborate planning and learning was often just a way to delay the real test: <strong>putting something out into the world</strong>.</p>
<h1 id="heading-why-developers-should-read-it">Why Developers Should Read It?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753466080369/adbff262-7fba-4f46-8985-a65946b4e4cf.png" alt class="image--center mx-auto" /></p>
<p>If you write code, you know the thrill of seeing a program run successfully. Now imagine getting that thrill from seeing an <strong>idea</strong> run in real life. That’s what this book is about – moving from code to outcome, from idea to impact. As developers, we’re great at tinkering in code, but we sometimes forget that <strong>no one really cares about our code until it solves their problem</strong>. Kagan drives this home:</p>
<blockquote>
<p><em>Customers don't care about your ideas; they care about whether you can solve their problems.</em></p>
</blockquote>
<p><strong>In dev-speak:</strong> no one’s starring your GitHub repo until it actually helps them in some way.</p>
<p>This book matters for devs because it reframes action as something we can <strong>engineer</strong>. It treats taking action like building a feature – something you can learn, iterate on, and improve. Instead of mystical “entrepreneurial spirit,” it gives you a playbook (almost like pseudocode) for going from zero to one. It’s full of insights that translate surprisingly well to our world:</p>
<ul>
<li><p><strong>“Now, Not How” Mindset:</strong> Stop over-engineering the solution in your head; build a quick prototype of your life. We’d never write a full system without a single test run, so why live that way?</p>
</li>
<li><p><strong>User Feedback &gt; Theoretical Perfect Product:</strong> Just like we need user testing, you need to test your ideas with real people <em>now</em>, not after you’ve perfected them. A crappy command-line tool that solves someone’s pain is worth more than a polished app no one needs.</p>
</li>
<li><p><strong>Overcoming Excuses:</strong> The book systematically knocks down excuses (no time, no money, not expert enough – you name it). Reading it, I felt a bit called out and a lot inspired. For every excuse, there was a counter:</p>
<ul>
<li><p>Don’t have time? Prioritise and make time.</p>
</li>
<li><p>Worried it’s not perfect? Good – ship it anyway and learn as you go.</p>
</li>
<li><p>Afraid to put yourself out there? Others have felt that too, and pushed past it.</p>
</li>
</ul>
</li>
</ul>
<p>In short, <strong>Million Dollar Weekend</strong> is the kick in the pants that us chronically hesitant developers need. It’s the reminder that our skills don’t fully matter until we <strong>apply</strong> them to something real, something that reaches people. And Kagan delivers this message in a way that feels like your tech friend who’s a bit ahead in the game giving you frank advice over coffee.</p>
<h1 id="heading-what-you-actually-need-to-launch">What You Actually Need to Launch</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753466548378/414ebbb8-0624-43ce-a207-a85e254a7f98.png" alt class="image--center mx-auto" /></p>
<p>One theme that resonated with me deeply: <strong>having the code is not enough; you need the courage to launch it</strong>. I’ve spent years accumulating programming knowledge, learning frameworks, refining my coding style – thinking that if I just code well enough, success will follow. But the hardest algorithm to optimiSe was my own fear of launching.</p>
<blockquote>
<p>Kagan’s blunt motto in the book is <strong>“</strong>Just f***ing start.” It’s literally the title of Chapter 1, Begin Before You Are Ready.</p>
</blockquote>
<p>That unapologetic push to take the leap hit me like a code review comment in all caps. He argues – and I’m now convinced – that the winners aren’t the ones with perfect code, but the ones who put something out there.</p>
<p>For us devs, this is like deploying to prod on day one. Terrifying? Yes. But you learn more from one deployment (even if it fails) than from a year of local builds. <strong>Code vs. Courage</strong> means I had to confront a tough truth: I wasn’t missing a framework or a feature, I was missing guts. The courage to hit “Launch” when my inner voice screamed it wasn’t ready. The courage to ask someone to try my app, or to charge for a <a target="_blank" href="https://www.thedaotool.com">tool I made</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/thedaotool">https://github.com/kartikmehta8/thedaotool</a></div>
<p> </p>
<p><strong>One of the most empowering realisations was that courage is a skill you build.</strong> The book suggests treating it like a muscle – you strengthen it by reps. Say, by doing small bold acts regularly. (Kagan even recommends wild exercises like the “Coffee Challenge” – asking a barista for a discount, just to practice being okay with rejection and awkwardness. Talk about stepping out of the comfort zone!). The specifics might sound silly, but the principle is gold: if you push your comfort zone in little ways, you’ll have the nerve to tackle the big stuff, like launching that app or startup you've been dreaming about.</p>
<p>Bottom line: <strong>If you have 100 side-projects worth of code but zero that are public, it might be time to focus less on code and more on courage.</strong> I’m saying this to myself as much as to anyone else. This book made me realise that my repository of unfinished projects wasn’t due to lack of coding time or talent – it was a lack of courage to share them with the world.</p>
<h1 id="heading-breaking-the-loop">Breaking the Loop</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753467579022/4a94aeb8-8260-4ced-82a9-f5065d0e749e.png" alt class="image--center mx-auto" /></p>
<p>Ever find yourself in an infinite loop of <strong>“think, plan, think, plan”</strong> and never actually execute? That was me. I’d map out grand architectures in my head, sketch feature roadmaps, compare tech stacks… and stall. It’s like writing a 10-page design document and never writing the actual code. <strong>Million Dollar Weekend</strong> basically yelled at me (in a friendly way) to break out of that loop. The cure? <strong>Action. Fast, imperfect action.</strong></p>
<p>One of the book’s core lessons is that <strong>speed is clarity</strong>. When you move quickly, you force clarity upon yourself. It’s akin to how in software development a rapid prototype teaches you more in a day than weeks of theoretical spec-writing. There’s a line in the book that essentially says:</p>
<blockquote>
<p><em>Start before you feel ready, because action leads to learning and growth</em>. Or as I like to rephrase it, <em>“Ship it, and you’ll figure it out as you go.”</em></p>
</blockquote>
<p>I had to admit, whenever I did hackathons or quick-and-dirty prototypes, I learned a ton – way more than when I spent forever reading docs without coding. Yet, with my own ideas I was doing the opposite: endless overthinking. The book pushed me to treat my projects more like a hackathon and less like a moon landing. <strong>Shipping fast breaks analysis paralysis.</strong> Once something is out there, you get actual feedback (users, errors, etc.), and that feedback is worth dozens of hypothetical scenarios.</p>
<p>A great example from <strong>Million Dollar Weekend</strong> is the idea of validating an idea in 48 hours:</p>
<blockquote>
<p>If you can find a few people willing to pay for your concept in a weekend, you’re onto something.</p>
</blockquote>
<p>This forced timeline is genius because you don’t have time to overthink — you have to strip the idea to its core value and act. I realised I could apply this to side projects: instead of planning every feature, what if I challenged myself to get <strong>one</strong> real user or <strong>one</strong> functional demo by the end of the weekend? That kind of constraint cuts through a lot of BS.</p>
<p>So how do we break the overthinking loop as devs? Here’s what I’m doing now, inspired by the book: whenever I catch myself spiraling into “I should research a bit more before I start coding this idea,” I stop and ask, “What’s the smallest test I can run <em>right now</em>?” Maybe it’s a 10-minute ugly prototype. Maybe it’s emailing a friend who might be the target user to ask if they’d even want this. Anything that produces a real outcome beats another evening of <strong>theory-crafting</strong>. It’s uncomfortable for sure – shipping something half-baked feels against our developer instincts – but I remind myself that <strong>an MVP in the wild beats a perfect plan on paper</strong> every time.</p>
<h1 id="heading-the-psychology-of-action">The Psychology of Action</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753467898199/8cb0f65c-a106-417d-9202-51f3b26127ea.png" alt class="image--center mx-auto" /></p>
<p>After years of being stuck in my habits, I realised I needed to rewire how my brain approaches taking action. We’re used to debugging our code; this was about debugging my mindset. <strong>Million Dollar Weekend</strong> digs into the psychology of why we hesitate and how to fix it in a very pragmatic way.</p>
<p>One concept that struck me was treating <strong>rejection and failure as mere signals, not stop signs.</strong> As a developer, I’m used to compilers spitting out errors – I don’t quit coding when I see errors; I treat them as feedback to fix the code. What if I approached life and projects the same way? The book actually suggests setting <strong>“rejection goals”</strong> – like aiming to get a certain number of no’s or failures under your belt. At first, that sounds insane. Why would I <em>want</em> to fail or get rejected? But then it clicked: if you’re not hitting any rejections, you’re probably not pushing your limits or asking for much. <strong>It reframes a “no” from the world as progress – you’re one attempt closer to a yes.</strong></p>
<p>For me, this hit home in the context of launching projects. I feared putting something out there because it might flop or people might criticise it. But Kagan’s approach is:</p>
<blockquote>
<p><strong>Expect</strong> some flops, even <strong>welcome</strong> them. They’re part of the process. He even reminds us that <em>“experiments are supposed to fail”</em><a target="_blank" href="https://sobrief.com/books/million-dollar-weekend#:~:text=%2A%20,and%20a%20valuable%20learning%20opportunity">[8]</a> – that’s how you learn.</p>
</blockquote>
<p>Reading that was oddly comforting. It’s like being given permission to mess up, as long as you <strong>mess up while trying</strong>. This is basically how we debug code; we run it, see it break, learn, and iterate. Why not apply the same iterative mindset to building a startup or launching a side project?</p>
<p>Another psychological barrier the book helped me with is <strong>imposter syndrome.</strong> It doesn’t address it by name, but the ethos is everywhere: stop worrying about being the best or an “expert” and just help someone with what you know. It dawned on me that every time I delayed launching something due to self-doubt, I was making it about me (how I’d be perceived) rather than about whoever could benefit from my app or tool. By flipping that mindset – focusing on the people I could help or the problem I could solve – action becomes easier. You’re not putting yourself on stage to be judged; you’re delivering value, and feedback (even negative) just tells you how to deliver more or better value next time.</p>
<p>In practical terms, I started doing little things to rewire my “dev brain” for action:</p>
<ul>
<li><p><strong>Tiny Acts of Boldness:</strong> Like pushing code to a public repo even if it’s not perfect, or <strong>tweeting</strong> about an idea before it’s fully formed to gauge interest. Each time, it gets a bit easier.</p>
</li>
<li><p><strong>Gamifying Action:</strong> I kind of make a game out of it now. e.g., “Can I get <strong>5 rejections</strong> this month from pitching my project to people?” Sounds weird, but if I hit 5, it means I actually asked at least 5 people (which is 5 more than if I stayed shy). And who knows, one of those might be a yes.</p>
</li>
<li><p><strong>Communities and Accountability:</strong> I told a few dev friends about the book and my goal to launch something by a certain date. Just like a stand-up meeting keeps you accountable on code, having peers know your goals helps you stick to them. We’re even considering a mini “ship it” challenge together.</p>
</li>
</ul>
<p><strong>Rewiring our mindset is like refactoring a legacy codebase</strong> – it takes effort, and the old patterns (habits, fears) are still lurking ready to cause regression. But with repetition, the new patterns start to stick. I’m finding that the more I practice this action-oriented approach, the more natural it feels. It’s oddly liberating to treat life a bit more like a coding experiment: try things, expect bugs, fix forward.</p>
<h1 id="heading-my-next-steps-an-honest-reflection">My Next Steps (An Honest Reflection)</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753468580235/9f1b05b0-d33d-4640-907f-bbb6468527a1.png" alt class="image--center mx-auto" /></p>
<p>I finished <strong>Million Dollar Weekend</strong> feeling a mix of fired-up excitement and <em>“why have I been like this for years?!”</em> frustration with myself. But I’ll take that over feeling stuck any day. <strong>The real question is: what am I going to do differently now?</strong> It’s easy to feel motivated after reading a book; the trick is to <strong>act</strong> on it. So here’s my plan, laid out like a dev roadmap, and a real example I’m already working on:</p>
<h3 id="heading-1-embracing-now-not-how">1. Embracing “Now, Not How”</h3>
<p>I’ve printed a little sticky note for my monitor with <strong>“NOW ➜ not HOW”</strong>. This is to remind me to act first, then figure it out. For instance, instead of spending a week choosing the perfect tech stack, I just used what I know best and built the core in a day. Surprise, it works! The old me would have spent that day reading about which database scales better for when I have a million users (ha!). The new me is like, “Let’s worry about a million users when we have, say, 10.” This shift is already making me more productive.</p>
<h3 id="heading-2-seeking-feedback-early">2. Seeking Feedback Early</h3>
<p>I’m pushing myself to share early drafts and prototypes for feedback. Whether it’s a blog post (like this one!) or a code snippet, I’m sending it out sooner. It’s a bit nerve-wracking, but here’s what I found: people are generally supportive, and their suggestions are gold. It’s like code review – scary before you do it, incredibly helpful after you get the comments.</p>
<h3 id="heading-3-staying-accountable">3. Staying Accountable</h3>
<p>I roped in a fellow developer friend as an accountability partner. We check in weekly on our progress – what did we ship this week, what rejections or learnings did we get, what’s the plan for next week. This mirrors one of the book’s tips about having an accountability partner to keep you on track. It’s amazing how knowing you have to report progress can push you to avoid slacking. It’s like pair programming for productivity. (Don’t find one? <a target="_blank" href="https://www.mrmehta.in/">HMU</a>)</p>
<h3 id="heading-4-continuous-deployments-to-life">4. Continuous “Deployments” to Life</h3>
<p>Ultimately, I want to treat my personal growth and projects like an agile project – continuous integration and deployment. Rather than one big reveal, I’ll try to make incremental improvements or releases regularly. Write that blog post series one part at a time. Roll out a new feature to my app every few days. Basically, keep the ball rolling so momentum keeps building.</p>
<p>Finally, I want to say this: If you’re a developer reading this and any of it felt uncomfortably familiar, consider this your invitation (or challenge) to shake things up. <strong>Million Dollar Weekend</strong> is worth your time, but only if you’re willing to do something with what it teaches. Don’t just read it and nod; use it as fuel. Start that project <strong>today</strong>, even if it’s clunky. Email that potential user or mentor <strong>now</strong>, even if you’re afraid they’ll say no. Push your work out of the cozy development branch and into production – <em>your life’s production environment</em>.</p>
<p>I’m done waiting for “perfect” and over-planning in the shadows. The next chapter for me is all about <strong>action</strong>. As I close this out (and push it live, gulp), I’ll leave you with the same question I had to ask myself:</p>
<p><strong>What will you ship by next weekend?</strong></p>
<p>Go on, grab that idea and give it a go. I’ll be cheering for you – and more importantly, I’ll be coding and launching right alongside you. <strong>Happy shipping!</strong> 🚀</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[The "Why" of Kubernetes]]></title><description><![CDATA[Yes, I've always wanted to learn more about Kubernetes. I've had a long-standing passion for it. However, what is the reality? The inner workings of the system, including the pods, services, DNS, and how these tiny, unseen components enable large sof...]]></description><link>https://writer.mrmehta.in/the-why-of-kubernetes</link><guid isPermaLink="true">https://writer.mrmehta.in/the-why-of-kubernetes</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Flask Framework]]></category><category><![CDATA[Redis]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sun, 13 Jul 2025 12:30:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752402206364/a7ac8a01-5d5d-439e-9824-92e98e57655b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Yes, I've always wanted to learn more about Kubernetes. I've had a long-standing passion for it. However, what is the reality? The inner workings of the system, including the pods, services, DNS, and how these tiny, unseen components enable large software to function like magic, have always captivated me more.</p>
<p>So this isn’t “<em>just another Kubernetes guide.</em>”</p>
<p>This is me, as a backend developer, sharing my journey of <strong>why each YAML line matters</strong> and how it fits together when you’re connecting a Python app to Redis inside Kubernetes.</p>
<h2 id="heading-why-i-even-did-this-context-for-you-if-you-are-like-me"><strong>Why I Even Did This (Context for You if You Are Like Me)</strong></h2>
<p>I did this because:</p>
<ol>
<li><p>Instead of merely deploying from ChatGPT or AI responses, I wanted to fully comprehend Kubernetes.</p>
</li>
<li><p>I was interested in the communication between pods of a basic application using Redis (any database).</p>
</li>
</ol>
<p>That's all. No fancy objective. Just the curiosity of a backend nerd. :)</p>
<h2 id="heading-installing-kubernetes-minikube"><strong>Installing Kubernetes (Minikube)</strong></h2>
<p>If you’re starting from scratch, here’s how I did it:</p>
<h3 id="heading-install-minikube-local-kubernetes">Install Minikube (Local Kubernetes)</h3>
<pre><code class="lang-bash">brew install minikube
</code></pre>
<h3 id="heading-install-kubectl-kubernetes-cli">Install kubectl (Kubernetes CLI)</h3>
<pre><code class="lang-bash">brew install kubectl
</code></pre>
<h3 id="heading-start-minikube">Start Minikube</h3>
<pre><code class="lang-javascript">minikube start
</code></pre>
<h3 id="heading-test">Test:</h3>
<pre><code class="lang-javascript">kubectl get nodes
</code></pre>
<p>You're all set if you see <code>minikube</code> Ready. This is the <a target="_blank" href="https://minikube.sigs.k8s.io/docs/handbook/troubleshooting">troubleshooting guide</a> if not.</p>
<h2 id="heading-why-should-i-care-about-kubernetes-as-a-backend-developer"><strong>Why Should I Care about Kubernetes as a Backend Developer?</strong></h2>
<p>You are undoubtedly already aware of the hype surrounding Kubernetes if you are reading this. Let's face it, though: Kubernetes isn't some magical device. Fundamentally, it is merely <strong>a system for safely running and managing your apps in containers, whether you have one app or a thousand</strong>.</p>
<p>Consider Kubernetes to be the <strong>babysitter</strong> for your app. It manages how apps locate and communicate with one another, keeps things running, and restarts things when they malfunction. No-fluff features:</p>
<ul>
<li><p><em>Scalability:</em> One instance or 100 across machines can be run with ease.</p>
</li>
<li><p><em>Self-healing:</em> Kubernetes automatically revives your app if it crashes.</p>
</li>
<li><p><em>Networking:</em> Makes app-to-app communication easier.</p>
</li>
<li><p><em>Portability:</em> It can operate on any platform, including Google Cloud, AWS, and your laptop.</p>
</li>
<li><p><em>Declarative Infrastructure:</em> You specify your needs (in YAML), and Kubernetes determines how to fulfil them.</p>
</li>
</ul>
<h2 id="heading-basic-terms-you-keep-seeing-in-yaml"><strong>Basic Terms You Keep Seeing in YAML</strong></h2>
<p><img src="https://substackcdn.com/image/fetch/$s_!PmQf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0706124-c9ce-4e44-91c6-fe6c51c8ad4d_1600x1087.png" alt="Kubernetes Made Easy: A Beginner's Roadmap to Container Orchestration" class="image--center mx-auto" /></p>
<p>(Image Credits: <a target="_blank" href="https://blog.bytebytego.com/p/kubernetes-made-easy-a-beginners">ByteByteGo</a>)</p>
<ol>
<li><p><strong>Pod:</strong> Smallest deployable unit. Think: 1 running container, or sometimes a small group sharing resources.</p>
</li>
<li><p><strong>Deployment:</strong> Defines <strong>how many pods</strong> you want, what image to run, and keeps them alive.</p>
</li>
</ol>
<p>Example from our YAML:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">replicas:</span> <span class="hljs-number">2</span>
</code></pre>
<p>Kubernetes will guarantee precisely two pod runs. if it passes away? automatically changed. There is a comprehensive YAML file in the following section, so don't worry.</p>
<ol start="3">
<li><strong>Service:</strong> A <strong>stable network address</strong> for your pods. Even if pods restart or change, services keep your app reachable.</li>
</ol>
<p>Example:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">redis-service</span>
</code></pre>
<p>This allows your backend to always find Redis, even if the Redis pod changes under the hood.</p>
<h2 id="heading-what-i-referred"><strong>What I referred?</strong></h2>
<p>Nana’s one-shot video gave me a clear understanding of how Kubernetes works under the hood. I referred to it and got a clear picture of every aspect of Kubernetes that I should know as a backend developer. I’d love for you to hold on to this blog, check out the video, and then come back later. :)</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=X48VuDVv0do">https://www.youtube.com/watch?v=X48VuDVv0do</a></div>
<p> </p>
<hr />
<p><strong>Note from me (Backend Dev to Backend Dev):</strong><br />These aren’t just terms. They’re how you control your infra with YAML, not manual commands.<br />Once you understand Pods, Deployments, and Services, you understand 80% of Kubernetes workflows.</p>
<h2 id="heading-docker-vs-minikube-docker"><strong>Docker vs Minikube Docker</strong></h2>
<p><strong>Why this matters:</strong><br />Your Mac’s Docker and Minikube’s Docker are separate. Kubernetes doesn’t care about your host machine’s images unless you explicitly build inside Minikube’s Docker.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">eval</span> $(minikube docker-env) <span class="hljs-comment"># Do this!</span>
</code></pre>
<p>Now you are building <strong>inside Minikube</strong>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># You will get the contxt for this command, hold on!</span>
<span class="hljs-comment"># Run this inside /backend folder.</span>
docker build -t backend:latest .
</code></pre>
<h2 id="heading-project-structure"><strong>Project Structure</strong></h2>
<p>So this is the project structure:</p>
<pre><code class="lang-bash">.
├── backend/                    <span class="hljs-comment"># Flask Application.</span>
│   ├── app.py                  <span class="hljs-comment"># Entry Point for Flask Application.</span>
│   ├── requirements.txt        <span class="hljs-comment"># Python Dependencies.</span>
│   └── Dockerfile              <span class="hljs-comment"># Dockerfile to Containerize Flask Application.</span>
└── kubernetes/
    ├── backend-deployment.yaml <span class="hljs-comment"># Deployment for Flask Application.</span>
    ├── backend-service.yaml    <span class="hljs-comment"># Service to Expose Flask Application.</span>
    ├── redis-deployment.yaml   <span class="hljs-comment"># Deployment for Redis DB.</span>
    └── redis-service.yaml      <span class="hljs-comment"># Service to Expose Redis DB.</span>
</code></pre>
<p>If you need the code, it is here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/k8-flask-basic">https://github.com/kartikmehta8/k8-flask-basic</a></div>
<p> </p>
<h2 id="heading-backend-code"><strong>Backend Code</strong></h2>
<p>A basic Flask app talking to Redis. Nothing fancy, just enough to understand Kubernetes.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request
<span class="hljs-keyword">import</span> redis
<span class="hljs-keyword">import</span> os

app = Flask(__name__)
redis_host = os.getenv(<span class="hljs-string">'REDIS_HOST'</span>, <span class="hljs-string">'localhost'</span>)
redis_port = int(os.getenv(<span class="hljs-string">'REDIS_PORT'</span>, <span class="hljs-number">6379</span>))

r = redis.Redis(host=redis_host, port=redis_port, decode_responses=<span class="hljs-literal">True</span>)

<span class="hljs-meta">@app.route('/set', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">set_value</span>():</span>
    key = request.json.get(<span class="hljs-string">'key'</span>)
    value = request.json.get(<span class="hljs-string">'value'</span>)
    r.set(key, value)
    <span class="hljs-keyword">return</span> {<span class="hljs-string">'status'</span>: <span class="hljs-string">'ok'</span>}

<span class="hljs-meta">@app.route('/get/&lt;key&gt;')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_value</span>(<span class="hljs-params">key</span>):</span>
    value = r.get(key)
    <span class="hljs-keyword">return</span> {<span class="hljs-string">'key'</span>: key, <span class="hljs-string">'value'</span>: value}

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5000</span>)
</code></pre>
<p>and a Dockerfile,</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Use the official Python 3.11 slim image as the base.</span>
<span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.11</span>-slim

<span class="hljs-comment"># Set the working directory inside the container to /app.</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-comment"># Copy all files from the current directory to /app in the container.</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-comment"># Install Python dependencies from requirements.txt.</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install -r requirements.txt</span>

<span class="hljs-comment"># Define the default command to run the Flask app.</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"python"</span>, <span class="hljs-string">"app.py"</span>]</span>
</code></pre>
<h2 id="heading-the-why-of-each-yaml-line">The “Why” of Each YAML Line</h2>
<p>Yes, YAML looks scary at first. I used to feel the same.<br />But once you read it like <strong>"what is this telling Kubernetes to do for me?"</strong>, it becomes super logical.</p>
<p>Let me break it down line-by-line for both <strong>Redis</strong> and <strong>Backend</strong>.</p>
<h3 id="heading-redis-deployment">Redis Deployment</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
</code></pre>
<p><strong>Inform Kubernetes of the API version you use for deployments.</strong><br />The standard for deployments is <code>apps/v1</code>. Kubernetes won't even understand what you're attempting to explain without this.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
</code></pre>
<p><strong>This tells Kubernetes: "I want you to manage pods for me."</strong><br />A Deployment keeps pods running, replaces them if they crash, and scales them up or down.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">redis</span>
</code></pre>
<p><strong>Human-friendly name for this deployment.</strong><br />Later when I run <code>kubectl get deployments</code>, this name shows up.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
</code></pre>
<p><strong>How many copies (pods) do I want?</strong><br />Here, just 1. For learning, that’s enough. If Redis crashes, Kubernetes will recreate it.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">selector:</span>
  <span class="hljs-attr">matchLabels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">redis</span>
</code></pre>
<p>The question, "<strong>Which pods does this deployment manage?</strong>" must be answered by Kubernetes.<br />The link is labels. For this deployment to manage pods, they must match this label.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">template:</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">redis</span>
</code></pre>
<p><strong>These labels are what actually get stamped onto the pods.</strong><br />The <code>selector</code> above looks for this. <strong>(IMPORTANT)</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">redis</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">redis:7</span>
</code></pre>
<p><strong>This runs the official Redis Docker image.</strong><br />No magic, no AI, no abstraction. This is literally pulling Redis from Docker Hub and running it.</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">6379</span>
</code></pre>
<p><strong>Tells Kubernetes this container listens on 6379 inside the pod.</strong><br />Later, Services will use this info to direct traffic properly.</p>
<h3 id="heading-redis-service">Redis Service</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
</code></pre>
<p>Standard API version for Services.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
</code></pre>
<p>This is saying: <strong>“Expose this pod internally via stable networking.”</strong><br />Otherwise, pods restart and change names/IPs constantly.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">redis-service</span>
</code></pre>
<p>This becomes the <strong>internal DNS</strong> name: Your backend will call <code>redis-service:6379</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">redis</span>
</code></pre>
<p><strong>Connect this service to pods labeled</strong> <code>app: redis</code>.<br />That’s how Kubernetes knows which pod to route traffic to.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">6379</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">6379</span>
</code></pre>
<p>Exposes <strong>6379 inside the cluster</strong>.<br />Traffic on this Service’s port 6379 goes to the Redis pod’s 6379.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span>
</code></pre>
<p><strong>Internal-only networking.</strong><br />No exposure to the outside world. Only pods inside Kubernetes can reach this Redis.</p>
<h3 id="heading-backend-deployment">Backend Deployment</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">backend</span>
</code></pre>
<p>Same logic as Redis. Just this time it’s <strong>our Python application</strong>. :)</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
</code></pre>
<p>One instance for now. In production, this might be 3+ for resilience.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">selector:</span>
  <span class="hljs-attr">matchLabels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">backend</span>
<span class="hljs-attr">template:</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">backend</span>
</code></pre>
<p>Same as Redis: Deployment looks for pods with <code>app: backend</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">containers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">backend</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">backend:latest</span> <span class="hljs-comment"># The command you ran in the previous section inside backend folder.</span>
    <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">Never</span>
</code></pre>
<p>This runs <strong>our Docker image built locally in Minikube.</strong><br /><code>imagePullPolicy: Never</code> forces Kubernetes to use our local image, not look for it on Docker Hub.</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">5000</span>
</code></pre>
<p>Our Flask app listens on <strong>5000 inside the pod</strong>.</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">env:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">REDIS_HOST</span>
      <span class="hljs-attr">value:</span> <span class="hljs-string">redis-service</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">REDIS_PORT</span>
      <span class="hljs-attr">value:</span> <span class="hljs-string">"6379"</span>
</code></pre>
<p><strong>Environment variables inside the container.</strong><br />Our Python code uses <code>REDIS_HOST</code> to find Redis through Kubernetes DNS: <code>redis-service:6379</code>.</p>
<h3 id="heading-backend-service">Backend Service</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">backend-service</span>
</code></pre>
<p>Service name we will port-forward to.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">backend</span>
</code></pre>
<p>Routes traffic to pods labeled <code>app: backend</code>.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">5000</span>
</code></pre>
<p>Exposes <strong>80 inside the cluster</strong>, forwards traffic to <strong>5000 on the pod</strong>.<br /><strong>Why?</strong> Because most HTTP tools assume port 80 unless told otherwise.</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span>
</code></pre>
<p>Again, internal-only networking in the cluster.</p>
<p><strong>But I want to test this locally. So I run:</strong></p>
<pre><code class="lang-bash">kubectl port-forward svc/backend-service 8000:80
</code></pre>
<p>Now <a target="_blank" href="http://localhost:8000"><code>localhost:8000</code></a> connects to Kubernetes Service → Pod.</p>
<h2 id="heading-run-the-cluster">Run the Cluster</h2>
<p>In the project root directory, run the following commands:</p>
<pre><code class="lang-bash">kubectl apply -f kubernetes/redis-deployment.yaml
kubectl apply -f kubernetes/redis-service.yaml
kubectl apply -f kubernetes/backend-deployment.yaml
kubectl apply -f kubernetes/backend-service.yaml
</code></pre>
<h2 id="heading-big-picture">Big Picture</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752408792202/e4e76bd7-1574-4de0-8da8-df37271c318b.png" alt class="image--center mx-auto" /></p>
<p>This simple flow explains exactly how traffic moves inside Kubernetes when you run this project. When you access <a target="_blank" href="http://localhost:8000"><code>localhost:8000</code></a>, Kubernetes uses <strong>port-forwarding</strong> to route your request to the <code>backend-service</code> on port <code>80</code>. That service knows how to find the <code>backend-pod</code>, where your Python Flask app is running on port <code>5000</code>. When your Flask app needs to access Redis, it doesn't use IPs — it simply calls <code>redis-service:6379</code>, and Kubernetes handles the networking behind the scenes. The <code>redis-service</code> then forwards traffic to the <code>redis-pod</code> running the Redis server.</p>
<p>This is how Kubernetes DNS and services keep everything connected cleanly.</p>
<h2 id="heading-final-thoughts"><strong>Final Thoughts</strong></h2>
<p>Congratulations if you've made it this far. <strong>Most backend developers keep putting off doing this because Kubernetes seems "too big," "too enterprise," or "too confusing."</strong>  </p>
<p>While developing this small Python + Redis project on Kubernetes, I came to the straightforward conclusion that: <strong>Kubernetes is not difficult. It's merely methodical</strong>.  </p>
<p>There is a purpose behind each YAML line. Each deployment, pod, and service fits together like a puzzle.<br />You cease to be afraid of the larger picture once you comprehend these little details.<br />Kubernetes is no longer seen as "DevOps magic," but rather as a potent instrument that allows you to manage the infrastructure of your applications.  </p>
<p>And truthfully?<br />That is the most fulfilling feeling a backend developer can have.<br /><strong>You are aware of what is operating, how it operates, and why it functions.</strong></p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[No Bottlenecks. Just SQS.]]></title><description><![CDATA[When building modern applications, we often encounter a dilemma:

Should we process tasks immediately within the API call?

Or offload them and return quickly?


As your application scales, tasks like sending emails, processing payments, resizing ima...]]></description><link>https://writer.mrmehta.in/no-bottlenecks-just-sqs</link><guid isPermaLink="true">https://writer.mrmehta.in/no-bottlenecks-just-sqs</guid><category><![CDATA[AWS SQS]]></category><category><![CDATA[SQS]]></category><category><![CDATA[AWS]]></category><category><![CDATA[queue]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sat, 12 Jul 2025 18:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743509216021/abebaaad-ad77-4f15-a046-170f829a7dd4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When building modern applications, we often encounter a dilemma:</p>
<ul>
<li><p>Should we process tasks immediately within the API call?</p>
</li>
<li><p>Or offload them and return quickly?</p>
</li>
</ul>
<p>As your application scales, tasks like sending emails, processing payments, resizing images, or syncing data can introduce latency and risk. That’s where <strong>queues</strong> shine — by allowing systems to decouple work and handle it <strong>asynchronously</strong>.</p>
<h2 id="heading-what-is-a-queue-in-software">What is a Queue in Software?</h2>
<p>A <strong>queue</strong> is a data structure that follows <strong>FIFO</strong> (First-In, First-Out) — the first item added is the first to be removed.</p>
<p>In distributed systems:</p>
<ul>
<li><p>A <strong>Producer</strong> adds jobs/tasks/messages to the queue.</p>
</li>
<li><p>One or more <strong>Consumers</strong> process those messages independently.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1382/0*TYGDLcBNJM49PpPV" alt="Distributed Messaging Queue: Everything You Need to Know About | by Arslan  Ahmad | Geek Culture | Medium" /></p>
<p>This separation improves responsiveness, fault tolerance, and scalability.</p>
<h3 id="heading-why-queues-are-critical-in-modern-architectures">Why Queues Are Critical in Modern Architectures</h3>
<ol>
<li><p><strong>Decoupling Components:</strong> Queues allow services to operate independently. (Ex, If the email service is slow, the signup API still works.)</p>
</li>
<li><p><strong>Improved Resilience:</strong> If a consumer crashes, the queue persists tasks until it's back.</p>
</li>
<li><p><strong>Auto-Scaling:</strong> You can increase consumers to process high loads dynamically.</p>
</li>
<li><p><strong>Retry &amp; Error Handling:</strong> Failed jobs can be retried or moved to a Dead Letter Queue (DLQ).</p>
</li>
<li><p><strong>Rate Limiting at Scale:</strong> You can control how fast background jobs are processed, avoiding downstream overload.</p>
</li>
</ol>
<h2 id="heading-why-choose-aws-sqs-as-your-queueing-system">Why Choose AWS SQS as Your Queueing System?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744527503375/2a8bb9c9-34e3-4b84-833f-40ce0267855f.png" alt class="image--center mx-auto" /></p>
<p>Amazon SQS stands out as a robust, fully managed message queuing service that eliminates the overhead of managing message brokers and scaling infrastructure manually. Designed for durability, flexibility, and performance, SQS offers features that make it ideal for both startups and enterprise-grade applications. Its native integration with the AWS ecosystem (like Lambda, SNS, and CloudWatch) allows for powerful event-driven architectures. You don’t have to worry about provisioning servers or handling failover — SQS scales automatically with your workload.</p>
<p>Features like visibility timeouts, dead-letter queues, long polling, and encryption make it a secure and intelligent choice when building modern, asynchronous backend systems. Amazon SQS (Simple Queue Service) is a fully managed message queuing service that:</p>
<ul>
<li><p>Scales automatically</p>
</li>
<li><p>Offers at-least-once delivery</p>
</li>
<li><p>Works seamlessly with other AWS services</p>
</li>
</ul>
<p><img src="https://a.b.cdn.console.awsstatic.com/a/v1/XKO4B6QDVFJTQWTL2XED4H2RRKZ73K7O4MYWLEE2XWGFYJ53GTLA/assets/images/55fc40afe501ed4ed4ed-SQS-diagram.svg" alt="Diagram detailing how SQS works." /></p>
<p>Amazon SQS allows producers to send messages to a queue. Messages are then stored in an SQS Queue. When consumers are ready to process new messages they poll them from the queue. Applications, micro-services, and multiple AWS services can take the role of producers or consumers.</p>
<h3 id="heading-benefits-and-features-of-sqs">Benefits and features of SQS</h3>
<ol>
<li><p><strong>Highly scalable Standard and FIFO queues:</strong> Queues scale elastically with your application. Nearly unlimited throughput and no limit to the number of messages per queue in Standard queues. First-In-First-Out delivery and exactly once processing in FIFO queues.</p>
</li>
<li><p><strong>Durability and availability:</strong> Your queues are distributed on multiple servers. Redundant infrastructure provides highly concurrent access to messages.</p>
</li>
<li><p><strong>Security:</strong> Protection in transit and at rest. Transmit sensitive data in encrypted queues. Send messages in a Virtual Private Cloud.</p>
</li>
<li><p><strong>Batching:</strong> Send, receive, or delete messages in batches of up to 10 messages or 256KB to save costs.</p>
</li>
</ol>
<h2 id="heading-what-were-building-today">What We're Building Today</h2>
<p>We're walking through a real-world implementation of an <strong>event-driven Node.js system using Amazon SQS</strong>. You’ll learn how we decoupled a user signup workflow by pushing email notifications to a queue — making our backend more scalable, performant, and resilient.</p>
<p>This guide is based on the project:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/node-aws-sqs-events">https://github.com/kartikmehta8/node-aws-sqs-events</a></div>
<p> </p>
<blockquote>
<p>This repo demonstrates a simple and scalable system where user registration triggers an asynchronous welcome email using AWS SQS and Nodemailer.</p>
</blockquote>
<p>We'll explore:</p>
<ul>
<li><p>A full walkthrough of the codebase structure</p>
</li>
<li><p>How to set up SQS in your AWS account</p>
</li>
<li><p>How to test your implementation under load</p>
</li>
</ul>
<p>Let's dive into the <strong>power of queues</strong> and how SQS unlocks a world of scalable, asynchronous architectures.</p>
<h2 id="heading-the-plot">The Plot</h2>
<p>Sending welcome emails directly during user registration can slow down your app, especially under load. A better approach is to <strong>offload the email task</strong> to a <strong>background worker using AWS SQS</strong> — giving your API a major performance boost.</p>
<h3 id="heading-1-project-structure">1. Project Structure</h3>
<pre><code class="lang-bash">.
├── config/                   <span class="hljs-comment"># MongoDB connection.</span>
│   └── db.js
├── controllers/
│   └── authController.js     <span class="hljs-comment"># Signup &amp; Signin with SQS enqueue.</span>
├── middleware/
│   └── rateLimitMiddleware.js
├── models/
│   └── User.js               <span class="hljs-comment"># Mongoose schema.</span>
├── routes/
│   └── authRoutes.js
├── services/
│   ├── emailService.js       <span class="hljs-comment"># (Not shown here) Worker to send actual emails.</span>
│   └── sqsService.js         <span class="hljs-comment"># AWS SQS integration (Producer)</span>
├── utils/
│   └── validateInput.js
├── worker/
│   └── emailWorker.js        <span class="hljs-comment"># (Consumer) Polls SQS and sends emails.</span>
├── .env                      <span class="hljs-comment"># Environment variables.</span>
├── server.js                 <span class="hljs-comment"># Entry point.</span>
└── package.json
</code></pre>
<p>This structure separates concerns: auth logic, SQS handling, email delivery, and background processing — all decoupled and scalable. I recommend readers check this out, as the code details are not discussed, the blog is focused on SQS.</p>
<h3 id="heading-2-environment-variables">2. Environment Variables</h3>
<p>Check out the sample <code>.env</code> file to see how the environment variables are structured.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># This file contains all the environment variables that are required by the application.</span>
PORT=5001
JWT_SECRET=kartikmehta
MONGO_URI=mongodb://localhost:27017

<span class="hljs-comment"># Email</span>
EMAIL_USER=
EMAIL_PASS=

<span class="hljs-comment"># AWS</span>
AWS_REGION=ap-south-1
AWS_SQS_QUEUE_URL=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
</code></pre>
<p><a target="_blank" href="https://docs.thedaotool.com/local-setup/mail-setup">This</a> is how you can set up <code>EMAIL_USER</code> and <code>EMAIL_PASS</code>.</p>
<h2 id="heading-setting-up-aws-sqs">Setting Up AWS SQS</h2>
<h3 id="heading-create-the-sqs-queue">Create the SQS Queue</h3>
<ol>
<li>Go to <a target="_blank" href="https://console.aws.amazon.com/sqs">https://console.aws.amazon.com/sqs</a></li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752339742439/66c94dc2-1956-49cf-8dde-e4a3732f06b1.png" alt class="image--center mx-auto" /></p>
<ol start="2">
<li><p>Click <strong>Create Queue</strong></p>
</li>
<li><p>Choose <strong>Standard</strong></p>
</li>
<li><p>Name it <code>email-queue</code></p>
</li>
<li><p>Leave defaults and create.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752339808509/6f1ea5c9-6789-46ba-949c-a47205cbeb2b.png" alt class="image--center mx-auto" /></p>
<p>The SQS Queue URI can be obtained from the following location:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752340238270/40042446-0b0b-418a-834e-cc51cd4f30d6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-create-iam-access-keys">Create IAM Access Keys</h3>
<ol>
<li>Go to <strong>AWS Console &gt; IAM</strong>.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742364960291/eb9c174b-1ad9-4b1f-a0a1-19426ffc3bab.png?auto=compress,format&amp;format=webp" alt /></p>
<ol start="2">
<li><p>On the left-hand side, under <strong>Access Management</strong>, click on <strong>Users</strong>. This is where you can create users and generate their access keys and secrets. Click <strong>Create user</strong>.</p>
</li>
<li><p>Enter a <strong>username</strong> and click <strong>Next</strong> to proceed.</p>
</li>
<li><p>In the <strong>Permissions</strong> section, select <strong>Attach policies directly</strong>.</p>
<p> Scroll down to view the available policies. We only need to attach two policies:</p>
<ul>
<li><code>AmazonSQSFullAccess</code> (for now)</li>
</ul>
</li>
<li><p>Click <strong>Create user</strong>. You have successfully created the user.</p>
</li>
<li><p>Now, to generate the <strong>Access Key ID</strong> and <strong>Secret Access Key</strong> for this user, select the user you just created. Navigate to the <strong>Security Credentials</strong> tab.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742366753723/1c704660-16f9-47f0-987e-b1d1c74cd4e8.png?auto=compress,format&amp;format=webp" alt /></p>
<ol start="7">
<li><p>Scroll down to the <strong>Access Keys</strong> section and click <strong>Create access key</strong>.</p>
</li>
<li><p>Select <strong>Command Line Interface (CLI)</strong> as the use case, acknowledge the warning at the bottom, and click <strong>Next</strong> to proceed.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742367032639/8c8c9358-9d55-49ce-a423-5421c7e60aa1.png?auto=compress,format&amp;format=webp" alt /></p>
<ol start="9">
<li>Click <strong>Create access key</strong>. You can see your <strong>Access Key ID</strong> and <strong>Secret Access Key</strong>.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742367235473/e78fce88-01da-402b-9f74-5bb3dc4b5f25.png?auto=compress,format&amp;format=webp" alt /></p>
<p>Now we have all the environment keys needed to run this project. Let’s go through the code to understand what’s being done.</p>
<h2 id="heading-before-vs-after-aws-sqs-integration">Before vs After AWS SQS Integration</h2>
<p>Let’s look at <strong>signup logic before and after</strong> integrating SQS.</p>
<h3 id="heading-before-synchronous-email-sending">Before (Synchronous Email Sending)</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> nodemailer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'nodemailer'</span>);

<span class="hljs-keyword">const</span> transporter = nodemailer.createTransport({ <span class="hljs-comment">/* config */</span> });

<span class="hljs-keyword">const</span> registerUser = <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { name, email, password } = req.body;
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.create({ name, email, password });

  <span class="hljs-keyword">await</span> transporter.sendMail({
    <span class="hljs-attr">from</span>: <span class="hljs-string">'support@example.com'</span>,
    <span class="hljs-attr">to</span>: email,
    <span class="hljs-attr">subject</span>: <span class="hljs-string">'Welcome!'</span>,
    <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;h1&gt;Hi <span class="hljs-subst">${name}</span>&lt;/h1&gt;`</span>,
  });

  res.status(<span class="hljs-number">201</span>).json({ user });
};
</code></pre>
<p><strong>Problems:</strong></p>
<ul>
<li><p>Email sending delays the response</p>
</li>
<li><p>If SMTP fails, user signup breaks</p>
</li>
<li><p>Harder to retry or monitor delivery failures</p>
</li>
</ul>
<h3 id="heading-after-async-sqs-job-enqueue">After (Async SQS Job Enqueue)</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// controllers/authController.js</span>
<span class="hljs-keyword">const</span> registerUser = <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { name, email, password } = req.body;
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.create({ name, email, password });

  <span class="hljs-comment">// Send to queue instead of SMTP directly</span>
  <span class="hljs-keyword">await</span> sendToQueue({ <span class="hljs-attr">email</span>: user.email, <span class="hljs-attr">name</span>: user.name });

  res.status(<span class="hljs-number">201</span>).json({ user });
};
</code></pre>
<p>and</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> processMessages = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> params = { <span class="hljs-attr">QueueUrl</span>: QUEUE_URL, <span class="hljs-attr">MaxNumberOfMessages</span>: <span class="hljs-number">10</span> };

  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> sqs.receiveMessage(params).promise();
  <span class="hljs-keyword">if</span> (data.Messages) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> message <span class="hljs-keyword">of</span> data.Messages) {
      <span class="hljs-keyword">const</span> { email, name } = <span class="hljs-built_in">JSON</span>.parse(message.Body);
      <span class="hljs-keyword">await</span> sendWelcomeEmail(email, name);
      <span class="hljs-keyword">await</span> sqs.deleteMessage({ <span class="hljs-attr">QueueUrl</span>: QUEUE_URL, <span class="hljs-attr">ReceiptHandle</span>: message.ReceiptHandle }).promise();
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Message processed: <span class="hljs-subst">${message.Body}</span>`</span>);
    }
  }
};

<span class="hljs-comment">// code...</span>

<span class="hljs-built_in">setInterval</span>(processMessages, <span class="hljs-number">5000</span>);
</code></pre>
<p><strong>Result:</strong></p>
<ul>
<li><p>Signup completes instantly</p>
</li>
<li><p>Email handled <strong>out-of-band</strong></p>
</li>
<li><p>You can now add metrics, retries, and even scale with AWS Lambda</p>
</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Benefit</td><td>Explanation</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Fast API Responses</strong></td><td>Signup API doesn’t wait for email to be sent</td></tr>
<tr>
<td><strong>Reliable Job Handling</strong></td><td>Emails won’t be lost on server crash</td></tr>
<tr>
<td><strong>Retry Support</strong></td><td>AWS SQS allows retry attempts or dead letter queue</td></tr>
<tr>
<td><strong>Async Scalability</strong></td><td>Workers scale separately — 1 or 100 workers</td></tr>
<tr>
<td><strong>Better User Experience</strong></td><td>Emails sent quickly in parallel with user interaction</td></tr>
<tr>
<td><strong>Clear Audit Trail</strong></td><td>Log every queued job for debugging or tracking</td></tr>
<tr>
<td><strong>Language/Platform Agnostic</strong></td><td>Multiple services in different stacks can share the same queue</td></tr>
</tbody>
</table>
</div><h3 id="heading-tldr-why-this-pattern-rocks">TL;DR: Why This Pattern Rocks</h3>
<ul>
<li><p>The frontend and API remain <strong>lightweight and blazing fast</strong></p>
</li>
<li><p>Failures in your SMTP provider <strong>won’t break user signups</strong></p>
</li>
<li><p>Your system is now <strong>ready for scale</strong>, reliability, and observability</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we implemented AWS SQS to offload email sending from the signup flow, improving speed and reliability. While this was a basic use case, SQS can power much more—like async tasks for report generation, payment processing, syncing services, or triggering workflows. With built-in features like retry policies, dead letter queues, visibility timeouts, and high throughput, AWS SQS ensures fault-tolerant, scalable job handling. By decoupling time-consuming operations from the main app, you keep user interactions fast and systems clean. Queues aren't just for email—they're essential for building modern, event-driven, production-grade backend architecture.</p>
<p><strong>Time to offload the heavy lifting.</strong></p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Is Solana the Visa of Crypto?]]></title><description><![CDATA[Introduction
Stablecoins are kind of like the unsung heroes of crypto. They don’t promise 100x returns, they don’t start flame wars on Twitter (most of the time), and yet, they power more real-world use than almost anything else in the space. If you’...]]></description><link>https://writer.mrmehta.in/is-solana-the-visa-of-crypto</link><guid isPermaLink="true">https://writer.mrmehta.in/is-solana-the-visa-of-crypto</guid><category><![CDATA[Solana]]></category><category><![CDATA[Stablecoins ]]></category><category><![CDATA[Cryptocurrency]]></category><category><![CDATA[research]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sun, 22 Jun 2025 07:37:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750534732026/589c3bcc-ad52-4c92-b149-db393cfccafc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Stablecoins are kind of like the unsung heroes of crypto. They don’t promise 100x returns, they don’t start flame wars on Twitter (most of the time), and yet, they power more real-world use than almost anything else in the space. If you’ve been around the DAO space or worked with any contributor payments, you already know this. No one wants to get paid in a token that tanks 30% overnight.</p>
<p>That’s why I’ve been increasingly drawn to what’s happening with stablecoins on Solana. As someone who’s worked on <a target="_blank" href="https://www.thedaotool.com">DAO tooling</a>, cross-chain infrastructure, and generally cares more about utility than ideology, I’ve been watching Solana’s resurgence closely. It’s not just about NFTs and fast swaps anymore. Solana is quietly (and now not-so-quietly) becoming the best backend for stablecoin-powered products.</p>
<p>So in this post, I’m doing a deep dive—not just stats and hype, but a builder-focused breakdown of where things stand, what’s actually shipping, and why I think Solana + stablecoins might be one of the most important narratives of the next cycle.</p>
<h1 id="heading-how-big-is-the-stablecoin-wave-on-solana">How Big is the Stablecoin Wave on Solana?</h1>
<p>Alright, let’s start with the numbers—but don’t worry, I’ll keep it human.</p>
<p>Back in late 2023, stablecoin activity on Solana was... not great. Post-FTX, a lot of capital had fled, and the chain was rebuilding trust. Fast forward to early 2025, and Solana’s stablecoin ecosystem has gone from ghost town to <a target="_blank" href="https://thedefiant.io/news/blockchains/solana-stablecoin-market-hits-record-usd12-8-billion">booming city</a>.</p>
<h2 id="heading-some-context">Some context:</h2>
<ul>
<li><p>In January 2025, Solana had around <strong>$5.2B</strong> in stablecoins circulating. A month later? Over <strong>$11.7B</strong>. That’s a 2.2x jump.</p>
</li>
<li><p>It now ranks <strong>third overall</strong> in stablecoin liquidity, behind Ethereum and Tron (yes, Tron is surprisingly huge for stablecoins).</p>
</li>
<li><p>The transaction activity is even wilder: in January 2025 alone, there were over <strong>263 million</strong> peer-to-peer stablecoin transfers. That’s nearly 9 transfers per second, not counting swaps on DEXs.</p>
</li>
<li><p>Daily active stablecoin addresses? Over <strong>3 million</strong>. That’s an 8x jump from late 2023.</p>
</li>
</ul>
<p>What this tells me is simple: Solana’s low fees and fast finality make stablecoins actually usable for things like micropayments, remittances, payroll, and even cross-border B2B flows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750537158131/4c2dd362-64a8-41f6-9bfd-53fc9f348465.png" alt class="image--center mx-auto" /></p>
<p>And it’s not just USDC. Sure, that’s still the dominant one (70% of supply), followed by USDT (~18%). But now there’s a growing long tail of stablecoins like PayPal’s PYUSD, Maker’s new USDS (formerly DAI), and FDUSD from Asia.</p>
<h1 id="heading-real-world-use">Real-World Use</h1>
<p>What excites me most is that this isn’t just about trading. It’s about <strong>actual use</strong>—real payments, not just liquidity farming. Here are a few integrations that stood out to me:</p>
<h2 id="heading-stripe-usdc-on-solana">Stripe + USDC on Solana</h2>
<p>Yes, that Stripe. The same Stripe that powers half the internet. In late 2024, they added support for USDC payments via Solana, and the results were pretty amazing: businesses in 70+ countries could now accept stablecoins with fiat payout. Stripe handles all the complexity—you send USDC, they handle the rest.</p>
<p>And here’s the kicker: they picked Solana because it’s fast and cheap. No one wants to pay $10 in gas to send $20, right?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.paymentsjournal.com/stripes-stablecoin-integration-sees-blockbuster-first-day">https://www.paymentsjournal.com/stripes-stablecoin-integration-sees-blockbuster-first-day</a></div>
<p> </p>
<h2 id="heading-shopify-solana-pay">Shopify + Solana Pay</h2>
<p>This one is fun. Solana Pay was integrated into Shopify stores back in 2023, starting with USDC payments, but it has since expanded to support many assets.</p>
<p>It’s simple: you go to checkout, click the Solana Pay option, scan a QR code, and boom—USDC gets sent from your wallet to the merchant instantly. No banks, no chargebacks, just clean UX.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://solana.com/news/solana-pay-shopify">https://solana.com/news/solana-pay-shopify</a></div>
<p> </p>
<h2 id="heading-visa-using-solana-to-settle-payments">Visa: Using Solana to Settle Payments</h2>
<p>Visa (yes, <em>Visa</em>) is using Solana to <strong>settle millions in daily transactions</strong>. In 2023, they added Solana to their pilot, moving USDC between entities like Worldpay and Nuvei. <a target="_blank" href="https://x.com/cuysheffield/status/1699031109080945049">This isn’t theory</a>. This is real settlement infrastructure being tested at scale.</p>
<p><strong>So let’s be clear:</strong> Solana’s stablecoin story isn’t about memecoins or short-term hype. It’s about Stripe, Visa, and Shopify treating Solana as <strong>infrastructure</strong>.</p>
<h1 id="heading-so-whos-building-what">So Who’s Building What?</h1>
<p>Here’s a quick (but personal) map of the stablecoin ecosystem on Solana as I see it. I’ve grouped it by use case, but this is far from exhaustive.</p>
<h2 id="heading-payments-fintech">Payments / Fintech</h2>
<ul>
<li><p><a target="_blank" href="https://www.kast.xyz"><strong>KAST</strong></a> – Digital banking with a debit card linked to your USDC on Solana</p>
</li>
<li><p><a target="_blank" href="https://spherepay.co/en"><strong>Sphere</strong></a> – Powering remittances using a “stablecoin sandwich” model (fiat → USDC → fiat)</p>
</li>
<li><p><a target="_blank" href="https://solanapay.com/"><strong>Solana Pay</strong></a> – Merchant payment rails with wallet-native checkout</p>
</li>
</ul>
<h2 id="heading-defi-amp-liquidity">DeFi &amp; Liquidity</h2>
<ul>
<li><p><a target="_blank" href="https://perena.org/stablecoins"><strong>Perena</strong></a> – Stablecoin AMM, launched by former Solana stablecoin lead</p>
</li>
<li><p><strong>USD</strong>* – A single stablecoin backed by a basket of USDC, USDT, and PYUSD</p>
</li>
<li><p><a target="_blank" href="https://huma.finance/"><strong>Huma</strong></a><strong>,</strong> <a target="_blank" href="https://synatra.xyz/"><strong>Synatra</strong></a> – Lending and yield infra for stablecoin credit</p>
</li>
</ul>
<h2 id="heading-treasury-daos-amp-dev-infra">Treasury, DAOs &amp; Dev Infra</h2>
<ul>
<li><p><a target="_blank" href="https://squads.xyz/"><strong>Squads Altitude</strong></a> – Bankless treasury accounts with multi-sig</p>
</li>
<li><p><a target="_blank" href="https://www.circle.com/cross-chain-transfer-protocol"><strong>Circle CCTP</strong></a> – Native USDC bridging (Ethereum ↔ Solana) without wrappers</p>
</li>
<li><p><a target="_blank" href="https://wormhole.com/"><strong>Wormhole</strong></a> – Messaging layer used by DAI (USDS) to bridge to Solana</p>
</li>
</ul>
<h2 id="heading-rwa-amp-regulated-issuers">RWA &amp; Regulated Issuers</h2>
<ul>
<li><p><strong>PYUSD</strong> – PayPal’s stablecoin</p>
</li>
<li><p><strong>FDUSD</strong> – Launched from Hong Kong</p>
</li>
<li><p><strong>USDe (Ethena)</strong> – Delta-neutral synthetic stablecoin</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750539093886/fd9a62ba-ad74-4373-9b13-41533e03bb0e.png" alt class="image--center mx-auto" /></p>
<p>All of this shows a key point: Solana isn’t just a chain with stablecoins. It’s becoming a <strong>hub</strong> where payments, liquidity, governance, and compliance intersect.</p>
<h1 id="heading-the-grown-ups-are-watching">The Grown-Ups Are Watching</h1>
<p>Let’s be real — if crypto wants to grow up and play in the big leagues, it can’t dodge regulation forever. And stablecoins, sitting right at the intersection of crypto and traditional money, are squarely in regulators’ sights. Whether you’re building DeFi tools, DAO treasuries, or just trying to integrate USDC into a Web2 product, it’s worth understanding how different regions are treating this new class of digital dollars.</p>
<h2 id="heading-united-states-enter-the-genius-act">United States – Enter the GENIUS Act</h2>
<p>After years of hand-wringing and tweet-thread policy debates, the US finally made a move. In June 2025, the GENIUS Act passed the Senate — the first serious piece of legislation dedicated to stablecoins. While the name might make you roll your eyes, the intent is clear: <strong>stablecoins need adult supervision</strong>.</p>
<p>Under this law, all major stablecoin issuers are required to hold <strong>100% of reserves in cash or short-term Treasuries</strong> — no funky collateral, no smoke and mirrors. If you're issuing billions worth of tokens, you’re also subject to mandatory audits and regular federal oversight. The idea here is to make stablecoins as safe and boring as bank deposits — and honestly, that’s probably a good thing if we want institutions to touch them.</p>
<p>What’s interesting is the bill doesn’t totally centralize power with the federal government. State regulators can still play a role, as long as their standards line up. This opens the door for a mix of fintech innovation and compliance — something Solana-native projects like KAST or Squads could benefit from, especially as more startups consider issuing branded stablecoins.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://abcnews.go.com/Business/genius-act-crypto-regulation-bill/story?id=121981442">https://abcnews.go.com/Business/genius-act-crypto-regulation-bill/story?id=121981442</a></div>
<p> </p>
<h2 id="heading-apac-patchwork-of-innovation-and-guardrails">APAC – Patchwork of Innovation and Guardrails</h2>
<p>Now let’s look east. The <strong>Asia-Pacific region</strong> is a mixed bag when it comes to stablecoins, but some countries are stepping up in a big way.</p>
<p>In <strong>Hong Kong</strong>, stablecoins are being embraced — with guardrails. A <a target="_blank" href="https://www.hkma.gov.hk/eng/news-and-media/press-releases/2025/05/20250521-3/">new law</a> that took effect in 2025 requires issuers to be licensed by the HK Monetary Authority (HKMA). Issuers must maintain high-quality reserves, offer smooth redemption, and manage risk. It's a strong “same risk, same regulation” approach — but the vibe is pro-innovation. That’s why names like <strong>FDUSD</strong> chose to launch from Hong Kong.</p>
<p><strong>Singapore</strong> has also taken a measured route. The Monetary Authority of Singapore (MAS) rolled out an opt-in framework where issuers of SGD or G10-pegged stablecoins can become <a target="_blank" href="https://www.mas.gov.sg/news/media-releases/2023/mas-finalises-stablecoin-regulatory-framework">officially regulated</a>. If you don’t opt in, you can’t call your token “MAS-compliant” — but the rules aren’t forced on everyone. MAS is focusing on value stability, redemption guarantees, and reserve transparency, which make it ideal for enterprise use cases.</p>
<p>On the flip side, <strong>Japan</strong> has kept things very tight. Only licensed banks or trust companies can issue <a target="_blank" href="https://www.sygna.io/blog/japan-regulates-stablecoins-in-milestone-bill/">stablecoins</a> — essentially ruling out most crypto-native projects. However, things are slowly loosening up. Circle’s USDC is expected to make its way into Japan via SBI, a regulated local player. Once approved, USDC could be listed for trading and used for compliant payments.</p>
<p>Elsewhere in APAC: <strong>Australia</strong> is drafting crypto reforms, <strong>India</strong> remains skeptical, and <strong>China</strong> is focused on its own CBDC (not stablecoin-friendly). The <strong>UAE</strong>, while not APAC, deserves a shoutout too — it’s building a crypto-regulation sandbox that encourages stablecoin adoption under tight controls.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.forbes.com/sites/digital-assets/2023/12/14/stablecoins-gain-traction-in-asia-but-challenges-remain/">https://www.forbes.com/sites/digital-assets/2023/12/14/stablecoins-gain-traction-in-asia-but-challenges-remain/</a></div>
<p> </p>
<h1 id="heading-product-ideas-that-matter">Product Ideas That Matter</h1>
<p>Let’s shift gears from what exists to what’s possible — and more importantly, what’s <strong>realistically buildable today</strong> on Solana. These ideas aren't “when we get L3s and intent-based routers” moonshots. They’re concepts that combine composability, stablecoins, and Solana’s UX to solve actual user problems. A few of these I’ve been noodling on myself, and others are inspired by things I’ve seen in the trenches.</p>
<h2 id="heading-dao-treasury-bot-on-chain-cfo"><strong>DAO Treasury Bot – On-Chain CFO</strong></h2>
<p>Every DAO has the same three problems: custody, capital efficiency, and contributor payouts. So… what if we automated all of that?</p>
<p>Picture a dApp that:</p>
<ul>
<li><p>Uses <a target="_blank" href="https://squads.xyz/">Squads Altitude</a> as the treasury vault,</p>
</li>
<li><p>Automatically moves idle USDC into <strong>USDY</strong> or <a target="_blank" href="https://perena.org/">Perena</a> to earn yield,</p>
</li>
<li><p>And streams contributor salaries via <a target="_blank" href="https://www.zebec.io/">Zebec</a> on a schedule.</p>
</li>
</ul>
<p>It’s basically a CFO-in-a-box — but programmable, transparent, and permissionless. This could also integrate multisig governance triggers (like stop salary if proposal X fails), and it fits perfectly for DAOs, collectives, or even freelance orgs operating internationally.</p>
<h2 id="heading-cross-border-payment-hub"><strong>Cross-Border Payment Hub</strong></h2>
<p>One of the biggest pain points in global payments is cost and latency. In emerging markets, even a $5 remittance fee matters.</p>
<p>You could build a remittance app where:</p>
<ul>
<li><p>A sender in, say, the US pays in dollars (via card or ACH),</p>
</li>
<li><p>It gets converted into USDC and transferred instantly on Solana,</p>
</li>
<li><p>And the recipient cashes out in local fiat on the other end.</p>
</li>
</ul>
<p>Projects like <a target="_blank" href="https://spherepay.co/en">Sphere</a> are already showing signs of this model working. The beauty? The user never has to know it’s crypto. You abstract the wallets, show balances in fiat, and optimize speed and cost under the hood using stablecoins on Solana. The potential to bring financial access to regions with high remittance costs is huge.</p>
<h1 id="heading-final-thoughts">Final Thoughts</h1>
<p>Stablecoins are where crypto meets the real world. And Solana, with its speed, UX, and growing credibility, is shaping up to be the chain that makes stablecoins actually usable at scale.</p>
<p>As someone who has worked across DAO tooling and bridging solutions, I see stablecoins as the <strong>monetary layer for Web3 coordination</strong>. They enable real-time finance, borderless collaboration, and true financial sovereignty—all without asking users to take on crazy volatility risk.</p>
<p>The way I see it, the "stablecoin wars" won’t be won by whoever shouts decentralization the loudest. They’ll be won by whoever makes stablecoins <strong>the most usable, reliable, and programmable</strong>. And right now? Solana is leading that charge.</p>
<p>Let’s keep building.</p>
<p>~ <a target="_blank" href="https://www.mrmehta.in">Mehta</a></p>
<h1 id="heading-references"><strong>References</strong></h1>
<ul>
<li><p>https://www.helius.dev/blog/solanas-stablecoin-landscape</p>
</li>
<li><p><a target="_blank" href="https://www.paymentsjournal.com/stripes-stablecoin-integration-sees-blockbuster-first-day">https://www.paymentsjournal.com/stripes-stablecoin-integration-sees-blockbuster-first-day</a></p>
</li>
<li><p><a target="_blank" href="https://decrypt.co/324930/shopify-roll-out-usdc-stablecoin-payments-on-base-coinbase-team-up">https://decrypt.co/324930/shopify-roll-out-usdc-stablecoin-payments-on-base-coinbase-team-up</a></p>
</li>
<li><p><a target="_blank" href="https://usa.visa.com/about-visa/newsroom/press-releases.releaseId.19881.html">https://usa.visa.com/about-visa/newsroom/press-releases.releaseId.19881.html</a></p>
</li>
<li><p><a target="_blank" href="https://blockworks.co/news/genius-stablecoin-act-passes-senate-vote">https://blockworks.co/news/genius-stablecoin-act-passes-senate-vote</a></p>
</li>
<li><p><a target="_blank" href="https://www.ledgerinsights.com/the-facts-about-mica-stablecoin-caps/">https://www.ledgerinsights.com/the-facts-about-mica-stablecoin-caps</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[S3rcel: Deploying PR Previews for React Using AWS S3 & CloudFront]]></title><description><![CDATA[Introduction
If you've used Vercel, you've likely seen how it provides PR Preview Deployments — a feature that generates a unique URL for each pull request, allowing developers to test changes before merging. This is incredibly useful for frontend ap...]]></description><link>https://writer.mrmehta.in/s3rcel-deploying-pr-previews-for-react-using-aws-s3-and-cloudfront</link><guid isPermaLink="true">https://writer.mrmehta.in/s3rcel-deploying-pr-previews-for-react-using-aws-s3-and-cloudfront</guid><category><![CDATA[AWS]]></category><category><![CDATA[S3]]></category><category><![CDATA[cloudfront]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[cicd]]></category><category><![CDATA[Vercel]]></category><category><![CDATA[deployment]]></category><category><![CDATA[automation]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Wed, 19 Mar 2025 07:50:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741983392267/59ec6dbf-0cfd-41c4-a8e0-69256feac084.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>If you've used <a target="_blank" href="https://vercel.com">Vercel</a>, you've likely seen how it provides <strong>PR Preview Deployments</strong> — a feature that generates a unique URL for each pull request, allowing developers to test changes before merging. This is incredibly useful for frontend applications, as it enables quick feedback cycles.</p>
<p>I wanted to replicate this system (as a developer, because why not?) using <strong>AWS Infrastructure</strong>, so I built my own, <strong>S3rcel — A</strong> <strong>Pull Request Preview / Deployment System</strong> for <strong>React applications</strong>, leveraging:</p>
<ul>
<li><p><strong>GitHub Actions</strong> for automation</p>
</li>
<li><p><strong>AWS S3</strong> for static file storage</p>
</li>
<li><p><strong>CloudFront</strong> for fast global delivery</p>
</li>
</ul>
<p><img src="https://pbs.twimg.com/media/GmKa7sObAAAsvPr?format=jpg&amp;name=large" alt="Image" class="image--center mx-auto" /></p>
<p>In this guide, I'll walk you through how to set up AWS, configure GitHub, and explain the workflows powering this deployment system. I will mostly be focusing on setting up AWS, so I'll provide you with everything else you need to get started.</p>
<h1 id="heading-prerequisites"><strong>Prerequisites</strong></h1>
<p>Before getting started, make sure you have:</p>
<ol>
<li><strong>An AWS account</strong> with root access or privileges to create an IAM user with the necessary permissions.</li>
</ol>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://aws.amazon.com">https://aws.amazon.com</a></div>
<p> </p>
<ol start="2">
<li><strong>Fork the Repository:</strong> To streamline the process and focus more on setting up AWS, you can start by forking the repository below. The following repository contains three workflows and a basic React application with Tailwind CSS styling. I will discuss the workflows once we are done with the AWS setup.</li>
</ol>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/s3rcel-pr-preview">https://github.com/kartikmehta8/s3rcel-pr-preview</a></div>
<p> </p>
<p>Once you have forked the repository, you need to set up the following <strong>secrets</strong> in your repository under <strong>Settings → Secrets and Variables → Actions</strong> as we continue progressing through the blog.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Secret Name</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>AWS_ACCESS_KEY_ID</code></td><td>Your AWS access key ID with permissions for S3 and CloudFront</td></tr>
<tr>
<td><code>AWS_SECRET_ACCESS_KEY</code></td><td>Your AWS secret access key</td></tr>
<tr>
<td><code>AWS_REGION</code></td><td>The AWS region where your S3 bucket is located (e.g., <code>ap-south-1</code>)</td></tr>
<tr>
<td><code>AWS_S3_BUCKET_NAME</code></td><td>The name of your S3 bucket for storing the previews</td></tr>
<tr>
<td><code>CLOUDFRONT_DISTRIBUTION_ID</code></td><td>The ID of your CloudFront distribution</td></tr>
<tr>
<td><code>CLOUDFRONT_DISTRIBUTION_DOMAIN_NAME</code></td><td>The domain name of your CloudFront distribution</td></tr>
</tbody>
</table>
</div><p>As we have completed the initial setup, let's log in to the <a target="_blank" href="http://console.aws.com/console/home">AWS Console</a> and start setting up the environment.</p>
<h1 id="heading-setup-s3-bucket"><strong>Setup - S3 Bucket</strong></h1>
<ol>
<li><p>Go to the <strong>AWS S3 Console</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742301284342/c8c6eeda-8d54-46d3-b3c4-e8d5f8ee184b.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Create bucket</strong>.</p>
</li>
<li><p>Set a <strong>unique bucket name</strong>.</p>
</li>
<li><p><strong>Uncheck</strong> "Block all public access".</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742301759974/8e3812dc-d90d-4641-8500-535477d923d4.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Leave the other settings as default.</p>
</li>
<li><p>Click <strong>Create bucket</strong>. You will see something like this: <em>(Your bucket will be empty initially.)</em></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742301903302/17180918-8c32-4701-9919-bdac22d4456e.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Enable <strong>Static Website Hosting</strong>: Go to the <strong>Properties</strong> tab, and scroll down to the bottom.</p>
<ol>
<li><p>Click on <strong>Edit</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742302296534/9b04eb10-779c-48fc-b43f-f44d70a667f9.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Enable <strong>Static website hosting</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742302414782/58b48348-c668-49e3-b3fe-84feb682d6de.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
</li>
<li><p>Set up the <strong>Bucket Policy</strong>: Go to the <strong>Permissions</strong> tab, paste the following policy under <strong>Bucket Policy</strong>, and save it. (Make sure to replace <code>"your-bucket-name"</code> with the actual name of your bucket.)</p>
<pre><code class="lang-json"> {
     <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
     <span class="hljs-attr">"Statement"</span>: [
         {
             <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
             <span class="hljs-attr">"Principal"</span>: <span class="hljs-string">"*"</span>,
             <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"s3:GetObject"</span>,
             <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:s3:::&lt;your-bucket-name&gt;/*"</span>
         }
     ]
 }
</code></pre>
<p> This S3 bucket policy allows public read access to all objects in the specified bucket name. The <code>"Principal": "*"</code> means anyone can access it, while <code>"Action": "s3:GetObject"</code> permits reading objects. The <code>"Resource"</code> specifies the bucket and its contents.</p>
</li>
<li><p>Check the <strong>AWS Region</strong>: You can find the region in the <strong>AWS Management Console</strong> at the top-right corner. Make sure you're using the correct region for your setup.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742303193992/949d70b5-fc81-4a11-8bb1-49b555c8b335.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<p>Alright, the <strong>S3 setup</strong> is complete! Now, you can add the following <strong>two secrets</strong> in your GitHub repository under:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><code>AWS_REGION</code></td><td>The AWS region where your S3 bucket is located.</td></tr>
</thead>
<tbody>
<tr>
<td><code>AWS_S3_BUCKET_NAME</code></td><td>The name of your S3 bucket for storing the previews.</td></tr>
</tbody>
</table>
</div><p>Let's start setting up <strong>AWS CloudFront</strong>.</p>
<h1 id="heading-setup-cloudfront"><strong>Setup - CloudFront</strong></h1>
<ol>
<li><p>Go to the <strong>AWS CloudFront Console</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742317438012/ece163be-f3b5-4c65-b86e-effb2bff25b5.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Create Distribution</strong>.</p>
</li>
<li><p>Set <strong>Origin Domain Name</strong> to the <strong>S3 bucket URL</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742363707162/a470fc13-fee9-4d55-a6bf-b78bd01a3fa9.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Set <strong>Viewer Protocol Policy</strong> to <code>Redirect HTTP to HTTPS</code>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742363828776/ec44a839-8c9d-4eba-b694-4a0ff83895c1.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Scroll down to the <strong>Settings</strong> section and set the Default Root Object to <strong>index.html</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742364235940/c29692d3-d5db-42df-93bc-d62096f0306d.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Create Distribution</strong>.</p>
</li>
</ol>
<p>We have completed the CloudFront setup. Now, go to the newly created distribution. You should be able to see the CloudFront Distribution ID and the Distribution Domain Name.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742364548152/9e0947e5-7bda-4887-b0de-c60eb43a4bd4.png" alt class="image--center mx-auto" /></p>
<p>Now, you can add the following <strong>two secrets</strong> in your GitHub repository under:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><code>CLOUDFRONT_DISTRIBUTION_ID</code></td><td>The ID of your CloudFront distribution</td></tr>
</thead>
<tbody>
<tr>
<td><code>CLOUDFRONT_DISTRIBUTION_DOMAIN_NAME</code></td><td>The domain name of your CloudFront distribution (without <code>https://</code>)</td></tr>
</tbody>
</table>
</div><p>Now, the only remaining step is to obtain the <strong>Access Key ID</strong> and <strong>Secret Access Key</strong>.</p>
<h1 id="heading-setup-iam"><strong>Setup - IAM</strong></h1>
<ol>
<li><p>Go to <strong>AWS Console &gt; IAM</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742364960291/eb9c174b-1ad9-4b1f-a0a1-19426ffc3bab.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>On the left-hand side, under <strong>Access Management</strong>, click on <strong>Users</strong>. This is where you can create users and generate their access keys and secrets. Click <strong>Create user</strong>.</p>
</li>
<li><p>Enter a <strong>username</strong> and click <strong>Next</strong> to proceed.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742365919232/a6ac11ed-13d0-4b75-bb26-50070c873014.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>In the <strong>Permissions</strong> section, select <strong>Attach policies directly</strong>.</p>
<p> Scroll down to view the available policies. We only need to attach two policies:</p>
<ul>
<li><p><strong>CloudFrontFullAccess</strong></p>
</li>
<li><p><strong>AmazonS3FullAccess</strong></p>
</li>
</ul>
</li>
</ol>
<p>    Select these policies and click <strong>Next</strong> to proceed.</p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742366455990/b24b012d-16f4-4660-9002-7b77fe627c67.png" alt class="image--center mx-auto" /></p>
<ol start="5">
<li><p>Click <strong>Create user</strong>. You have successfully created the user.</p>
</li>
<li><p>Now, to generate the <strong>Access Key ID</strong> and <strong>Secret Access Key</strong> for this user, select the user you just created. Navigate to the <strong>Security Credentials</strong> tab.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742366753723/1c704660-16f9-47f0-987e-b1d1c74cd4e8.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Scroll down to the <strong>Access Keys</strong> section and click <strong>Create access key</strong>.</p>
</li>
<li><p>Select <strong>Command Line Interface (CLI)</strong> as the use case, acknowledge the warning at the bottom, and click <strong>Next</strong> to proceed.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742367032639/8c8c9358-9d55-49ce-a423-5421c7e60aa1.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Create access key</strong>. You can see your <strong>Access Key ID</strong> and <strong>Secret Access Key</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742367235473/e78fce88-01da-402b-9f74-5bb3dc4b5f25.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<p>Now, you can add the following <strong>two secrets</strong> in your GitHub repository under:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><code>AWS_ACCESS_KEY_ID</code></td><td>Your AWS access key ID with permissions for S3 and CloudFront</td></tr>
</thead>
<tbody>
<tr>
<td><code>AWS_SECRET_ACCESS_KEY</code></td><td>Your AWS secret access key</td></tr>
</tbody>
</table>
</div><p>We have completed all the necessary configurations in the <strong>AWS Console</strong>.</p>
<p>Now, it's time to set up <strong>GitHub</strong> and go through the workflows that will handle <strong>PR Previews</strong> and <strong>Deployments</strong> automatically.</p>
<h1 id="heading-setup-github"><strong>Setup - GitHub</strong></h1>
<ol>
<li><p>I assume you have already set up the required secrets under <strong>Settings → Secrets and Variables → Actions</strong>, so I'll skip that step and move forward with the next part of the setup.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742367880731/6b042c2f-debc-4869-ad19-3ce3c5a471d0.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Go to <strong>Settings → Actions → General</strong> in your GitHub repository and ensure your settings match the following configuration:</p>
</li>
</ol>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Workflow permissions</strong></td><td>Read and write permissions. Check - Allow GitHub Actions to create and approve pull requests.</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Approval for running fork pull request workflows from contributors</strong></td><td>Require approval for first-time contributors.</td></tr>
<tr>
<td><strong>Actions permissions</strong></td><td>Allow all actions and reusable workflows.</td></tr>
</tbody>
</table>
</div><p>Great! We’ve completed all the setup. Now, we’re ready to create a <strong>Pull Request (PR)</strong> with changes and watch the workflow in action.</p>
<h1 id="heading-understanding-the-workflows">Understanding the Workflows</h1>
<p>But wait! Before we dive in, let’s take a moment to <strong>understand the workflow</strong> that powers and supercharges this project.</p>
<h3 id="heading-pr-preview-deployment">PR Preview Deployment</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">PR</span> <span class="hljs-string">Preview</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">synchronize</span>, <span class="hljs-string">reopened</span>]

<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">write</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy-preview:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">Repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Fix</span> <span class="hljs-string">React</span> <span class="hljs-string">Build</span> <span class="hljs-string">Paths</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"REACT_APP_PUBLIC_URL=/"</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">.env</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">React</span> <span class="hljs-string">App</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configure</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Credentials</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws configure set region ${{ secrets.AWS_REGION }}
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">PR</span> <span class="hljs-string">Deployment</span> <span class="hljs-string">Path</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"DEPLOY_PATH=pr-$<span class="hljs-template-variable">{{ github.event.pull_request.number }}</span>"</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">$GITHUB_ENV</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">S3</span> <span class="hljs-string">(Preview)</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">aws</span> <span class="hljs-string">s3</span> <span class="hljs-string">sync</span> <span class="hljs-string">./build</span> <span class="hljs-string">s3://${{</span> <span class="hljs-string">secrets.AWS_S3_BUCKET_NAME</span> <span class="hljs-string">}}/${{</span> <span class="hljs-string">env.DEPLOY_PATH</span> <span class="hljs-string">}}</span> <span class="hljs-string">--delete</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">Correct</span> <span class="hljs-string">Content-Type</span> <span class="hljs-string">for</span> <span class="hljs-string">JS</span> <span class="hljs-string">Files</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          aws s3 cp s3://${{ secrets.AWS_S3_BUCKET_NAME }}/${{ env.DEPLOY_PATH }} s3://${{ secrets.AWS_S3_BUCKET_NAME }}/${{ env.DEPLOY_PATH }} --recursive --exclude "*" --include "*.js" --metadata-directive REPLACE --content-type "application/javascript"
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Invalidate</span> <span class="hljs-string">CloudFront</span> <span class="hljs-string">Cache</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">aws</span> <span class="hljs-string">cloudfront</span> <span class="hljs-string">create-invalidation</span> <span class="hljs-string">--distribution-id</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLOUDFRONT_DISTRIBUTION_ID</span> <span class="hljs-string">}}</span> <span class="hljs-string">--paths</span> <span class="hljs-string">"/*"</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Comment</span> <span class="hljs-string">on</span> <span class="hljs-string">PR</span> <span class="hljs-string">with</span> <span class="hljs-string">Preview</span> <span class="hljs-string">URL</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">thollander/actions-comment-pull-request@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">message:</span> <span class="hljs-string">"🚀 Preview deployed: [View Preview](https://$<span class="hljs-template-variable">{{ secrets.CLOUDFRONT_DISTRIBUTION_DOMAIN_NAME }}</span>/$<span class="hljs-template-variable">{{ env.DEPLOY_PATH }}</span>/index.html)"</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<p>This workflow triggers <strong>whenever a PR is created or updated</strong> against the <code>main</code> branch. It installs dependencies, builds the React project, and uploads the build files to an <strong>S3 bucket under a PR-specific path</strong> (<code>pr-&lt;PR_NUMBER&gt;</code>). Once uploaded, CloudFront ensures that the preview is accessible globally, and a <strong>comment is posted on the PR with the preview link</strong>. This allows reviewers to test the changes before merging. Each time the PR is updated, the deployment is refreshed with the latest changes.</p>
<h3 id="heading-main-deployment">Main Deployment</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Production</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">Repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Fix</span> <span class="hljs-string">React</span> <span class="hljs-string">Build</span> <span class="hljs-string">Paths</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"REACT_APP_PUBLIC_URL=/"</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">.env</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">React</span> <span class="hljs-string">App</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>   

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configure</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Credentials</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws configure set region ${{ secrets.AWS_REGION }}
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">S3</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">aws</span> <span class="hljs-string">s3</span> <span class="hljs-string">sync</span> <span class="hljs-string">./build</span> <span class="hljs-string">s3://${{</span> <span class="hljs-string">secrets.AWS_S3_BUCKET_NAME</span> <span class="hljs-string">}}/main</span> <span class="hljs-string">--delete</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">Correct</span> <span class="hljs-string">Content-Type</span> <span class="hljs-string">for</span> <span class="hljs-string">JS</span> <span class="hljs-string">Files</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          aws s3 cp s3://${{ secrets.AWS_S3_BUCKET_NAME }}/main s3://${{ secrets.AWS_S3_BUCKET_NAME }}/main --recursive --exclude "*" --include "*.js" --metadata-directive REPLACE --content-type "application/javascript"
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Invalidate</span> <span class="hljs-string">CloudFront</span> <span class="hljs-string">Cache</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">aws</span> <span class="hljs-string">cloudfront</span> <span class="hljs-string">create-invalidation</span> <span class="hljs-string">--distribution-id</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLOUDFRONT_DISTRIBUTION_ID</span> <span class="hljs-string">}}</span> <span class="hljs-string">--paths</span> <span class="hljs-string">"/*"</span>
</code></pre>
<p>Every time code is pushed to <code>main</code>, this workflow kicks in to <strong>deploy the latest production build</strong>. It follows the same steps as the PR preview workflow but uploads the build to the <code>main</code> <strong>directory</strong> in S3. Additionally, it <strong>invalidates the CloudFront cache</strong> to ensure that users receive the latest version of the deployed app without waiting for cache expiry.</p>
<h3 id="heading-cleanup-workflow">Cleanup Workflow</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Cleanup</span> <span class="hljs-string">PR</span> <span class="hljs-string">Preview</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">closed</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">cleanup-preview:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Remove</span> <span class="hljs-string">PR</span> <span class="hljs-string">Preview</span> <span class="hljs-string">from</span> <span class="hljs-string">S3</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">aws</span> <span class="hljs-string">s3</span> <span class="hljs-string">rm</span> <span class="hljs-string">s3://${{</span> <span class="hljs-string">secrets.AWS_S3_BUCKET_NAME</span> <span class="hljs-string">}}/pr-${{</span> <span class="hljs-string">github.event.pull_request.number</span> <span class="hljs-string">}}</span> <span class="hljs-string">--recursive</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">AWS_ACCESS_KEY_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_SECRET_ACCESS_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_REGION:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_REGION</span> <span class="hljs-string">}}</span>
</code></pre>
<p>Once a PR is <strong>closed or merged</strong>, there is no need to retain the preview deployment. This workflow automatically deletes the PR’s specific deployment folder from <strong>S3</strong>, keeping the storage clean and avoiding unnecessary clutter. This ensures that old, unused previews do not persist indefinitely.</p>
<h1 id="heading-give-it-a-try">Give It a Try!</h1>
<p>Want to see it in action? Just create a <strong>Pull Request (PR) to the</strong> <code>main</code> <strong>branch</strong> in the forked repository, and GitHub Actions will automatically deploy a preview of your changes. A comment will be posted on the PR with the preview link, allowing you to test the deployment before merging. Check it out and experience the seamless PR preview workflow! 💪</p>
<p>Here's an example Pull Request:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/kartikmehta8/s3rcel-pr-preview/pull/20">https://github.com/kartikmehta8/s3rcel-pr-preview/pull/20</a></div>
<p> </p>
<h1 id="heading-conclusion"><strong>Conclusion</strong></h1>
<p>This project demonstrates how you can <strong>self-host a Vercel-like PR preview system</strong> using AWS infrastructure. Instead of relying on third-party services, we leveraged S3 for storage, CloudFront for efficient global distribution, and GitHub Actions for automation. As a developer, building your own tools not only gives you greater control but also deepens your understanding of cloud infrastructure and CI/CD workflows. If you're using AWS in your stack, taking the time to <strong>set up custom dev tools</strong> like this can save costs and provide a tailored experience. Give it a try, experiment with AWS, and start deploying your own frontend projects seamlessly!</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[When AWS Lambda Meets API Gateway]]></title><description><![CDATA[Disclaimer: This article is a continuation of the one below. If you have already read it or have a Lambda function to deploy to API Gateway, you may proceed. Otherwise, I recommend reading the previous article first.
https://writer.mrmehta.in/why-dev...]]></description><link>https://writer.mrmehta.in/when-aws-lambda-meets-api-gateway</link><guid isPermaLink="true">https://writer.mrmehta.in/when-aws-lambda-meets-api-gateway</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[aws-apigateway]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 14 Mar 2025 10:00:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739532677732/0a293f07-990b-4163-adfe-6bb8792b2a3b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Disclaimer:</strong> <strong>This article is a continuation of the one below.</strong> If you have already read it <em>or</em> have a Lambda function to deploy to API Gateway, you may proceed. Otherwise, I recommend reading the previous article first.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://writer.mrmehta.in/why-developers-are-avoiding-aws-lambda-and-how-to-master-it">https://writer.mrmehta.in/why-developers-are-avoiding-aws-lambda-and-how-to-master-it</a></div>
<p> </p>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>Now that you’ve successfully deployed an AWS Lambda function to calculate Fibonacci numbers, it's time to <strong>expose it as a REST API</strong> using AWS API Gateway.</p>
<p>Without API Gateway, your Lambda function can only be invoked through the AWS Console, CLI, or AWS SDKs. However, by integrating it with <strong>API Gateway</strong>, we can:</p>
<ul>
<li><p>Make Lambda accessible via HTTP requests.</p>
</li>
<li><p>Enable frontend and mobile apps to communicate with it.</p>
</li>
<li><p>Enhance scalability and security.</p>
</li>
<li><p>Easily control request authentication and authorisation.</p>
</li>
</ul>
<p>In this guide, we will <strong>connect your existing AWS Lambda function</strong> (which calculates Fibonacci numbers using an <code>npm</code> package) to <strong>AWS API Gateway</strong> and transform it into a fully functional API endpoint. By the end of this tutorial, you’ll have a working API that can be called via any browser, Postman, or curl request.</p>
<p><em>This tutorial will help you understand how to connect AWS Lambda to API Gateway.</em></p>
<h2 id="heading-why-should-you-integrate-api-gateway-with-aws-lambda"><strong>Why Should You Integrate API Gateway with AWS Lambda?</strong></h2>
<p>AWS Lambda <strong>alone</strong> is powerful, but combining it with API Gateway <strong>unlocks new possibilities</strong> for modern web and mobile applications.</p>
<p><strong>1. Exposing AWS Lambda as a REST API:</strong> API Gateway allows us to make Lambda functions callable via HTTP endpoints. This means your function can be accessed using a simple URL instead of relying on AWS CLI or SDKs. (Don’t worry, we will create our own shortly.)</p>
<p><strong>2. Security and Access Control:</strong> With API Gateway, we can <strong>secure</strong> our Lambda function using:</p>
<ul>
<li><p><strong>IAM roles</strong> (AWS identity-based access control)</p>
</li>
<li><p><strong>API keys</strong> (restrict access to authorised users)</p>
</li>
<li><p><strong>CORS (Cross-Origin Resource Sharing)</strong> settings</p>
</li>
</ul>
<p><strong>3. Request &amp; Response Transformation:</strong> API Gateway lets us modify the request before passing it to Lambda and format the response before sending it back to the client.</p>
<p><strong>4. Rate Limiting &amp; Caching:</strong> You can control API usage by setting rate limits and improve performance using caching mechanisms.</p>
<p><strong>5. Cost Optimisation:</strong> By integrating Lambda with API Gateway, you get a server-less, scalable, and cost-efficient API, where you only pay for execution time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741940951492/c235af6c-b53b-480a-b3c5-0a34c73637e8.png" alt class="image--center mx-auto" /></p>
<p>Now that you know some good reasons to integrate, let's jump directly to the AWS Console and create the API.</p>
<h2 id="heading-step-1-open-api-gateway-in-aws-console">Step 1: Open API Gateway in AWS Console</h2>
<ol>
<li><p>Go to the AWS Management Console.</p>
</li>
<li><p>Search for <strong>API Gateway</strong> and open it.</p>
</li>
<li><p>Click "Create API".</p>
</li>
</ol>
<p>At this point, you’ll see <strong>several API types</strong> to choose from:</p>
<ul>
<li><p><strong>REST API (Recommended)</strong> → Best for full-featured APIs.</p>
</li>
<li><p><strong>HTTP API (Cheaper &amp; Faster)</strong> → Ideal for simple integrations.</p>
</li>
<li><p><strong>WebSocket API</strong> → Used for real-time, event-driven applications.</p>
</li>
<li><p><strong>REST API Private</strong> → REST API that is only accessible from within a VPC.</p>
</li>
</ul>
<p><strong>Choose "REST API"</strong> since we need fine-grained control. Click <strong>Build</strong> to proceed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741941552530/777011b4-5903-4d78-b9a4-cb25f5422204.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-2-create-a-new-api"><strong>Step 2: Create a New API</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741942117963/0b6bdd2b-3fef-4855-8fc4-50bbcc5d288d.png" alt class="image--center mx-auto" /></p>
<p>Now, configure the new API:</p>
<ol>
<li><p>Choose "New API"</p>
</li>
<li><p>Give it a name (<code>FibonacciAPI</code>)</p>
</li>
<li><p>Set Endpoint Type</p>
<ul>
<li><p><strong>Regional</strong> (default): Best for most applications</p>
</li>
<li><p><strong>Edge-Optimised</strong>: Used for low-latency global APIs</p>
</li>
<li><p><strong>Private</strong>: For internal AWS network use</p>
</li>
</ul>
</li>
</ol>
<p><strong>Select "Regional"</strong> (best for standard use cases). Click <strong>Create API</strong>.</p>
<h2 id="heading-step-3-create-a-new-resource"><strong>Step 3: Create a New Resource</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741943574054/270f12da-cf5b-489e-b4cf-8f94ca91bc8b.png" alt class="image--center mx-auto" /></p>
<p>Now, we need to create an <strong>API resource</strong> that will serve as an endpoint for our function:</p>
<ol>
<li><p>Go to "Resources" (Left Sidebar)</p>
</li>
<li><p>Click "Create Resource"</p>
</li>
<li><p>Enter Resource Name → <code>fibonacci</code></p>
</li>
<li><p>Enable "CORS"</p>
</li>
<li><p>Click <strong>Create Resource</strong></p>
</li>
</ol>
<h2 id="heading-step-4-create-a-method-and-connect-it-to-lambda"><strong>Step 4: Create a Method and Connect It to Lambda</strong></h2>
<p>Now, let's create a <strong>POST method</strong> that will send data to our Lambda function.</p>
<ol>
<li><p>Select the <code>/fibonacci</code> resource.</p>
</li>
<li><p>Click <strong>"Create Method"</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741944172683/9c38b6fa-13b6-4dda-beec-c4fb02f5edb5.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Choose <strong>POST</strong> method from method type dropdown.</p>
</li>
<li><p>Under <strong>Integration Type</strong>, select <strong>Lambda Function</strong>.</p>
</li>
<li><p>Select your Lambda function from the dropdown from the specific region.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741944402538/b3a108cc-cc6f-49e6-9865-a8aeb9bcdcd0.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Save</strong> and <strong>OK</strong> when prompted to give permissions.</p>
</li>
</ol>
<p><strong>Your API is now linked to Lambda!</strong></p>
<h3 id="heading-other-settings-you-may-explore"><strong>Other Settings You May Explore</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741944877762/9dabe4f0-0782-4c35-8a0a-c6dfab6d0b68.png" alt class="image--center mx-auto" /></p>
<p><strong>1. Authorisation:</strong> Specifies how API Gateway should handle authentication for this method.</p>
<ul>
<li><p><strong>Options:</strong></p>
<ul>
<li><p><strong>None:</strong> No authentication is required (public API).</p>
</li>
<li><p><strong>AWS IAM:</strong> Uses AWS Identity and Access Management (IAM) permissions.</p>
</li>
</ul>
</li>
<li><p><strong>When to use it?</strong></p>
<ul>
<li><p>Use None for public APIs.</p>
</li>
<li><p>Use IAM for internal AWS services.</p>
</li>
</ul>
</li>
</ul>
<p><strong>2. Request Validator:</strong> Determines whether API Gateway should validate request parameters before invoking the backend.</p>
<ul>
<li><p><strong>Options:</strong></p>
<ul>
<li><p><strong>None:</strong> No validation.</p>
</li>
<li><p><strong>Validate only parameters &amp; headers:</strong> Checks query parameters and headers.</p>
</li>
<li><p><strong>Validate body:</strong> Validates request body based on a schema.</p>
</li>
<li><p><strong>Validate body, parameters, and headers:</strong> Validates all incoming request data.</p>
</li>
</ul>
</li>
<li><p><strong>When to use it?</strong></p>
<ul>
<li><p>Use <strong>validation</strong> when you want to enforce required query parameters, headers, or body structure before invoking Lambda.</p>
</li>
<li><p>Helps prevent invalid requests from reaching backend services.</p>
</li>
</ul>
</li>
</ul>
<p><strong>3. API Key Required:</strong> Specifies whether a client must provide an API key in the request.</p>
<ul>
<li><p><strong>When to use it?</strong></p>
<ul>
<li><p>Use it when you want to track usage and limit access to specific clients via <strong>API keys</strong>.</p>
</li>
<li><p>Ideal for rate-limiting, monetisation, or private API access.</p>
</li>
</ul>
</li>
</ul>
<p><strong>4. Operation Name (Optional):</strong> A label for the operation, helpful for logging and API documentation.</p>
<ul>
<li><p><strong>When to use it?</strong></p>
<ul>
<li><p>When you want to identify API operations easily in logs or API definitions.</p>
</li>
<li><p>Example: Naming it GetPets for a method that fetches pet data.</p>
</li>
</ul>
</li>
</ul>
<p><strong>Other expandable sections:</strong></p>
<ul>
<li><p><strong>URL Query String Parameters:</strong> Defines required/optional query parameters.</p>
</li>
<li><p><strong>HTTP Request Headers:</strong> Specifies required headers (e.g., <code>Content-Type</code>).</p>
</li>
<li><p><strong>Request Body:</strong> Defines body structure for methods like <code>POST</code> and <code>PUT</code>.</p>
</li>
</ul>
<p>These settings help configure how requests are handled before reaching the backend, improving security, validation, and access control in your API.</p>
<h2 id="heading-step-5-deploy-the-api"><strong>Step 5: Deploy the API</strong></h2>
<p>Before testing, we need to <strong>deploy the API</strong>:</p>
<ol>
<li><p>Click "Deploy API".</p>
</li>
<li><p><strong>Create a New Stage</strong> (let’s say, <code>prod</code>).</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741945149341/852ca0b5-9f84-40d3-8017-e75746048857.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Deploy</strong>.</p>
</li>
</ol>
<p>AWS will generate a <strong>public URL</strong> for your API. Copy this URL—we’ll use it for testing!</p>
<pre><code class="lang-bash">https://4kabch7a4m.execute-api.ap-south-1.amazonaws.com/prod
</code></pre>
<h2 id="heading-step-6-test-the-api"><strong>Step 6: Test the API</strong></h2>
<p>Now, let's send a <strong>POST request</strong> with input data:</p>
<p>You can use Curl or Postman for testing the endpoint:</p>
<pre><code class="lang-bash">curl --location <span class="hljs-string">'https://4kabch7a4m.execute-api.ap-south-1.amazonaws.com/prod/fibonacci'</span> \
--header <span class="hljs-string">'Content-Type: application/json'</span> \
--data <span class="hljs-string">'{
    "number": 10
}'</span>
</code></pre>
<p>You should get a response like this:</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"number"</span>: 10,
    <span class="hljs-string">"result"</span>: 55
}
</code></pre>
<p>Congratulations! You’ve successfully:</p>
<ul>
<li><p><strong>Connected AWS Lambda to API Gateway</strong></p>
</li>
<li><p><strong>Created a fully functional API</strong></p>
</li>
</ul>
<h2 id="heading-why-use-api-gateway-beyond-aws-lambda"><strong>Why Use API Gateway Beyond AWS Lambda?</strong></h2>
<p>While integrating AWS Lambda with API Gateway is a common practice, API Gateway's capabilities extend far beyond serving as a front-end for Lambda functions. It offers a robust set of features that enhance the management, security, and performance of APIs across various architectures.​</p>
<p><strong>1. Centralised API Management:</strong> <a target="_blank" href="https://aws.amazon.com/api-gateway">API Gateway</a> acts as a unified entry point for multiple backend services, simplifying the process of monitoring, versioning, and maintaining APIs. This centralised approach streamlines operations and ensures consistent API behaviour across different services. ​</p>
<p><strong>2. Enhanced Security Features:</strong> With built-in support for authentication mechanisms such as AWS Identity and Access Management (IAM) policies, Lambda authoriser functions, and Amazon Cognito user pools, API Gateway ensures secure access to APIs. It also integrates with AWS Web Application Firewall (WAF) to protect APIs from common web exploits.</p>
<p><strong>3. Traffic Management and Scalability:</strong> API Gateway efficiently handles traffic management tasks, including request throttling and burst rate limits, to protect backend services from being overwhelmed. It automatically scales to accommodate varying levels of API traffic, ensuring consistent performance during peak usage periods.</p>
<p><strong>4. Protocol Flexibility:</strong> Beyond RESTful APIs, API Gateway supports WebSocket APIs, enabling real-time two-way communication applications such as chat apps and streaming dashboards. This versatility allows developers to choose the most appropriate protocol for their application's needs.</p>
<p><strong>5. Cost Efficiency:</strong> As a fully managed service, API Gateway eliminates the need for provisioning and managing infrastructure for API hosting. Its pay-as-you-go pricing model ensures that organisations only pay for the API calls they receive and the amount of data transferred out, optimising cost management. ​</p>
<p>By leveraging these features, organisations can build secure, scalable, and high-performance APIs that cater to a wide range of use cases, independent of AWS Lambda.​</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Integrating AWS Lambda with API Gateway transforms server-less functions into robust, scalable APIs accessible over standard HTTP protocols. This integration not only simplifies the deployment of micro-services but also enhances security, performance, and monitoring capabilities. By following the steps outlined in this guide, developers can effectively expose Lambda functions via API Gateway, unlocking the full potential of server-less architectures.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in/"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Redefining DevOps with AIxBlock & ActivePieces]]></title><description><![CDATA[If you're a developer, DevOps engineer, or Web3 enthusiast, you've probably run into the pain of debugging CI/CD failures and managing automation in decentralised (or centralised) networks. But what if I told you that we could supercharge debugging w...]]></description><link>https://writer.mrmehta.in/redefining-devops-with-aixblock-and-activepieces</link><guid isPermaLink="true">https://writer.mrmehta.in/redefining-devops-with-aixblock-and-activepieces</guid><category><![CDATA[aixblock]]></category><category><![CDATA[activepieces]]></category><category><![CDATA[Devops]]></category><category><![CDATA[cicd]]></category><category><![CDATA[automation]]></category><category><![CDATA[AI]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[decentralization]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Thu, 27 Feb 2025 06:10:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740484052127/e0699624-0367-40af-a436-a2c23db2d120.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're a developer, DevOps engineer, or Web3 enthusiast, you've probably run into the pain of debugging CI/CD failures and managing automation in decentralised (or centralised) networks. But what if I told you that we could supercharge debugging with AI agents, automate DevOps workflows, and decentralise execution — <strong>all without relying on traditional cloud infrastructure?</strong></p>
<h3 id="heading-disclaimer">⚠️ Disclaimer</h3>
<p><strong>This isn’t your typical “sit back and read” blog.</strong> This is an interactive deep dive that will require you to stick around till the end and perform hands-on tasks to fully grasp <strong>how AI-driven decentralised debugging actually works.</strong> If you're looking for a quick introductory post, this might not be it — but if you're here to build, experiment, and push the boundaries of DePIN automation, you're in the right place.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before we jump in, make sure to explore <a target="_blank" href="https://aixblock.io">AIxBlock</a> and <a target="_blank" href="https://www.activepieces.com">ActivePieces</a> on their respective websites. This blog will not be an introduction to these platforms but rather a deep technical walkthrough of how they work together to enable autonomous debugging in a decentralised CI/CD pipeline.</p>
<p>Now that we’ve set the stage, let’s dive straight into building an AI-driven debugging pipeline that works seamlessly within DePIN environments.</p>
<h1 id="heading-what-were-creating">What We’re Creating</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740501090176/422465c8-7ac1-40d1-84f5-435b18e08d14.png" alt class="image--center mx-auto" /></p>
<p>Our goal? To <strong>create an AI-powered debugging pipeline</strong> that <strong>automates failure analysis</strong> using <strong>GitHub Actions, ActivePieces, and AIxBlock</strong> while ensuring that <strong>logs are intelligently formatted and sent to Slack</strong> for immediate action.</p>
<p>Here’s what we’ll be building from start to finish:</p>
<h3 id="heading-step-1-writing-the-breaking-code">Step 1: Writing the Breaking Code</h3>
<p>We’ll start by writing a JavaScript program with some predefined tests. But here’s the catch — some tests will <strong>intentionally fail</strong>. These failures will act as real-world debugging challenges that our AI agents will analyse.</p>
<h3 id="heading-step-2-triggering-the-debugging-pipeline">Step 2: Triggering the Debugging Pipeline</h3>
<p>Whenever we <strong>push code to GitHub</strong>, a GitHub Actions workflow will:</p>
<ul>
<li><p>Run the tests</p>
</li>
<li><p>Capture the logs</p>
</li>
<li><p>Send the logs to ActivePieces, which will then trigger AI analysis on AIxBlock.</p>
</li>
</ul>
<h3 id="heading-step-3-log-analysis-on-aixblock">Step 3: Log Analysis on AIxBlock</h3>
<p>Once the logs reach <strong>AIxBlock</strong>, a set of AI agents will:</p>
<ul>
<li><p>Use OpenAI, Gemini, and other models to analyse the failure logs</p>
</li>
<li><p>Identify patterns, suggest fixes, and explain errors</p>
</li>
<li><p>Format the insights into a structured response</p>
</li>
</ul>
<h3 id="heading-step-4-sending-ai-formatted-logs-to-slack">Step 4: Sending AI-Formatted Logs to Slack</h3>
<p>The response will be <strong>sent back to an ActivePieces workflow</strong>, which is listening for responses from AIxBlock. This workflow ensures that:</p>
<ul>
<li><p>The AI output is structured</p>
</li>
<li><p>It is properly formatted for readability</p>
</li>
</ul>
<p>Finally,</p>
<ul>
<li><p>Take the AI-generated debugging insights</p>
</li>
<li><p>Format them into Slack-compatible markdown</p>
</li>
<li><p>Send the final structured report to a Slack channel</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740504896192/842eb69c-3e30-4528-9804-fca0b206a7ca.png" alt class="image--center mx-auto" /></p>
<p>By the end of this guide, you won’t just understand how to automate debugging with AI — you’ll have built a <strong>fully functional decentralised debugging pipeline</strong> that’s ready for real-world use.</p>
<h1 id="heading-1-github-actions-activepieces">#1 GitHub Actions → ActivePieces</h1>
<p>To get started instantly, fork this repository: <a target="_blank" href="https://github.com/kartikmehta8/AIxBlock-Activepieces-Kit"><strong>AIxBlock-ActivePieces-Kit</strong></a></p>
<p>I have already set up:</p>
<ul>
<li><p>A <strong>basic JavaScript project</strong> with test cases (one will fail intentionally).</p>
</li>
<li><p>A <strong>GitHub Actions workflow</strong> to run tests on every push to <code>main</code>.</p>
</li>
</ul>
<h3 id="heading-core-files">Core Files</h3>
<ul>
<li><p><code>mathUtils.js</code> → A simple JavaScript file with basic math functions.</p>
</li>
<li><p><code>mathUtils.test.js</code> → A Jest test suite that includes an <strong>intentional failure</strong> to trigger AI debugging.</p>
</li>
<li><p><code>runTest.js</code> → The script that:</p>
<ul>
<li><p>Runs tests</p>
</li>
<li><p>Captures failure logs</p>
</li>
<li><p>Sends logs to ActivePieces &amp; AIxBlock for processing</p>
</li>
</ul>
</li>
<li><p><code>.github/workflows/test-and-send-results.yml</code> → GitHub Actions workflow that:</p>
<ul>
<li><p>Triggers on push to <code>main</code></p>
</li>
<li><p>Installs dependencies (<code>npm install</code>)</p>
</li>
<li><p>Calls <code>runTest.js</code> . (Make sure to configure the <code>WEBHOOK_URL</code> in this file, which we will receive in <strong>another section</strong>.)</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-running-the-workflow">Running the Workflow</h3>
<ol>
<li><p>Fork the repository and clone it:</p>
<pre><code class="lang-bash"> git <span class="hljs-built_in">clone</span> https://github.com/YOUR_USERNAME/AIxBlock-Activepieces-Kit.git
</code></pre>
</li>
<li><p>Push any changes to <code>main</code>, and GitHub Actions will:</p>
<ul>
<li><p>Run tests</p>
</li>
<li><p>Capture the logs</p>
</li>
<li><p>Send these logs to ActivePieces using the web hook</p>
</li>
</ul>
</li>
<li><p><strong>Listening on ActivePieces:</strong> (We'll configure this next!)</p>
</li>
</ol>
<h1 id="heading-2-activepieces-aixblock">#2 ActivePieces → AIxBlock</h1>
<p>Now that our GitHub Actions workflow is sending failure logs to ActivePieces, it’s time to build the flow to capture these logs and send them to AIxBlock for analysis.</p>
<h2 id="heading-initial-activepieces-workflow">Initial ActivePieces Workflow</h2>
<ol>
<li><p>Create an account on <a target="_blank" href="https://cloud.activepieces.com">ActivePieces Cloud</a>. You will then be redirected to the dashboard.</p>
</li>
<li><p>Click on <code>New Flow &gt; From Scratch</code> on the dashboard.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740548008748/853b97a6-7792-4b33-83d2-767011337f0e.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>You will be redirected to the page where you can create the workflow. Click on the dropdown for "Empty Trigger." This trigger will listen to the GitHub Actions logs. Search for "Webhook" and then click on <strong>"Catch Webhook"</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740548346272/a8e4b54d-4c6d-41e3-85b8-21f9aaf1cec1.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>On the right-hand side, you will see the settings for the web hook and the <strong>LIVE URL</strong>. You need to update this as <strong>WEBHOOK_URL</strong> in the code (<code>runTest.js, line 4</code>), and don’t forget to append <strong>/sync</strong> at the end. Leave the other settings as default.</p>
<p> If you want to test whether this is working, update the <strong>WEBHOOK_URL</strong>, click on “Test Trigger”, <strong>push a commit</strong>, and you should receive a response from GitHub Actions in the body of the response, something like this:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740549044798/50e3b59c-34cc-48f7-9560-06c004a57d93.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<pre><code class="lang-json">{
  <span class="hljs-attr">"method"</span>: <span class="hljs-string">"POST"</span>,
  <span class="hljs-attr">"headers"</span>: {
    <span class="hljs-attr">"connection"</span>: <span class="hljs-string">"Upgrade"</span>,
    ...
    <span class="hljs-attr">"x-real-ip"</span>: <span class="hljs-string">"172.11.11.11"</span>
  },
  <span class="hljs-attr">"body"</span>: {
    <span class="hljs-attr">"repository"</span>: <span class="hljs-string">"AIxBlock-AI-Agent"</span>,
    <span class="hljs-attr">"branch"</span>: <span class="hljs-string">"main"</span>,
    <span class="hljs-attr">"logs"</span>: <span class="hljs-string">"{\n  \"details\": \"\\n&gt; aixblock-ai-agent@1.0.0 test\\n&gt; jest\\n\\nFAIL 
            ./mathUtils.test.js\\n  ✓ Addition works correctly (2 ms)\\n  
            ✓ Subtraction works correctly (1 ms)\\n  ✓ Multiplication works correctly\\n  
            ✓ Division by non-zero number (1 ms)\\n  ✕ Intentional Failure (2 ms)\\n\\n  
            ● Intentional Failure\\n\\n    expect(received).toBe(expected) // Object.is 
            equality\\n\\n    Expected: false\\n    001b[39m\\n    \\u001b[31mtal\\nTime: 
            0.509 s\\nRan all test suites.\\n\"\n}"</span>
  },
  <span class="hljs-attr">"queryParams"</span>: {}
}
</code></pre>
<ol start="5">
<li><p>Now, we need to send this to AIxBlock.</p>
<p> Click on the <strong>plus icon</strong>, then search for <strong>HTTP</strong> and click on it. You will see an option to add a block called <strong>"Send HTTP Request."</strong> Click on it.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740550309422/de84ce18-0424-42df-94e6-a40c14635fe4.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>You will see the settings for this section. One of the best features of <strong>ActivePieces</strong> is that you can use the results from previous blocks here.</p>
<p> Now, let’s configure the settings as follows:</p>
<ol>
<li><p>Method: POST</p>
</li>
<li><p>URL: (don’t worry, we will be creating that right after this step.)</p>
</li>
<li><p>Headers: <code>platform:activepieces</code></p>
</li>
<li><p>Query params: <code>platform:activepieces</code></p>
</li>
<li><p>Body Type: JSON</p>
</li>
<li><p>JSON Body:</p>
</li>
</ol>
</li>
</ol>
<pre><code class="lang-json">{
  <span class="hljs-attr">"repository"</span>: <span class="hljs-string">"{{trigger['body']['repository']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"branch"</span>: <span class="hljs-string">"{{trigger['body']['branch']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"logs"</span>: <span class="hljs-string">"{{trigger['body']['logs']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"webhook"</span>: <span class="hljs-string">"WE WILL UPDATE THIS WHEN WE BE GOING TO SEND THE DATA TO SLACK. CHILL!"</span>
}
</code></pre>
<h2 id="heading-create-ai-agents-on-aixblock">Create AI Agents on AIxBlock</h2>
<p>We have completed the initial setup of ActivePieces, and now comes the exciting part — creating the <strong>AI Agent workflow</strong> on <strong>AIxBlock</strong>. Let’s get started!</p>
<p><strong>For you:</strong> Public page of <a target="_blank" href="https://multiagent.aixblock.io/crew-voting/67bd6246471a83f0355e05c9">this AIxBlock Agent</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740551561619/7a65e951-9586-4949-b902-39edd8aa1a32.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Create an account on <a target="_blank" href="https://multiagent.aixblock.io">multiagent.aixblock.io</a>.</p>
</li>
<li><p>The first thing you need to do is set up the storage. Click on <strong>“Set Storage Now.”</strong></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740552362050/c1efd42f-ffe9-4842-9bad-e9f880fb5ef5.png" alt class="image--center mx-auto" /></p>
<p> The easiest setup is to use the <strong>AIxBlock storage</strong> provided by their platform. Select this option, enter a name, and click <strong>Add</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740552498039/f67353f0-7b5f-497f-9523-ec9142c69095.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Now, let's add the <strong>models</strong> that our AI agent will use for log analysis.</p>
<ol>
<li><p>Navigate to <code>My Library &gt; Model Management</code>.</p>
</li>
<li><p>On the right-hand side, click <strong>“Create”</strong>.</p>
</li>
<li><p>Now, you need to add a provider (such as OpenAI or Gemini), specify the model name, and enter the platform-specific API key.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740553385136/1ea49c7b-65c6-4658-a542-d32e89acfe37.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Select a provider (e.g., OpenAI or Gemini).</p>
</li>
<li><p>Enter the model name.</p>
</li>
<li><p>Provide the API key for the selected platform.</p>
</li>
<li><p>Click "Validate" to ensure the configuration is correct.</p>
</li>
<li><p>Add the model based on your preference.</p>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<p>        For now, I will add <strong>two models</strong>: <strong>OpenAI</strong> and <strong>Gemini</strong>.</p>
<p>        <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740553420861/788dc848-00b0-4b47-abb2-8247cdee81ac.png" alt class="image--center mx-auto" /></p>
<ol start="4">
<li><p>Now, let's <strong>select Tools</strong>. These tools will help in searching for solutions from the web and retrieving relevant information (RAG).</p>
<ol>
<li><p>Go to <code>My Library &gt; Tools</code>.</p>
</li>
<li><p>Click on <strong>“Create”</strong>.</p>
</li>
<li><p>Some tools <strong>do not</strong> require an API key, while others do.</p>
</li>
<li><p>For tools like <a target="_blank" href="https://serper.dev">Serper</a>, visit their platform to obtain an API key, then add it to the tool.</p>
</li>
<li><p>Select the tools you need (as specified in the image).</p>
</li>
<li><p>Click <strong>“Add Tools”</strong> to finalise.</p>
</li>
</ol>
</li>
</ol>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740553800494/584c942e-991f-44a9-96f8-746f060e0d6b.png" alt class="image--center mx-auto" /></p>
<p>    <strong>Note:</strong> Make sure to add an API key for <a target="_blank" href="https://serper.dev">Serper</a>.</p>
<ol start="5">
<li><p>Now, let’s start creating a <strong>crew of agents</strong> — this is where the real magic happens! A Crew is a group of agents working together to perform a specific task. Go to <code>AI Agents Crew</code> and click on <strong>“Create”</strong>.</p>
</li>
<li><p>The first step is to <strong>add the basic details of the crew</strong>. I have attached a screenshot of the values I have selected. After adding them, click <strong>"Create"</strong>. (Note: Add variables, <code>repository</code>, <code>branch</code>, and <code>logs</code> which we will receive from ActivePieces HTTP request.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740555102684/0e970630-29b3-4141-b4b8-b21510d72cb7.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Next, we need to add the AI Agents (this is what we're here for, right?). I will add two agents:</p>
<ol>
<li><p><strong>Log Analyser</strong> (Analyses logs, reports problems, provides answers with links. Returns in string format.)</p>
</li>
<li><p><strong>Markdown Formatter</strong> (Converts the string text into markdown code.)</p>
</li>
</ol>
</li>
</ol>
<p>    I am attaching images of both agents and their details:</p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740556189580/b58a149c-6012-4474-ab3a-cf49ce83b453.png" alt class="image--center mx-auto" /></p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740556458112/d8b0af1d-5a60-42ed-a39a-7be9e4bf29fb.png" alt class="image--center mx-auto" /></p>
<p>    Obviously, you can improve the prompts. Also, remember we added the three variables - <code>repository</code>, <code>branch</code>, and <code>logs</code>. <strong>We can add them here by using</strong> <code>{}</code> <strong>and including the variable name.</strong></p>
<p>    Alright, so we have successfully created the two agents. Now let's set up the task and flow. Click <strong>“Next“</strong>.</p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740556740612/69e28eff-e4e9-454c-bf4b-2537376a4a72.png" alt class="image--center mx-auto" /></p>
<ol start="8">
<li><p>This is where you create the flow of agents, defining what each will do and what each will return. Now you will be using your agents to create a pipeline where <strong>you are going to connect all the pieces</strong>. To add a task, click on <strong>"Add Task"</strong>.  </p>
<p> A task consists of the following:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740557349169/1c136aab-1829-402e-ad5b-6c9003eec517.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><strong>Description:</strong> A brief overview of what the agent will do.</p>
</li>
<li><p><strong>Expected Output:</strong> The desired format or content of the output.</p>
</li>
<li><p><strong>Raw / Structured Output:</strong> Choose whether you want a raw output or a formatted one (e.g., JSON, CSV, etc.).</p>
</li>
<li><p><strong>Assign to Agent:</strong> Select the agent responsible for performing this task.</p>
</li>
<li><p><strong>Context:</strong> Any relevant previous task context or knowledge the flow needs to be aware of.</p>
</li>
</ul>
</li>
</ol>
<p>    I am attaching an image of what needs to be done so far. Please refer to it.</p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740557764203/f4d365fc-6f7f-4fc1-af2e-b44c49ff33fa.png" alt class="image--center mx-auto" /></p>
<ol start="9">
<li>Click <strong>“Next“</strong>. Click <strong>“Publish now“</strong>.</li>
</ol>
<p>    Congratulations! <strong>You have created your first AI Pipeline on AIxBlock.</strong> You should have received the API endpoint and the JSON schema of what needs to be sent to this endpoint. (<a target="_blank" href="https://multiagent.aixblock.io/api/v1/execute/result/67bd6246471a83f0355e05c9">This is the endpoint</a> we have created so far.)</p>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740558188793/e78ad80f-c5d0-4188-9e00-76bff78a217f.png" alt class="image--center mx-auto" /></p>
<ol start="10">
<li><p><strong>Important:</strong> Copy the <strong>API Endpoint</strong> and paste it into the <strong>URL</strong> field in the ActivePieces workflow, where we are sending the POST request.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740559184544/aa2ac86c-76fb-4db0-9814-ceab5a5c7a3f.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<p>Alright! The last thing we need is the <strong>Webhook URL</strong>, which we will send in this <strong>POST request</strong> (remember?).</p>
<p>To achieve this, we need to <strong>create another ActivePieces workflow</strong> that will:</p>
<ul>
<li><p><strong>Listen</strong> to the endpoint</p>
</li>
<li><p><strong>Format</strong> the AI-generated response</p>
</li>
<li><p><strong>Send</strong> it to Slack</p>
</li>
</ul>
<p>Let's get started and create this workflow!</p>
<h1 id="heading-3-activepieces-slack">#3 ActivePieces → Slack</h1>
<p>Let's fix the POST request we need to send to <strong>AIxBlock</strong>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"repository"</span>: <span class="hljs-string">"{{trigger['body']['repository']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"branch"</span>: <span class="hljs-string">"{{trigger['body']['branch']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"logs"</span>: <span class="hljs-string">"{{trigger['body']['logs']}}"</span>, <span class="hljs-comment">// Data from previous block.</span>
  <span class="hljs-attr">"webhook"</span>: <span class="hljs-string">"LET's FIX THIS URL"</span>
}
</code></pre>
<ol>
<li><p>Click on <code>New Flow &gt; From Scratch</code> on the dashboard.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740563568830/c229bd2e-cf72-4b37-8b96-bf877d37b4cf.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>You will be redirected to the page where you can create the workflow. Click on the dropdown for "Empty Trigger." <strong>This trigger will listen to the response from AIxBlock.</strong> Search for "Webhook" and then click on <strong>"Catch Webhook"</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740563996032/7d06e700-3291-4502-b16f-9095eb36f86a.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>On the right-hand side, you will see the settings for the web hook and the <strong>LIVE URL</strong>. You need to update this as <strong>WEBHOOK_URL</strong> in the JSON body (above), and don’t forget to append <strong>/sync</strong> at the end. Leave the other settings as default.</p>
</li>
</ol>
<p>Till now, you have configured everything—from <strong>GitHub Actions</strong> to <strong>sending logs to AIxBlock</strong>, receiving the <strong>AI-generated response</strong>, and capturing it again in <strong>ActivePieces</strong>. Now, let’s <strong>format</strong> the response and <strong>send it to Slack</strong>!</p>
<ol start="4">
<li><p>Now, we need to format the response we will receive from the web hook.</p>
<p> Click on the <strong>plus icon</strong>, then search for <strong>“markdown to slack format“</strong> and click on it. You will see an option to add a block called <strong>"Markdown to Slack format."</strong> Click on it.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740564703482/5fc887a1-bf07-436b-9dfc-5f03051ea1d8.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>On the right-hand side, you will see an input box for entering the <strong>Markdown</strong> format.</p>
<ol>
<li><p>Click on it.</p>
</li>
<li><p>Dynamically insert variables from the received response by selecting them from the list.</p>
</li>
<li><p>Format the message as shown in the image below.</p>
</li>
</ol>
</li>
</ol>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740564978368/f0c69866-bb58-4e5b-ae1e-9f0be52a2489.png" alt class="image--center mx-auto" /></p>
<ol start="6">
<li><p>Alright! Now that our response is in Slack Markdown format, the final step is to <strong>connect ActivePieces to our Slack workspace</strong> and send the formatted message to the selected channel.</p>
</li>
<li><p>Click on the <strong>plus icon</strong>, then search for <strong>“send message to“</strong> and click on it. You will see an option to add a block called <strong>"Send Message to A Channel"</strong> Click on it.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740565555893/d407ee8b-a672-49f9-b865-251df62ac24c.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>On the right-hand side, you can see the settings for this block. First, let’s create a connection to our <strong>Slack workspace</strong>. Click on <strong>“Create Connection.”</strong> Connect your slack workspace.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740565838279/48d0f77f-dc92-4713-928c-afeafeb17943.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>After successfully connecting the workspace you will be able to see the <strong>list of channels</strong> in the dropdown below the connection field.</p>
</li>
</ol>
<h3 id="heading-note">Note:</h3>
<p>Please make sure add the bot to the channel by following these steps:</p>
<ol>
<li><p>Type <code>/invite</code> in the channel's chat.</p>
</li>
<li><p>Click on Add apps to this channel.</p>
</li>
<li><p>Search for and add the bot.</p>
</li>
</ol>
<p>If you can't find the channel in the dropdown list (which fetches up to 2000 channels), please click on the <strong>(F)</strong> and type the channel ID directly.</p>
<ol start="10">
<li><p>Select the appropriate <strong>channel</strong> from the list where you want to receive the message.</p>
</li>
<li><p>For the <strong>message</strong>, directly select the <strong>formatted response</strong> from the previous block and leave the other settings as default.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740566177018/efb6ed16-79e1-4311-a000-ccacdb50d1e6.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<p>After successfully running this pipeline, you will receive a message on Slack that looks something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740577563196/3e48e4a1-808c-4fe0-bcd0-4bb42e3f5d5b.png" alt class="image--center mx-auto" /></p>
<p>Give yourself a <strong>pat on the back</strong>—you've successfully assembled all the pieces of the pipeline and built your very own Log Analysis Pipeline using <strong>AIxBlock!</strong> 🎉</p>
<h1 id="heading-watch-it-in-action">Watch It in Action</h1>
<p>Seeing is believing! Instead of just reading about how this works, watch the <strong>full walkthrough</strong> of our <strong>AI-powered decentralised debugging pipeline</strong> in action.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/mZJsOskpElo">https://youtu.be/mZJsOskpElo</a></div>
<p> </p>
<p>Now, let’s wrap things up and discuss the future of AI-powered decentralised automation!</p>
<h1 id="heading-not-just-another-fancy-ai-tool">Not Just Another Fancy AI Tool</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740569839543/91959dbd-374f-4434-84bc-48ccab334f75.png" alt class="image--center mx-auto" /></p>
<p>This project addresses a critical gap in modern software development—<strong>automating debugging and failure analysis using decentralised AI agents</strong>. Traditional CI/CD pipelines rely heavily on centralised platforms, leading to high costs, single points of failure, and limited scalability. By leveraging ActivePieces for automation and AIxBlock for decentralised AI execution, this solution provides a <strong>more resilient, scalable, and cost-effective alternative</strong> that can be easily adapted across different industries. Businesses looking to streamline DevOps processes can integrate this workflow to reduce downtime, accelerate issue resolution, and optimise developer efficiency.</p>
<p>Beyond its business potential, the value this project brings is in <strong>enhancing debugging efficiency and reducing manual intervention in software maintenance.</strong> Rather than relying on engineers to sift through logs and identify errors, the AI agent <strong>analyses failures in real-time, suggests solutions, and delivers structured insights directly to Slack</strong>, making debugging faster and more actionable.</p>
<p>This concept can be <strong>extended beyond DevOps</strong>—for example, AI-driven cybersecurity workflows that analyse threat logs, or blockchain monitoring systems that detect vulnerabilities in smart contracts. By combining AI reasoning with decentralised automation, this project lays the groundwork for a <strong>new standard in autonomous, intelligent infrastructure management.</strong></p>
<h1 id="heading-thank-you-for-reading"><strong>Thank You for Reading!</strong></h1>
<p>If you’ve made it this far, <strong>huge respect to you!</strong> 🎉</p>
<p>I hope this guide gave you something new to think about and inspired you to experiment with AI-powered workflows in DePIN environments. Whether you’re a developer, DevOps engineer, or just an automation enthusiast, there’s so much potential in combining AI with decentralised infrastructure.</p>
<p>If you ever need help or just want to chat, <strong>DM me on Twitter / X or LinkedIn.</strong></p>
<p><a target="_blank" href="https://www.mrmehta.in"><strong>Kartik Mehta</strong></a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><strong><em>X</em></strong></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><strong><em>LinkedIn</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Why step out when the internet pays?]]></title><description><![CDATA[Once upon a time, for every ambitious Indian developer, there was only one golden ticket to tech success — the mighty H1B visa. The dream was simple: crack DSA, grind LeetCode, get into FAANG, and fly off to the land of opportunity. But for me, Karti...]]></description><link>https://writer.mrmehta.in/why-step-out-when-the-internet-pays</link><guid isPermaLink="true">https://writer.mrmehta.in/why-step-out-when-the-internet-pays</guid><category><![CDATA[remote work]]></category><category><![CDATA[superteamDAO]]></category><category><![CDATA[workfromhome]]></category><category><![CDATA[remote-working]]></category><category><![CDATA[remote]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sun, 23 Feb 2025 14:42:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740294570293/77900a69-38c8-4466-a354-5e3c5871d7a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Once upon a time, for every ambitious Indian developer, there was only one golden ticket to tech success — the mighty <strong>H1B visa</strong>. The dream was simple: crack DSA, grind LeetCode, get into FAANG, and fly off to the land of opportunity. But for me, Kartik Mehta (a.k.a. <a target="_blank" href="https://www.mrmehta.in">Mehta</a> for the cool ones), the dream wasn’t about crossing oceans. It was about crossing <strong>WiFi routers.</strong></p>
<p>Honestly, I never had the H1B dream. Not because I didn’t want the dollars, but because I knew that the internet made geography irrelevant. Remote work promised <strong>global pay, flexible hours, and zero commute</strong>, all while keeping me close to my family and friends!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740296542840/2e445299-332a-41bd-b720-d20f7e788d87.png" alt class="image--center mx-auto" /></p>
<p>Hi! I’m a <strong>Software Engineer</strong> at <a target="_blank" href="https://rtcamp.com">rtCamp</a>, currently living in <strong>Ghaziabad (NCR), UP</strong>, but for a good chunk of the year, you’ll find me sipping chai in <strong>Manali</strong> at my <strong>maternal grandparents' home</strong> — working remotely with the mountains as my backdrop. I’ll share more about my journey as a nomad programmer as you continue reading this blog. But for now, I don’t want to overwhelm you with my professional story. Instead, let me take you back to where it all began — my early days of programming.</p>
<h1 id="heading-how-it-all-began">How It All Began</h1>
<p>Like every other Class 11 student who chose Computer Science, I was handed <em>the legendary book</em> —<strong>Computer Science with C++ by Sumita Arora</strong>. Thick, intimidating, and filled with code snippets that looked like ancient spells, it was my first real introduction to C++. At first, I wasn’t sure why we were torturing ourselves with semicolons and curly braces when Excel already existed. But then came <strong>pattern printing programs</strong>, and everything changed. (I don’t know why, but I still like pattern printing, haha) There was something oddly satisfying about making stars appear in the shape of a triangle, a square, or even a pyramid—all with just a few lines of code.</p>
<p>The moment I saw my first <strong>"Hello, World!"</strong> program run successfully, I knew there was no turning back. Programming wasn’t just a subject anymore — it was a puzzle, a challenge, and honestly, a little bit of magic.</p>
<p>And just like that, I was hooked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740298400710/f6d62ff0-5b4e-43a8-952c-3339fd927b2c.png" alt class="image--center mx-auto" /></p>
<p>Fast-forwarding to college, which I attended at <a target="_blank" href="https://www.kiet.edu">KIET Group of Institutions, Ghaziabad</a>, I found myself surrounded by people who weren’t as ambitious about their goals as I was (<strong>though, of course, there were exceptions</strong>). I still tried to engage in college activities and get involved in the coding culture on campus.</p>
<p>By the time I hit my second year of college, while many were religiously grinding Data Structures &amp; Algorithms, dreaming of FAANG jobs, I found myself on a different path. Sure, I gave DSA a shot, but soon, development caught my eye, and that was it. I dived headfirst into everything I could get my hands on: Web Development, Mobile Development, Blockchain, Cloud, DevOps — you name it. While most of my peers stuck to a single tech stack throughout college, I had a different approach: <strong>why settle for one when you can explore it all?</strong> And honestly, I still do! (Just check my <a target="_blank" href="https://x.com/kartik_mehta8"><strong>X [a.k.a Twitter]</strong></a>, where I randomly post about whatever new tech fascinates me.)</p>
<p>Back then, I had this grand dream of launching my own firm, but well… college life had its own plans. I couldn’t make it happen then, but guess what? <strong>Now I have one.</strong> (More on that in the next section—stay tuned!)</p>
<h1 id="heading-until-i-landed-my-first-remote-job">Until I Landed My First Remote Job</h1>
<h2 id="heading-the-grind">The Grind</h2>
<p>The early days of job hunting? Brutal! Imagine this - every week in my 3rd year of college, I was applying for 20-25 jobs, hoping to land a remote role. And no, I’m not exaggerating!</p>
<p>I must’ve crossed <strong>100+</strong> applications, yet most of them didn’t even make it past the automated rejection wall. Some companies rejected me so fast that I’m convinced they had my name on a blacklist, haha. Even today, my resume doesn’t make it past certain “renowned” hiring platforms. (Seriously, if anyone reading this knows why, please <a target="_blank" href="https://www.mrmehta.in">hit me up</a>!)</p>
<p>I tried everything,</p>
<ul>
<li><p>Twitter / LinkedIn direct messages, Cold emails</p>
</li>
<li><p>Referrals (didn’t get many)</p>
</li>
<li><p>Company websites &amp; portals (where job applications go to die)</p>
</li>
<li><p>Wellfound (because startups have vibes)</p>
</li>
</ul>
<h2 id="heading-the-one-that-hurt-the-most">The One That Hurt the Most</h2>
<p>Ah, January 2023, the time when I almost had a dream part-time gig. A company that shall not be named had taken me through all their interview rounds. It is a US-based startup that works on creating Alexa skills for their clients. The pay was set at $20/hr — not bad for my experience at the time. I was <strong>this close</strong> to signing the contract. And then?</p>
<p><strong>GHOSTED</strong></p>
<p>Just like that. One minute I was discussing salary details, the next, I was left on “read” because they had a sudden hiring freeze. I was shattered, but <strong>giving up was never an option.</strong> I knew my worth, and I knew my time was coming.</p>
<p>You know the worst thing here? <strong>Cracking an interview? Easy. Getting an interview? A nightmare.</strong> For every 50 applications, I’d get maybe one interview call. And no matter how confident I was, the constant rejections and ghosting started messing with my mind.</p>
<p>Did I ever think of giving up on remote jobs? <strong>Never.</strong> One big reason? <em>Waking up five minutes before standup syncs.</em> The thought of waking up at 7 AM to commute to an office? <strong>Absolutely not happening.</strong></p>
<h2 id="heading-my-first-remote-job-at-push-protocol">My First Remote Job at Push Protocol</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740304438403/2ae19e1b-ab50-4e02-87f4-92e731eac04e.png" alt class="image--center mx-auto" /></p>
<p>Finally, my breakthrough moment. I came across <a target="_blank" href="https://push.org"><strong>Push Protocol (Push Chain)</strong></a> while browsing Wellfound (formerly AngelList). They had a Rockstar Intern opening, and something told me — this is it. I applied and, to my surprise, got a response. My interview was taken by <a target="_blank" href="https://x.com/pranshurastogii"><strong>Pranshu Rastogi</strong></a>, Head of Ecosystem &amp; Integrations at Push Chain. It was one of those conversations where everything just <em>clicked</em>. He saw my potential, and before I knew it, I was offered an internship as <strong>"Intern – Ecosystem &amp; Growth."</strong></p>
<p>This wasn’t just another internship, it was a life-changing experience where I learned:</p>
<ul>
<li><p>Support the growth and expansion of ecosystem, leveraging the understanding of the web3 culture and landscape.</p>
</li>
<li><p>Collaborate with cross-functional teams to identify, design, and implement process improvements and new features.</p>
</li>
<li><p>Onboarding people smarter than me to Push's Ecosystem - took around 35 interviews.</p>
</li>
<li><p>Managing Push Ambassador India program (1st iteration).</p>
</li>
</ul>
<p>After countless rejections, ghostings, and second-guessing myself, getting that offer email was pure validation<strong>.</strong> I had <strong>officially entered the remote work world</strong>, proving to myself that I belonged here.</p>
<p>After Push Protocol, my career kept evolving:</p>
<ol>
<li><p>Part-time Full-Stack Developer at <a target="_blank" href="https://x.com/Social3Club">Social3</a></p>
</li>
<li><p>Currently a Software Engineer at <a target="_blank" href="https://rtcamp.com">rtCamp</a></p>
</li>
</ol>
<p>Looking back, the journey was messy, unpredictable, and exhausting, but worth every bit of effort. If you’re an aspiring developer looking to break into remote work, trust me, it’s possible. Keep learning, keep applying, and <strong>never let rejections define you.</strong></p>
<h1 id="heading-a-day-in-my-life">A Day in My Life</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740305229645/bcfdc8cc-62c8-4914-b9fb-a512594743d5.png" alt class="image--center mx-auto" /></p>
<p>I <em>technically</em> start my workday at 10:30 AM, but let’s be honest — I <strong>wake up at 10:20 AM</strong>. That means exactly 10 minutes to go from “half-asleep zombie” to “fully functional software engineer.”</p>
<p>First things first, I check Slack<strong>.</strong> Not for work, though, I just want to see who’s online and who isn’t. If my project manager is missing, I know I have a grace period<strong>.</strong> If my favourite people are online, I drop a casual good morning.</p>
<p>Then comes the daily standup<strong>.</strong> While some people prepare reports and task updates, my approach is simple:</p>
<ul>
<li><p>Yeah, I’m working on stuff.</p>
</li>
<li><p>Stuff that I may or may not have already completed but haven't pushed yet.</p>
</li>
<li><p>Stuff that sounds technical enough so no one questions me.</p>
</li>
</ul>
<p>After successfully surviving standup, it's time to <strong>get serious.</strong> Or at least, try.</p>
<p>I sit at my dedicated desk setup, which isn’t exactly "Pinterest aesthetic." (below)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740306940063/d9bb6af1-a21c-40a3-b89a-39b24fc98e2b.jpeg" alt class="image--center mx-auto" /></p>
<ul>
<li><p>Messy cables everywhere.</p>
</li>
<li><p>A wall rug of an NFT (because why not).</p>
</li>
<li><p>Another laptop playing Beyblade episodes.</p>
</li>
</ul>
<p>I start coding while listening to Punjabi songs (for me, its Karan Aujla &amp; Shubh), eating constantly, and switching tabs between Slack, Twitter, and GitHub. Am I a focused single-tasker? Absolutely not. Everything is open at once — Slack, VS Code, YouTube, GitHub, and a random Jira page that I opened but never actually use.</p>
<p>Post-work, I dive into my side projects and my software agency, <a target="_blank" href="https://www.tailwine.in">Tailwine</a>. Whether it's coding something new, collaborating with other devs, or just pushing commits to make my GitHub graph look cool, I try to maximize this time. Even after "official" work hours, I sometimes end up coding late into the night — either for side projects, open-source contributions, or just random <a target="_blank" href="https://github.com/kartikmehta8">experiments</a> that I may or may not finish<strong>.</strong></p>
<p>But you know what the best part of a remote job is? <strong>You can always pretend you're working even when you aren't!</strong> I'm on a spree to visit and explore different states. So far, I've visited <em>Jaipur, Ladakh, Manali, Amritsar, Ahemdabad</em>, and various other places without taking any official leave. You just work at night and travel during the day.</p>
<p>Don't you think it's super cool?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740308123778/8e1ee1bd-79dc-4ee7-8361-b57bcf9a0027.jpeg" alt class="image--center mx-auto" /></p>
<h1 id="heading-the-struggles-no-one-talks-about">The Struggles No One Talks About</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740318670194/5f3381fe-57ce-4e3e-b877-74bd06b3337c.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-when-work-steals-your-night">When Work Steals Your Night</h2>
<p>Working remotely for a US client means my schedule isn’t always in my control. Late-night meetings at <strong>10:30 or 11 PM (IST)</strong> are pretty normal, and let’s just say, they’ve ruined my sleep schedule beyond repair.</p>
<p>Some nights, I tell myself, <em>"I'll take a quick nap before the meeting."</em><br />Reality? I wake up in panic mode at 10:32 PM, scrambling to open my laptop while still half-asleep.</p>
<p>The worst part? Looking at my colleagues on Zoom, fresh, bright, and full of energy (because it’s daytime for them). Meanwhile, I’m there, trying not to yawn, sipping coffee like it’s an energy drink, and pretending I understand the discussion when, in reality, my brain stopped functioning an hour ago.</p>
<p>And let’s be real, there is no sleep cycle in remote work. It’s a <strong>never-ending loop of waking up, opening the laptop, and working.</strong></p>
<h2 id="heading-the-cost-of-freedom">The Cost of Freedom</h2>
<p>One of the biggest perks of remote work is <strong>freedom</strong> — I can <strong>work from anywhere, travel whenever I want,</strong> and escape the boring 9-5 office grind. But the downside? <strong>Less social interaction.</strong></p>
<p>There are times when I miss the casual office gossip, the random tea breaks with colleagues, and just having real human interaction beyond Slack messages. But at the same time, remote work also means I can party every weekend and not worry about waking up early for office the next day. (Best of both worlds? Maybe.)</p>
<h2 id="heading-we-belong-to-an-indian-family">We belong to an Indian Family</h2>
<p>If you work remotely in an Indian household, you already know — <strong>family members don’t believe you’re actually working :).</strong></p>
<p>One time, I was on a serious meeting with my Project Manager, discussing timelines and deliverables, when my mom casually walked in and said:<br /><em>"Kartik, lunch ready hai!"</em> (Kartik, the lunch is ready!)</p>
<p>I tried signaling her that I was in a meeting, but nope — she stood there waiting, like this was my top priority<strong>.</strong> So now, I had two choices:</p>
<ol>
<li><p>Mute my mic, rush to the kitchen, and return before anyone noticed.</p>
</li>
<li><p>Pretend I didn’t hear her and face the consequences later.</p>
</li>
</ol>
<p><em>Spoiler:</em> I chose <strong>Option 2.</strong> And let’s just say, my mom was <em>not</em> pleased afterward, haha.</p>
<h2 id="heading-expectation-vs-reality">Expectation vs. Reality</h2>
<p>Remote work isn’t just chilling in pajamas, working from beaches, and sipping coffee in a fancy cafe. It comes with its own set of struggles. But at the end of the day? <strong>It’s still worth it.</strong> The flexibility, the global opportunities, and the ability to build my career on my own terms outweigh the occasional WiFi disasters.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Expectation</strong></td><td><strong>Reality</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Working from a beach with a laptop</td><td>Sweating because the WiFi router isn’t working</td></tr>
<tr>
<td>Flexible work hours</td><td>Meetings at 11 PM &amp; messages at random hours</td></tr>
<tr>
<td>Peaceful work environment</td><td>Parents asking for house chores mid-meeting</td></tr>
<tr>
<td>No office politics</td><td>Still stuck in endless Slack threads &amp; approvals</td></tr>
<tr>
<td>Earning in USD</td><td>Still struggling with UPI failures &amp; international payment delays</td></tr>
</tbody>
</table>
</div><p>The list is not exhaustive and varies from company to company. <em>Would you trade it for a 9-to-5 office job?</em></p>
<h1 id="heading-final-thoughts-and-tips">Final Thoughts (And Tips)</h1>
<h2 id="heading-show-dont-just-tell">Show, Don’t Just Tell</h2>
<p>Breaking into the remote work world isn’t just about having great technical skills—it’s about putting yourself out there and making sure the right people notice you.</p>
<p>The biggest mistake most developers make? <strong>They build projects but never talk about them.</strong> If your resume is just a list of skills, it won’t stand out. But if you’re constantly <strong>sharing your work, progress, and insights</strong> on Twitter, LinkedIn, or GitHub, you automatically get noticed.</p>
<ul>
<li><p>Tweet about your projects, what you're learning, and technical challenges you solved.</p>
</li>
<li><p>Write blog posts or threads explaining your work—it shows depth &amp; communication skills.</p>
</li>
<li><p>Engage with industry leaders and hiring managers on Twitter &amp; LinkedIn.</p>
</li>
</ul>
<p>When recruiters see that you’re actively <strong>building in public</strong>, it makes them confident that you’re not just another developer—you’re a problem solver, a thinker, and someone who takes initiative.</p>
<h2 id="heading-the-real-resume">The Real Resume</h2>
<p>Forget fancy CV templates—your <strong>real resume is your GitHub, side projects, and hackathons.</strong></p>
<ul>
<li><p><strong>Hackathons</strong> – Winning (or even participating in) hackathons proves that you can build under pressure, collaborate with a team, and solve real-world problems.</p>
</li>
<li><p><strong>Open-Source Contributions</strong> – Engaging in open-source projects shows that you can work with large codebases and contribute to the global developer ecosystem.</p>
</li>
<li><p><strong>Side Projects</strong> – A well-executed side project <strong>can be better than an internship.</strong> If it solves a problem, people will notice. Grab bounties and grants.</p>
</li>
</ul>
<h1 id="heading-thank-you-for-reading">Thank You for Reading!</h1>
<p>If you’ve read this far, <strong>thank you for sticking around!</strong> 🎉</p>
<p>I hope this blog gave you some insights, laughs, and maybe even the motivation to start your own remote work journey. If you’re an aspiring Indian developer, trust me—remote work is not just a trend, it’s the future. Keep learning, keep applying, and keep putting yourself out there.</p>
<p>And if you ever need advice or just want to chat, <strong>DM me on Twitter / X.</strong> Let’s build the future, one remote job at a time!</p>
<p><a class="user-mention" href="https://hashnode.com/@mehtaaaaaah">Kartik Mehta</a></p>
<p><a target="_blank" href="https://x.com/kartik_mehta8"><em>X</em></a> <em>/</em> <a target="_blank" href="https://www.linkedin.com/in/kartikmehta17"><em>LinkedIn</em></a></p>
]]></content:encoded></item><item><title><![CDATA[Why Developers Are Avoiding AWS Lambda? (And How to Master It!)]]></title><description><![CDATA[AWS Lambda has revolutionised the way developers approach server-less computing, offering scalable and cost-effective solutions for event-driven applications. However, despite its advantages, many developers hesitate to adopt Lambda for various reaso...]]></description><link>https://writer.mrmehta.in/why-developers-are-avoiding-aws-lambda-and-how-to-master-it</link><guid isPermaLink="true">https://writer.mrmehta.in/why-developers-are-avoiding-aws-lambda-and-how-to-master-it</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[API Gateway]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Fri, 14 Feb 2025 09:35:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739363648321/bee044a1-7296-45a1-a795-5875d9264aeb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AWS Lambda has revolutionised the way developers approach server-less computing, offering scalable and cost-effective solutions for event-driven applications. However, despite its advantages, many developers hesitate to adopt Lambda for various reasons.</p>
<p>In this blog, I’ll try to cover some common challenges developers face with AWS Lambda and provide a step-by-step guide to deploying a simple Node function that calculates Fibonacci numbers, demonstrating how to overcome these hurdles.</p>
<h2 id="heading-developers-arent-embracing-aws-lambda">Developers Aren’t Embracing AWS Lambda?</h2>
<p>While AWS Lambda offers numerous benefits, several challenges deter developers from fully embracing it. I tried finding some challenges and opinions on Stack Overflow and other websites about the main hassles of using it.</p>
<ol>
<li><p><strong>Complexity in Configuration and Deployment</strong>: Setting up and deploying Lambda functions, especially with multiple integrations, can become complex and time-consuming. Here, Ben jotted down the <a target="_blank" href="https://torvo.com.au/articles/5-aws-lambda-pitfalls-most-developers-dont-know-about">five AWS Lambda pitfalls most developers don’t know about</a>.</p>
</li>
<li><p><strong>Complexity in Testing and Debugging</strong>: Simulating the Lambda environment locally for testing and debugging can be challenging, leading to potential issues during deployment.</p>
</li>
<li><p>People have debated using this instead of a web framework for various use cases where execution time is minimal, such as an image optimiser or a basic interceptor. I recommend checking out this sub-reddit — "<a target="_blank" href="https://www.reddit.com/r/Python/comments/1092py3/why_or_why_not_use_aws_lambda_instead_of_a_web/">Why or why not use AWS Lambda instead of a web framework for your REST APIs?</a>" — to get a bird's-eye view of what AWS Lambda can and cannot achieve.</p>
</li>
<li><p><strong>Resource Limitations</strong>: There are constraints on memory, storage, and concurrent executions, which can pose challenges for resource-intensive applications.</p>
</li>
</ol>
<p>Besides all of this, I know from experience that getting started with AWS Lambda for a specific use case can be challenging, especially without a personalised doc or setup guide. (Don’t worry, I’ve got you covered!)</p>
<p><strong>Ps:</strong> I would love to hear from you! What challenges have you faced when considering or implementing AWS Lambda in your projects? Share your experiences and insights in the comments below.</p>
<h2 id="heading-making-aws-lambda-easy">Making AWS Lambda Easy!</h2>
<p>To demystify the process, let's walk through deploying a simple Node function on AWS Lambda that calculates Fibonacci numbers. I’ll try to cover all the technicalities related to Lambda here (hope so).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739365810405/d97a710c-53ae-46ee-9eb1-733dc75feb1d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-1-set-up-your-environment">Step 1: Set Up Your Environment</h3>
<p>Before getting started, ensure that you have an <strong>active AWS account</strong>, as it is essential for accessing and utilising AWS services. If you don’t have one, you can create an account on the <a target="_blank" href="https://aws.amazon.com/">AWS website</a> and follow the necessary setup steps. Additionally, make sure that Node is installed on your local machine, as it is required for running JavaScript-based applications. You can verify your Node installation by running <code>node -v</code> in your terminal.</p>
<h3 id="heading-step-2-write-the-fibonacci-function">Step 2: Write the Fibonacci Function</h3>
<p>Now, we will use the <a target="_blank" href="https://www.npmjs.com/package/fibonacci"><strong>fibonacci</strong></a> <code>npm package</code> instead of writing our own recursive function. This will also demonstrate <em>how to package third-party dependencies for AWS Lambda</em>.</p>
<h4 id="heading-1-initialise-a-node-project"><strong>1. Initialise a Node Project</strong></h4>
<p>Navigate to your project directory and initialise a new Node project:</p>
<pre><code class="lang-bash">&gt;&gt;&gt; mkdir lambda-fibonacci
&gt;&gt;&gt; <span class="hljs-built_in">cd</span> lambda-fibonacci
&gt;&gt;&gt; npm init -y
</code></pre>
<h4 id="heading-2-install-the-fibonacci-package"><strong>2. Install the</strong> <code>fibonacci</code> Package</h4>
<p>Run the following command to install the <code>fibonacci</code> package:</p>
<pre><code class="lang-bash">npm install fibonacci
</code></pre>
<h4 id="heading-3-create-the-lambda-function"><strong>3. Create the Lambda Function</strong></h4>
<p>Now, create an <code>index.mjs</code> file and use the <code>fibonacci</code> package:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// index.mjs file.</span>
<span class="hljs-keyword">const</span> fibonacci = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fibonacci"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-comment">// Parse the input number from the event object (assumed to be passed in the request).</span>
    <span class="hljs-keyword">const</span> number = <span class="hljs-built_in">parseInt</span>(event.number);

    <span class="hljs-comment">// Validate the input: Ensure it is a non-negative integer.</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(number) || number &lt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">statusCode</span>: <span class="hljs-number">400</span>, <span class="hljs-comment">// HTTP status code for bad request.</span>
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Please provide a valid non-negative integer.'</span> }),
        };
    }

    <span class="hljs-comment">// Use the "fibonacci" npm package to compute the Fibonacci number at the given position.</span>
    <span class="hljs-comment">// `iterate(number).number` returns the Fibonacci number at the specified position.</span>
    <span class="hljs-comment">// Visit: https://www.npmjs.com/package/fibonacci</span>
    <span class="hljs-keyword">const</span> result = fibonacci.iterate(number).number;

    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ number, result }),
    };
};
</code></pre>
<p>This function leverages the <code>fibonacci</code> package to efficiently compute Fibonacci numbers.</p>
<h3 id="heading-step-3-package-and-upload-your-lambda-function">Step 3: Package and Upload Your Lambda Function</h3>
<p>Since we are using an external <code>npm package</code>, we need to bundle all dependencies before uploading the function to AWS Lambda.</p>
<h4 id="heading-1-zip-the-required-files"><strong>1. Zip the Required Files</strong></h4>
<p>To include all dependencies, make sure you zip your <code>index.mjs</code> file along with the <code>node_modules</code> directory:</p>
<pre><code class="lang-bash">zip -r function.zip index.mjs node_modules package.json package-lock.json
</code></pre>
<h4 id="heading-2-create-a-lambda-function-on-aws-console"><strong>2. Create a Lambda Function on AWS Console</strong></h4>
<ul>
<li><strong>Log in to AWS Lambda</strong> and go to the AWS Lambda console. Click on <strong>Create a function</strong>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739520872218/0ef35ddb-7023-4037-aac8-759f7d2d0087.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>You will land on a page where you can see various options.</p>
<ul>
<li><p><strong>You have three ways to create a function:</strong></p>
<ul>
<li><p><strong>Author from scratch</strong>: Start with an empty Lambda function and manually configure it.</p>
</li>
<li><p><strong>Use a blueprint</strong>: Select a pre-configured template with sample code and AWS settings.</p>
</li>
<li><p><strong>Container image</strong>: Deploy your Lambda function using a Docker container.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>        For most cases, <strong>choosing "Author from scratch"</strong> is best, especially if you're building a custom function.</p>
<ul>
<li><p><strong>Basic Information Section:</strong></p>
<ul>
<li><p><strong>Function name</strong></p>
<ul>
<li><p>Choose a unique name for your function.</p>
</li>
<li><p>It must be 1 to 64 characters and can only contain <strong>letters, numbers, hyphens (-), and underscores (_).</strong></p>
</li>
</ul>
</li>
<li><p><strong>Runtime</strong></p>
<ul>
<li><p>This specifies the language environment for your function.</p>
</li>
<li><p>Options include <strong>Node,</strong> <strong>Python, Java, Go, Ruby, and more.</strong></p>
</li>
<li><p>Choose the appropriate runtime based on your function’s programming language.</p>
</li>
<li><p>For now, we will go with <code>Node.js 22.x</code></p>
</li>
</ul>
</li>
<li><p><strong>Architecture</strong></p>
<ul>
<li><p><strong>x86_64</strong> (Default): Best for general-purpose computing.</p>
</li>
<li><p><strong>arm64</strong>: More cost-efficient and energy-saving but may not support all libraries.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>For now, you can ignore the other configurations and simply click on <strong>Create Function</strong> at the bottom.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739521863406/7fc0be1b-3c38-47fc-813e-7d62ba5a89c9.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-3-upload-the-lambda-function"><strong>3. Upload the Lambda function.</strong></h4>
<p>After AWS creates the function, you will be redirected to the <strong>function dashboard</strong>. Here, you will see:</p>
<ul>
<li><p>Function name</p>
</li>
<li><p>ARN (Amazon Resource Name)</p>
</li>
<li><p>Runtime</p>
</li>
<li><p>Memory and timeout settings</p>
</li>
<li><p>Triggers and permissions</p>
</li>
<li><p>A <strong>code editor</strong> to edit / upload the code.</p>
</li>
</ul>
<p>Since we are using an external <code>npm package</code> (<code>fibonacci</code>), we need to <strong>package our function</strong> and upload it manually.</p>
<ol>
<li><p>Go to the <strong>Code</strong> tab in the Lambda function dashboard.</p>
</li>
<li><p>Click <strong>Upload from → .zip file</strong>.</p>
</li>
<li><p>Select your <code>function.zip</code> file.</p>
</li>
<li><p>Click <strong>Save and Deploy</strong>.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739522672006/a5f6e378-3610-47ea-940d-200698776fca.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-4-test-the-function">Step 4: Test the Function</h3>
<ol>
<li><p>Click <strong>"Test"</strong> at the top of the Lambda editor.</p>
</li>
<li><p><strong>Create a new test event</strong>:</p>
<ol>
<li><p>Set the test event name to <strong>FibonacciTest</strong>.</p>
</li>
<li><p>Add the following JSON payload:</p>
<pre><code class="lang-json"> {
     <span class="hljs-attr">"number"</span>: <span class="hljs-string">"10"</span>
 }
</code></pre>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739523665869/75f13dab-5868-4c2e-a8ee-0237eb2607dc.png" alt class="image--center mx-auto" /></p>
</li>
<li><p><strong>Run the test</strong> and check the response.</p>
<pre><code class="lang-json"> {
     <span class="hljs-attr">"number"</span>: <span class="hljs-number">10</span>,
     <span class="hljs-attr">"result"</span>: <span class="hljs-number">55</span>
 }
</code></pre>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739523760878/240c8549-448d-4c03-9507-54339dc6c865.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
</li>
</ol>
<p>Hooray! You have successfully created an AWS Lambda function with third-party dependencies, bundled it, uploaded it, and tested it!</p>
<h2 id="heading-more-on-the-code-and-context">More on the code and context.</h2>
<p>In our <code>index.js</code> file, the function begins with:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event) =&gt; {
</code></pre>
<p>This is the <strong>entry point</strong> for AWS Lambda. When the function is invoked, AWS passes an <code>event</code> object to this handler, containing all the necessary data for processing.</p>
<p>Let's dive deeper into the <code>event</code> object and other parameters that AWS Lambda provides.</p>
<h3 id="heading-1-the-event-object"><strong>1. The</strong> <code>event</code> Object</h3>
<p>The <code>event</code> parameter contains <strong>input data</strong> passed to the Lambda function. The structure of this object <strong>depends on the trigger</strong> (API Gateway, S3, DynamoDB, etc.).</p>
<h4 id="heading-api-gateway-trigger-for-an-http-request"><strong>API Gateway Trigger (for an HTTP request)</strong></h4>
<p>If your Lambda function is triggered via API Gateway, the <code>event</code> object looks like this:</p>
<p><em>(Don’t worry, there will be a separate blog where I will help you configure this Lambda with AWS API Gateway.)</em></p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"resource"</span>: <span class="hljs-string">"/fibonacci"</span>,
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/fibonacci"</span>,
    <span class="hljs-attr">"httpMethod"</span>: <span class="hljs-string">"POST"</span>,
    <span class="hljs-attr">"headers"</span>: { <span class="hljs-attr">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> },
    <span class="hljs-attr">"queryStringParameters"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"body"</span>: <span class="hljs-string">"{\"number\": \"10\"}"</span>,
    <span class="hljs-attr">"isBase64Encoded"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
<ul>
<li><p><code>httpMethod</code>: Specifies the request type (<code>GET</code>, <code>POST</code>, etc.).</p>
</li>
<li><p><code>headers</code>: Contains metadata like authentication tokens or content type.</p>
</li>
<li><p><code>body</code>: Holds the actual data sent to the function (e.g., <code>{"number": "10"}</code>).</p>
</li>
<li><p><code>path</code> &amp; <code>resource</code>: Indicate which endpoint was accessed.</p>
</li>
</ul>
<h3 id="heading-2-the-context-parameter"><strong>2. The Context Parameter</strong></h3>
<p>Besides <code>event</code>, AWS Lambda provides a <code>context</code> object, which includes metadata about the execution environment.</p>
<p>To use it, modify the handler function like this:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
    <span class="hljs-built_in">console</span>.log(context);
};
</code></pre>
<p>Key properties of <code>context</code> include:</p>
<ul>
<li><p><code>functionName</code>: The name of the Lambda function.</p>
</li>
<li><p><code>functionVersion</code>: The deployed version of the function.</p>
</li>
<li><p><code>memoryLimitInMB</code>: The memory allocated to the function.</p>
</li>
<li><p><code>awsRequestId</code>: A unique identifier for the function invocation.</p>
</li>
<li><p><code>logGroupName</code> &amp; <code>logStreamName</code>: Where the logs are stored in AWS CloudWatch.</p>
</li>
<li><p><code>getRemainingTimeInMillis()</code>: The remaining execution time before timeout.</p>
</li>
</ul>
<p>Feel free to use their <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html">official guide</a> if you want.</p>
<h2 id="heading-in-simple-words-why-aws-lambda">In simple words… why AWS Lambda?</h2>
<p>AWS Lambda is Amazon's server-less compute service that allows developers to run code without managing servers or containers. It automatically scales based on the workload, making it suitable for various applications such as data pipelines, responding to web requests, and sending emails. Developers can run Lambda functions locally during development and only pay for the compute time used, which can lead to significant cost savings compared to running virtual machines or containers. This flexibility and efficiency make AWS Lambda a versatile tool for executing code in the AWS cloud.</p>
<p>Visit: <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html#features">https://docs.aws.amazon.com/lambda/latest/dg/welcome.html#features</a></p>
<h2 id="heading-up-next">Up Next!</h2>
<p>Don't worry, this is not the end! I am in the process of integrating this AWS Lambda with API Gateway to create a simple server-less API. AWS offers a wide range of services, but the best way to truly understand them is by using them. So, dive in, get hands-on experience, and you'll become an AWS expert in no time!</p>
<p><strong>Mehta :)</strong></p>
]]></content:encoded></item><item><title><![CDATA[Using Web Workers in JavaScript - An Old School Way of Writing Code]]></title><description><![CDATA[Introduction
JavaScript is a single-threaded language, meaning it can only perform one operation at a time. This limitation can lead to performance issues, especially when dealing with complex computations or heavy tasks that can block the main threa...]]></description><link>https://writer.mrmehta.in/using-web-workers-in-javascript</link><guid isPermaLink="true">https://writer.mrmehta.in/using-web-workers-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[optimization]]></category><category><![CDATA[webworker]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Thu, 18 Jul 2024 17:12:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741952788531/5b993b98-3d26-40e6-af83-6c3a506fad30.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>JavaScript is a single-threaded language, meaning it can only perform one operation at a time. This limitation can lead to performance issues, especially when dealing with complex computations or heavy tasks that can block the main thread and cause the UI to become unresponsive. Web Workers provide a solution by allowing JavaScript to run tasks in the background, on separate threads, without interfering with the main execution thread. In this article, we will explore Web Workers in depth, understand how they work, and learn how to use them effectively.</p>
<h2 id="heading-what-are-web-workers">What are Web Workers?</h2>
<p>Web Workers are a standard way to run scripts in background threads. They are an essential feature of modern web development, providing the ability to perform tasks concurrently without blocking the user interface. Web Workers can be used for a variety of purposes, such as handling large data processing tasks, performing complex calculations, or managing I/O operations.</p>
<h3 id="heading-types-of-web-workers">Types of Web Workers</h3>
<p>There are three types of Web Workers:</p>
<ol>
<li><p><strong>Dedicated Workers</strong>: These are the most common type of Web Workers. Each dedicated worker is linked to a single script and a single context.</p>
</li>
<li><p><strong>Shared Workers</strong>: These workers can be accessed by multiple scripts, even if they are in different windows or tabs.</p>
</li>
<li><p><strong>Service Workers</strong>: These are special workers that can intercept network requests and manage the caching of resources. They are used primarily for building Progressive Web Apps (PWAs).</p>
</li>
</ol>
<h2 id="heading-creating-and-using-web-workers">Creating and Using Web Workers</h2>
<p>To start using Web Workers, you first need to create a new worker by providing the path to the worker script. Here is a simple example:</p>
<h3 id="heading-basic-example-of-a-dedicated-worker">Basic Example of a Dedicated Worker</h3>
<h3 id="heading-mainjs"><code>`main.js` </code></h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Check if the browser supports Web Workers</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.Worker) {
  <span class="hljs-comment">// Create a new Web Worker, providing the file name of the worker script</span>
  <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'worker.js'</span>);

  <span class="hljs-comment">// Define an event listener for messages received from the worker</span>
  worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Log the message received from the worker to the console</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>, event.data);
  };

  <span class="hljs-comment">// Send a message to the worker</span>
  worker.postMessage(<span class="hljs-string">'Hello, Worker!'</span>);
}
</code></pre>
<h3 id="heading-workerjs"><code>`worker.js` </code></h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Define an event listener for messages received from the main script</span>
onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-comment">// Log the message received from the main script to the console</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from main script'</span>, event.data);

  <span class="hljs-comment">// Send a message back to the main script</span>
  postMessage(<span class="hljs-string">'Hello, Main!'</span>);
};
</code></pre>
<h3 id="heading-main-script-components">Main Script Components</h3>
<ol>
<li><p><strong>Worker Creation</strong>:</p>
<ul>
<li>Create a new Worker instance by specifying the path to the worker script or using a Blob URL.</li>
</ul>
</li>
<li><p><strong>Message Passing</strong>:</p>
<ul>
<li><p>Use <code>postMessage()</code> to send data to the worker.</p>
</li>
<li><p>Set up an <code>onmessage</code> event handler to receive messages from the worker.</p>
</li>
</ul>
</li>
<li><p><strong>Error Handling</strong>:</p>
<ul>
<li>Set up an <code>onerror</code> event handler to catch and handle errors that occur within the worker.</li>
</ul>
</li>
<li><p><strong>Worker Termination</strong>:</p>
<ul>
<li>Use the <code>terminate()</code> method to stop the worker when it is no longer needed.</li>
</ul>
</li>
</ol>
<h3 id="heading-worker-script-components">Worker Script Components</h3>
<ol>
<li><p><strong>Message Handling</strong>:</p>
<ul>
<li>Set up an <code>onmessage</code> event handler to receive messages from the main script.</li>
</ul>
</li>
<li><p><strong>Message Sending</strong>:</p>
<ul>
<li>Use <code>postMessage()</code> to send data back to the main script.</li>
</ul>
</li>
<li><p><strong>Error Handling</strong>:</p>
<ul>
<li>Optionally, throw errors within the worker script to be caught by the main script's <code>onerror</code> handler.</li>
</ul>
</li>
</ol>
<h2 id="heading-understanding-the-event-loop-and-concurrency">Understanding the Event Loop and Concurrency</h2>
<p>Before diving deeper into Web Workers, it's important to understand how the JavaScript event loop works. The event loop is responsible for executing the code, collecting and processing events, and executing queued sub-tasks.</p>
<h3 id="heading-the-event-loop-explained">The Event Loop Explained</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680291011371/7d3e8f5e-74ee-4e93-a214-bdcf6536b23d.png" alt="JavaScript Event Loop: How it Works and Importance of Efficient, Non-B" /></p>
<p>JavaScript uses an event-driven, non-blocking I/O model. This means that tasks are added to a task queue and executed one by one. If a task takes a long time to execute, it can block the event loop, causing the UI to become unresponsive.</p>
<p>Refer <a target="_blank" href="https://www.youtube.com/watch?v=8zKuNo4ay8E&amp;pp=ygUXYWtzaGF5IHNhaW5pIGV2ZW50IGxvb3A%3D">Asynchronous JavaScript &amp; EVENT LOOP from scratch 🔥 | Namaste JavaScript Ep.15</a> for detailed explanation on Event Loop.</p>
<h3 id="heading-how-web-workers-help">How Web Workers Help</h3>
<p><strong>Web Workers help by offloading tasks to separate threads, allowing the main thread to remain responsive.</strong> This is particularly useful for CPU-intensive tasks or tasks that involve heavy computation.</p>
<h2 id="heading-advanced-web-worker-features">Advanced Web Worker Features</h2>
<p>Web Workers provide several advanced features that can be used to optimize and manage background tasks more effectively.</p>
<h3 id="heading-passing-data-to-and-from-workers">Passing Data to and from Workers</h3>
<p>Web Workers communicate with the main script using the <code>postMessage</code> method. You can pass different types of data, including strings, objects, and arrays. Here is an example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-comment">// Check if the browser supports Web Workers</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.Worker) {
  <span class="hljs-comment">// Create a new Web Worker, providing the file name of the worker script</span>
  <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'worker.js'</span>);

  <span class="hljs-comment">// Define an object containing data to be sent to the worker</span>
  <span class="hljs-keyword">const</span> data = { <span class="hljs-attr">text</span>: <span class="hljs-string">'Hello, Worker!'</span>, <span class="hljs-attr">count</span>: <span class="hljs-number">5</span> };

  <span class="hljs-comment">// Define an event listener for messages received from the worker</span>
  worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Log the message received from the worker to the console</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>, event.data);
  };

  <span class="hljs-comment">// Send the data object to the worker</span>
  worker.postMessage(data);
}

<span class="hljs-comment">// worker.js</span>
<span class="hljs-comment">// Define an event listener for messages received from the main script</span>
onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-comment">// Extract the data object from the event</span>
  <span class="hljs-keyword">const</span> data = event.data;

  <span class="hljs-comment">// Repeat the text in the data object 'count' times</span>
  <span class="hljs-keyword">const</span> result = data.text.repeat(data.count);

  <span class="hljs-comment">// Send the resulting string back to the main script</span>
  postMessage(result);
};
</code></pre>
<p>In this example:</p>
<ul>
<li><p>We pass an object to the worker containing a string and a count.</p>
</li>
<li><p>The worker processes the data and sends back the repeated string.</p>
</li>
</ul>
<h3 id="heading-using-blob-urls-to-create-workers">Using Blob URLs to Create Workers</h3>
<p>You can create a Web Worker using a Blob URL, which allows you to define the worker script directly in the main script. Here is an example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Define the script to be run by the Web Worker as a string</span>
<span class="hljs-keyword">const</span> workerScript = <span class="hljs-string">`
  onmessage = function(event) {
    const data = event.data;
    const result = data.text.repeat(data.count);
    postMessage(result);
  };
`</span>;

<span class="hljs-comment">// Create a new Blob object from the worker script string, specifying the MIME type as JavaScript</span>
<span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">new</span> Blob([workerScript], { <span class="hljs-attr">type</span>: <span class="hljs-string">'application/javascript'</span> });

<span class="hljs-comment">// Create a URL for the Blob object, which can be used to create a Web Worker</span>
<span class="hljs-keyword">const</span> blobURL = URL.createObjectURL(blob);

<span class="hljs-comment">// Create a new Web Worker using the Blob URL</span>
<span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(blobURL);

<span class="hljs-comment">// Define an event listener for messages received from the worker</span>
worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-comment">// Log the message received from the worker to the console</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>, event.data);
};

<span class="hljs-comment">// Define an object containing data to be sent to the worker</span>
<span class="hljs-keyword">const</span> data = { <span class="hljs-attr">text</span>: <span class="hljs-string">'Hello, Worker!'</span>, <span class="hljs-attr">count</span>: <span class="hljs-number">5</span> };

<span class="hljs-comment">// Send the data object to the worker</span>
worker.postMessage(data);
</code></pre>
<h3 id="heading-error-handling-in-web-workers">Error Handling in Web Workers</h3>
<p>Error handling in Web Workers is straightforward. You can set up an <code>onerror</code> handler to catch errors that occur within the worker script. Here is an example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-comment">// Check if the browser supports Web Workers</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.Worker) {
  <span class="hljs-comment">// Create a new Web Worker, providing the file name of the worker script</span>
  <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'worker.js'</span>);

  <span class="hljs-comment">// Define an event listener for errors occurring in the worker</span>
  worker.onerror = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Log the error details from the worker to the console</span>
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in worker'</span>, event.message, event.filename, event.lineno);
  };

  <span class="hljs-comment">// Define an event listener for messages received from the worker</span>
  worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Log the message received from the worker to the console</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>, event.data);
  };

  <span class="hljs-comment">// Send a message to the worker</span>
  worker.postMessage(<span class="hljs-string">'Hello, Worker!'</span>);
}

<span class="hljs-comment">// worker.js</span>
<span class="hljs-comment">// Define an event listener for messages received from the main script</span>
onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-comment">// Throw an error intentionally to simulate an error scenario</span>
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Something went wrong!'</span>);
};
</code></pre>
<h3 id="heading-terminating-workers">Terminating Workers</h3>
<p>You can terminate a worker using the <code>terminate</code> method. This stops the worker immediately and prevents it from processing any further messages. Here is an example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-comment">// Check if the browser supports Web Workers</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.Worker) {
  <span class="hljs-comment">// Create a new Web Worker, providing the file name of the worker script</span>
  <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'worker.js'</span>);

  <span class="hljs-comment">// Define an event listener for messages received from the worker</span>
  worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-comment">// Log the message received from the worker to the console</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>, event.data);
  };

  <span class="hljs-comment">// Send a message to the worker</span>
  worker.postMessage(<span class="hljs-string">'Hello, Worker!'</span>);

  <span class="hljs-comment">// Set a timeout to terminate the worker after 5 seconds</span>
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Terminate the worker</span>
    worker.terminate();
    <span class="hljs-comment">// Log that the worker has been terminated to the console</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Worker terminated'</span>);
  }, <span class="hljs-number">5000</span>); <span class="hljs-comment">// 5000 milliseconds (5 seconds)</span>
}

<span class="hljs-comment">// worker.js</span>
<span class="hljs-comment">// Define an event listener for messages received from the main script</span>
onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-comment">// Set a timeout to send a message back to the main script after 10 seconds</span>
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Send a message back to the main script</span>
    postMessage(<span class="hljs-string">'Hello, Main!'</span>);
  }, <span class="hljs-number">10000</span>); <span class="hljs-comment">// 10000 milliseconds (10 seconds)</span>
};
</code></pre>
<h2 id="heading-using-web-workers-with-frameworks">Using Web Workers with Frameworks</h2>
<p>Web Workers can be integrated with modern JavaScript frameworks like React and Next.js to improve performance and user experience.</p>
<p>Refer <a target="_blank" href="https://dev.to/sumankalia/web-workers-in-reactjs-4bc7">Web workers in ReactJs by Sumit Kumar</a> for more information.</p>
<p>React is a popular library for building user interfaces. You can use Web Workers in React to handle heavy computations without blocking the UI. Example:</p>
<h3 id="heading-image-processing-with-web-workers-in-react">Image Processing with Web Workers in React</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/ImageProcessor.js</span>
<span class="hljs-comment">// Import necessary modules from React</span>
<span class="hljs-keyword">import</span> React, { useState, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-comment">// Define the ImageProcessor component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ImageProcessor</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Declare a state variable 'result' to store the processed image data</span>
  <span class="hljs-keyword">const</span> [result, setResult] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-comment">// Create a reference to the file input element</span>
  <span class="hljs-keyword">const</span> fileInputRef = useRef(<span class="hljs-literal">null</span>);

  <span class="hljs-comment">// Handle file input change event</span>
  <span class="hljs-keyword">const</span> handleFileChange = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-comment">// Get the selected file from the input</span>
    <span class="hljs-keyword">const</span> file = event.target.files[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">if</span> (file) {
      <span class="hljs-comment">// Create a new FileReader to read the file content</span>
      <span class="hljs-keyword">const</span> reader = <span class="hljs-keyword">new</span> FileReader();
      <span class="hljs-comment">// Define the onload event handler for the FileReader</span>
      reader.onload = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Get the result (data URL) of the file reading</span>
        <span class="hljs-keyword">const</span> imageData = reader.result;
        <span class="hljs-comment">// Call the processImage function to process the image data</span>
        processImage(imageData);
      };
      <span class="hljs-comment">// Read the file as a data URL</span>
      reader.readAsDataURL(file);
    }
  };

  <span class="hljs-comment">// Function to process the image data using a Web Worker</span>
  <span class="hljs-keyword">const</span> processImage = <span class="hljs-function">(<span class="hljs-params">imageData</span>) =&gt;</span> {
    <span class="hljs-comment">// Define the script to be run by the Web Worker as a string</span>
    <span class="hljs-keyword">const</span> workerScript = <span class="hljs-string">`
      onmessage = function(event) {
        const imageData = event.data;
        // Perform image processing here (e.g., apply a filter)
        const resultData = imageData; // For simplicity, just return the original image data
        postMessage(resultData);
      };
    `</span>;

    <span class="hljs-comment">// Create a new Blob object from the worker script string, specifying the MIME type as JavaScript</span>
    <span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">new</span> Blob([workerScript], { <span class="hljs-attr">type</span>: <span class="hljs-string">'application/javascript'</span> });
    <span class="hljs-comment">// Create a URL for the Blob object, which can be used to create a Web Worker</span>
    <span class="hljs-keyword">const</span> blobURL = URL.createObjectURL(blob);
    <span class="hljs-comment">// Create a new Web Worker using the Blob URL</span>
    <span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(blobURL);

    <span class="hljs-comment">// Define an event listener for messages received from the worker</span>
    worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
      <span class="hljs-comment">// Update the state variable 'result' with the processed image data</span>
      setResult(event.data);
    };

    <span class="hljs-comment">// Send the image data to the worker for processing</span>
    worker.postMessage(imageData);
  };

  <span class="hljs-comment">// Render the component</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Image Processor<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {/* File input element for selecting an image */}
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{fileInputRef}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleFileChange}</span> /&gt;</span>
      {/* Display the processed image if available */}
      {result &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{result}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Processed"</span> /&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Export the ImageProcessor component as the default export</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ImageProcessor;
</code></pre>
<h2 id="heading-scenarios-to-use-web-workers">Scenarios to Use Web Workers</h2>
<ul>
<li><p><strong>Heavy Computations</strong>:</p>
<ul>
<li><p>Image processing</p>
</li>
<li><p>Mathematical calculations (e.g., large matrix multiplications)</p>
</li>
<li><p>Cryptographic calculations</p>
</li>
<li><p>Data analysis and parsing large datasets</p>
</li>
</ul>
</li>
<li><p><strong>Asynchronous Data Fetching</strong>:</p>
<ul>
<li><p>Long-running API requests</p>
</li>
<li><p>Processing data from multiple sources simultaneously</p>
</li>
</ul>
</li>
<li><p><strong>Real-time Applications</strong>:</p>
<ul>
<li><p>Gaming engines</p>
</li>
<li><p>Real-time data visualization</p>
</li>
<li><p>Continuous data streams (e.g., stock prices, sensor data)</p>
</li>
</ul>
</li>
<li><p><strong>Background Tasks</strong>:</p>
<ul>
<li><p>File uploads/downloads</p>
</li>
<li><p>Background synchronization (e.g., offline mode syncing)</p>
</li>
<li><p>Periodic data updates</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-advantages-of-using-web-workers">Advantages of Using Web Workers</h2>
<ul>
<li><p><strong>Performance Improvement</strong>:</p>
<ul>
<li>Offload heavy computations from the main thread, keeping the UI responsive.</li>
</ul>
</li>
<li><p><strong>Concurrency</strong>:</p>
<ul>
<li>Execute multiple tasks simultaneously, improving the efficiency of data processing.</li>
</ul>
</li>
<li><p><strong>Scalability</strong>:</p>
<ul>
<li>Better manage resources and improve the scalability of applications with heavy background processing needs.</li>
</ul>
</li>
<li><p><strong>User Experience</strong>:</p>
<ul>
<li>Prevent UI freezing and enhance the overall user experience by keeping interactions smooth and responsive.</li>
</ul>
</li>
</ul>
<h2 id="heading-disadvantages-of-using-web-workers">Disadvantages of Using Web Workers</h2>
<ul>
<li><p><strong>Complexity</strong>:</p>
<ul>
<li>Increased complexity in managing multiple threads, especially in debugging and maintaining code.</li>
</ul>
</li>
<li><p><strong>Context Limitations</strong>:</p>
<ul>
<li>Limited access to the DOM, making it unsuitable for tasks that require direct manipulation of the webpage.</li>
</ul>
</li>
<li><p><strong>Communication Overhead</strong>:</p>
<ul>
<li>Overhead in message passing between the main thread and workers, which can impact performance for small or simple tasks.</li>
</ul>
</li>
<li><p><strong>Browser Compatibility</strong>:</p>
<ul>
<li>Variability in support and performance across different browsers, especially older versions.</li>
</ul>
</li>
<li><p><strong>Resource Consumption</strong>:</p>
<ul>
<li>Each worker consumes additional system resources (CPU and memory), which can be significant if many workers are created.</li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Web Workers are a powerful tool for enhancing the performance of web applications by offloading heavy computations and tasks to background threads. By understanding how to create, use, and manage Web Workers, you can ensure that your applications remain responsive and efficient. Whether you're working with plain JavaScript or modern frameworks like React and Next.js, Web Workers can be integrated seamlessly to handle complex tasks without blocking the main thread.</p>
<p>Using Web Workers might seem like an "old school" way of writing code, but they remain a vital part of the web development toolkit. As web applications continue to grow in complexity, the ability to perform tasks concurrently will become increasingly important. By mastering Web Workers, you'll be well-equipped to build high-performance web applications that provide a smooth and responsive user experience.</p>
]]></content:encoded></item><item><title><![CDATA[Intricacies of Fail-Open Mechanism]]></title><description><![CDATA[In the digital age, where data breaches and cyber-attacks are increasingly common, the importance of robust authentication mechanisms cannot be overstated. These systems not only serve as the gateway guarding access to sensitive information and funct...]]></description><link>https://writer.mrmehta.in/intricacies-of-fail-open-mechanism</link><guid isPermaLink="true">https://writer.mrmehta.in/intricacies-of-fail-open-mechanism</guid><category><![CDATA[authentication]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[optimization]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Kartik Mehta]]></dc:creator><pubDate>Sun, 25 Feb 2024 09:02:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741953350619/c4923e4f-6f13-417a-8ef4-0b671848c235.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the digital age, where data breaches and cyber-attacks are increasingly common, the importance of robust authentication mechanisms cannot be overstated. These systems not only serve as the gateway guarding access to sensitive information and functionalities but also define the user experience, impacting everything from security to service availability. This article delves into the essence of authentication, with a particular focus on the nuanced concept of fail-open mechanisms, unraveling their potential, risks, and strategies for secure implementation.</p>
<h2 id="heading-the-foundation-of-authentication">The Foundation of Authentication</h2>
<p><img src="https://www.cloudflare.com/resources/images/slt3lc6tev37/iVLWiZgkpEMOKgvZ9ukUg/5b30ca255ae5b7dafdf586a77cdbfe81/what_is_authentication_password_example.png" alt="What is authentication? | Cloudflare" class="image--center mx-auto" /></p>
<p>The Foundation of Authentication delves into the crucial process of verifying the identity of users or entities as they attempt to access a digital system. This security measure is fundamental in ensuring that only individuals with confirmed credentials can engage with specific data or functionalities. The mechanisms employed for authentication are diverse, each offering different levels of security and user experience.</p>
<h3 id="heading-authentication-methods"><strong>Authentication Methods</strong></h3>
<ol>
<li><p><strong>Password-Based Authentication</strong>:</p>
<ul>
<li><p><strong>Example</strong>: Logging into an email account using a username and password.</p>
</li>
<li><p><strong>Context</strong>: The most basic form of authentication. It requires users to enter a secret password that matches one stored in the system.</p>
</li>
</ul>
</li>
<li><p><strong>Two-Factor Authentication (2FA)</strong>:</p>
<ul>
<li><p><strong>Example</strong>: Accessing a banking website that requires a password and a one-time code sent to your phone.</p>
</li>
<li><p><strong>Context</strong>: Adds an extra layer of security by combining something the user knows (password) with something the user has (a mobile device for receiving One-Time Passwords).</p>
</li>
</ul>
</li>
<li><p><strong>Multi-Factor Authentication (MFA)</strong>:</p>
<ul>
<li><p><strong>Example</strong>: Entering a secured office building using a key card and a fingerprint scan.</p>
</li>
<li><p><strong>Context</strong>: Involves two or more verification methods from independent categories of credentials, significantly increasing security.</p>
</li>
</ul>
</li>
<li><p><strong>Biometric Authentication</strong>:</p>
<ul>
<li><p><strong>Example</strong>: Unlocking a smartphone using facial recognition or a fingerprint.</p>
</li>
<li><p><strong>Context</strong>: Uses unique biological traits of the user for verification, offering a high level of security and convenience.</p>
</li>
</ul>
</li>
<li><p><strong>Token-Based Authentication</strong>:</p>
<ul>
<li><p><strong>Example</strong>: Accessing a service using a security token generated by an authentication app.</p>
</li>
<li><p><strong>Context</strong>: Tokens are generated by an external device or app, providing a time-limited access credential that is difficult to replicate.</p>
</li>
</ul>
</li>
</ol>
<p>Authentication serves as the gateway to digital resources, balancing the need to protect sensitive information while providing a user-friendly access experience. As cyber threats evolve, so too will the methods of authentication, requiring ongoing innovation and adaptation to ensure the security and privacy of users and systems alike.</p>
<h2 id="heading-the-paradox-of-fail-open-authentication">The Paradox of Fail-Open Authentication</h2>
<p>Fail-open authentication mechanisms represent a significant paradigm within the domain of cybersecurity, where the default response to system failures is to grant access. This approach, seemingly at odds with traditional security wisdom, aims to prioritize the continuity of critical services over strict access control under failure conditions. However, this balance between security and accessibility introduces a complex landscape of risks and implications.</p>
<h3 id="heading-the-rationale-behind-fail-open-mechanisms">The Rationale Behind Fail-Open Mechanisms</h3>
<p>Fail-open systems are designed with the understanding that, in certain scenarios, the availability of the service is paramount, even at the expense of potential security breaches. This is particularly relevant in environments where system downtime can have severe consequences, such as in <strong>healthcare, emergency services, or critical infrastructure operations</strong>. The underlying philosophy is to ensure that users retain access to necessary functions and data during system malfunctions, thereby minimizing operational disruptions.</p>
<h3 id="heading-implementation">Implementation</h3>
<p>Consider a simple JavaScript example where a web application attempts to authenticate users based on a backend service's response. In a fail-open scenario, the system might default to granting access if it can't verify the user's credentials due to a service outage.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">authenticateUser</span>(<span class="hljs-params">username, password</span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> isAuthenticated = authService.verifyCredentials(username, password);
        <span class="hljs-keyword">return</span> isAuthenticated; <span class="hljs-comment">// safe.</span>
    } <span class="hljs-keyword">catch</span> (error) {

        <span class="hljs-comment">// Implement a secure fallback mechanism here.</span>
        <span class="hljs-keyword">if</span> (error.type === <span class="hljs-string">'ServiceUnavailable'</span>) {

            <span class="hljs-comment">// For example, checking against a local cache with limited credentials</span>
            <span class="hljs-keyword">return</span> checkLocalCredentialsCache(username, password);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (error.type === <span class="hljs-string">'SystemOutage'</span>) {
            <span class="hljs-comment">// Provide the basic functionality for time critical operations.</span>
            <span class="hljs-keyword">return</span> criticalTaskOngoing(username, password);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Otherwise, return false.</span>
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
    }
}
</code></pre>
<h3 id="heading-potential-risks-and-security-implications">Potential Risks and Security Implications</h3>
<h4 id="heading-unauthorized-access">Unauthorized Access</h4>
<ul>
<li><p><strong>Issue</strong>: The most glaring risk associated with fail-open mechanisms is the potential for unauthorized access. During a system or authentication service failure, these mechanisms may inadvertently allow users without proper credentials to access sensitive areas of the system.</p>
</li>
<li><p><strong>Impact</strong>: This can lead to data breaches, unauthorized transactions, or the exposure of confidential information, posing significant risks to both the organization and its stakeholders.</p>
</li>
</ul>
<h4 id="heading-exploitation-of-fail-open-behavior">Exploitation of Fail-Open Behavior</h4>
<ul>
<li><p><strong>Issue</strong>: Attackers, aware of the fail-open configuration, might deliberately trigger conditions that cause the system to fail, thereby exploiting the mechanism to bypass security controls.</p>
</li>
<li><p><strong>Techniques</strong>: This could involve <em>DDoS</em> attacks aimed at overwhelming the authentication service or exploiting specific vulnerabilities that cause system errors leading to a fail-open state.</p>
</li>
<li><p><strong>Impact</strong>: Such exploitation not only undermines the security of the system but also erodes trust in its reliability and integrity.</p>
</li>
</ul>
<h4 id="heading-configuration-errors">Configuration Errors</h4>
<ul>
<li><p><strong>Issue</strong>: Fail-open mechanisms, being counterintuitive to traditional security practices, can often be the result of configuration errors rather than intentional design choices.</p>
</li>
<li><p><strong>Common Pitfalls</strong>: Misconfigurations can arise from a misunderstanding of the system settings, improper implementation of authentication protocols, or lack of awareness about the default behavior of certain components under failure conditions.</p>
</li>
<li><p><strong>Impact</strong>: These errors can leave systems unintentionally exposed, making them ripe targets for exploitation by malicious actors who seek to take advantage of such oversights.</p>
</li>
</ul>
<h3 id="heading-strategies-for-secure-fail-open-implementations">Strategies for Secure Fail-Open Implementations</h3>
<p>Let's delve into each strategy and explore how they can be implemented.</p>
<h4 id="heading-1-constrained-access-on-failure">1. Constrained Access on Failure</h4>
<p><strong>Objective</strong>: Limit the actions a user can perform when the system is in fail-open mode to minimize potential security breaches.</p>
<p><strong>Implementation</strong>: In a web application, you can check if the system is in fail-open mode and conditionally restrict user actions.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Pseudocode to illustrate constrained access.</span>
<span class="hljs-keyword">if</span> (authenticationService.isOperational()) {
    <span class="hljs-comment">// Normal authentication flow.</span>
    user.authenticate();
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Fail-open mode.</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Authentication service down. Entering fail-open mode."</span>);
    user.grantReadOnlyAccess();
}
</code></pre>
<h4 id="heading-2-enhanced-monitoring-and-alerts">2. Enhanced Monitoring and Alerts</h4>
<p><strong>Objective</strong>: Implement real-time monitoring and alerting for authentication failures to quickly identify and address issues.</p>
<p><strong>Implementation</strong>: Setting up an alert system using an application monitoring tool like Sentry, New Relic, or a custom solution.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Pseudocode for monitoring authentication service status.</span>
<span class="hljs-keyword">if</span> (!authenticationService.isOperational()) {
    alertSystem.send(<span class="hljs-string">"Authentication service failure detected. Activating fail-open mode."</span>);
}
</code></pre>
<h4 id="heading-3-fallback-authentication-methods">3. Fallback Authentication Methods</h4>
<p><strong>Objective</strong>: Employ alternative authentication methods during failures to ensure secure and reliable access.</p>
<p><strong>Implementation</strong>: Implementing a secondary authentication mechanism, such as sending a one-time password to the user's registered email or phone number.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Pseudocode for fallback authentication.</span>
<span class="hljs-keyword">if</span> (authenticationService.isOperational()) {
    user.authenticate();
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Primary authentication service down. Using fallback authentication method."</span>);
    user.sendOtp(); <span class="hljs-comment">// Send OTP to user's registered email or phone.</span>
    user.authenticateWithOtp();
}
</code></pre>
<h4 id="heading-4-security-focused-fail-over-systems">4. Security-Focused Fail-over Systems</h4>
<p><strong>Objective</strong>: Design fail-over systems with inherent security measures to provide a safer alternative during primary system failures.</p>
<p><strong>Implementation</strong>: Setting up a secondary authentication server that activates only when the primary server fails, ensuring that authentication can still be securely processed.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Pseudocode for a security-focused failover system.</span>
<span class="hljs-keyword">if</span> (primaryAuthenticationService.isOperational()) {
    user.authenticateWithPrimaryService();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (failoverAuthenticationService.isOperational()) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Primary authentication service down. Switching to failover system."</span>);
    user.authenticateWithFailoverService();
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Both authentication services down. Entering constrained access mode."</span>);
    user.grantReadOnlyAccess();
}
</code></pre>
<p>These strategies offer a roadmap for implementing fail-open mechanisms in a way that balances the need for accessibility during system failures with the imperative of maintaining security. It's crucial to tailor these approaches to the specific context and security requirements of your system to effectively mitigate the risks associated with fail-open scenarios.</p>
<h3 id="heading-fail-open-vs-fail-close-authentication">Fail-Open Vs Fail-Close Authentication</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Fail-Open Authentication System</td><td>Fail-Close Authentication System</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Default Behavior on Failure</strong></td><td>Grants access during system or authentication failures.</td><td>Denies access during system or authentication failures.</td></tr>
<tr>
<td><strong>Primary Goal</strong></td><td>Ensures availability and continuous operation, even at the potential cost of security.</td><td>Prioritizes security, potentially at the cost of availability.</td></tr>
<tr>
<td><strong>Risk Profile</strong></td><td>Higher risk of unauthorized access during failures.</td><td>Lower risk of unauthorized access, but higher risk of legitimate access denial.</td></tr>
<tr>
<td><strong>Suitable for</strong></td><td>Environments where service availability is critical and interruption could cause significant disruption or cost.</td><td>Environments where security is paramount and the risk of unauthorized access cannot be tolerated.</td></tr>
<tr>
<td><strong>Example Application</strong></td><td>Critical infrastructure systems where downtime is unacceptable.</td><td>Systems handling sensitive or classified information where security cannot be compromised.</td></tr>
<tr>
<td><strong>Mitigation Strategies</strong></td><td>Implement limited access, robust monitoring and alerting, fallback authentication methods, and secure fail-over systems.</td><td>Ensure reliability and redundancy in authentication services, along with rapid failure detection and response.</td></tr>
</tbody>
</table>
</div><h2 id="heading-conclusion">Conclusion</h2>
<p>Fail-open authentication mechanisms embody a critical balance between ensuring service availability and maintaining security integrity. While they offer a pathway to keeping services operational during unexpected system failures, they necessitate careful planning, robust monitoring, and stringent fallback procedures to mitigate inherent security risks. By embracing a cautious approach and employing strategic security measures, developers can harness the benefits of fail-open mechanisms without compromising on security.</p>
]]></content:encoded></item></channel></rss>