<?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" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Curious Rhythms]]></title><description><![CDATA[Dive deep into algo-rhythms, and tackle puzzles that make your brain do a happy dance. After all, in the grand puzzle of life, we’re all just bits and bytes searching for that perfect line of code.
Dive in, and let’s debug the universe together!]]></description><link>https://curiousrhythms.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!-Lu6!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fcuriousrhythms.substack.com%2Fimg%2Fsubstack.png</url><title>Curious Rhythms</title><link>https://curiousrhythms.substack.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 14 Apr 2026 02:38:36 GMT</lastBuildDate><atom:link href="https://curiousrhythms.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Mrinal]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[curiousrhythms@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[curiousrhythms@substack.com]]></itunes:email><itunes:name><![CDATA[Mrinal]]></itunes:name></itunes:owner><itunes:author><![CDATA[Mrinal]]></itunes:author><googleplay:owner><![CDATA[curiousrhythms@substack.com]]></googleplay:owner><googleplay:email><![CDATA[curiousrhythms@substack.com]]></googleplay:email><googleplay:author><![CDATA[Mrinal]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[I Shipped an Algorithm That Could Lose People Real Money — Here's How I Slept at Night]]></title><description><![CDATA[On building a portfolio allocation engine for &#8377;300Cr+ in live money, the shadow mode that saved us, and the estimation disaster I own.]]></description><link>https://curiousrhythms.substack.com/p/i-shipped-an-algorithm-that-could</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/i-shipped-an-algorithm-that-could</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Sat, 21 Mar 2026 15:15:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iZrr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iZrr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iZrr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iZrr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Hero &#8212; I Shipped an Algorithm That Could Lose People Real Money&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Hero &#8212; I Shipped an Algorithm That Could Lose People Real Money" title="Hero &#8212; I Shipped an Algorithm That Could Lose People Real Money" srcset="https://substackcdn.com/image/fetch/$s_!iZrr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iZrr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff321f614-8ac5-436b-89fa-bd18b91879e7_2400x1260.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><div><hr></div><p>There's a specific kind of fear that hits you when you're about to deploy code that controls how real money moves. Not "oops, the page crashed" money. <em>Hundreds of crores.</em> Thousands of accounts. Options trades where a partial execution doesn't just lose money &#8212; it leaves someone with an unhedged position and <strong>theoretically unlimited risk.</strong></p><p>I built the portfolio allocation engine at a Y Combinator-backed fintech that automated options trading. This is the story of how I shipped it without blowing up anyone's account &#8212; and the estimation disaster that almost derailed the whole thing.</p><div><hr></div><h2>The Problem: One Portfolio Wasn't Enough</h2><p>Users on our platform could only invest in a single trading strategy. If that strategy had a bad week, their entire capital took the hit. No diversification. Churn was climbing.</p><p>The product ask was straightforward: let users invest across multiple model portfolios simultaneously. Spread risk. Reduce churn.</p><p>The engineering reality was anything but straightforward.</p><div><hr></div><h2>The Constraint That Changed Everything</h2><p>Options trading has a property that makes it fundamentally different from, say, allocating money across mutual funds. Most options strategies involve <strong>hedged positions</strong> &#8212; a spread, for example, where you buy one contract and sell another. The two legs protect each other.</p><p>If you only execute one leg, the user is left holding a <strong>naked position</strong>. Depending on the market, that exposure can be <em>unlimited.</em></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3y0G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3y0G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3y0G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Hedged vs Naked Position Risk&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Hedged vs Naked Position Risk" title="Hedged vs Naked Position Risk" srcset="https://substackcdn.com/image/fetch/$s_!3y0G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3y0G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28d48efc-cad9-49c8-9c15-8527226b614f_1600x800.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This meant the allocation algorithm had a hard constraint: <em><strong>either a user trades ALL their assigned strategies for the week, or they trade NONE.</strong></em> There's no "let's do 3 out of 5 and skip the rest." Partial allocation is financially dangerous.</p><p>I remember sitting with this constraint and thinking &#8212; this isn't a software problem anymore. This is a <em>"someone's life savings"</em> problem.</p><div><hr></div><h2>The Two-Pass Algorithm</h2><p>If you squint at this problem from an algorithms perspective, it's a variant of the <strong>0/1 Knapsack problem with an all-or-nothing constraint</strong>. Each user has a "knapsack" (their available capital) and a set of "items" (strategies, each with a capital cost). The twist: unlike classic knapsack where you maximize value by selecting a subset, here the constraint is <strong>you must fit ALL items or take NONE</strong>. There's no partial selection. This simplifies the optimization (no need to find the optimal subset) but makes the feasibility check critical &#8212; get it wrong, and someone has an unhedged position.</p><p>More formally, this is a <strong>constraint satisfaction problem (CSP)</strong>: given a user's capital state and a set of strategy requirements, determine if a valid assignment exists where all strategies are funded simultaneously. Pass 1 is the feasibility check. Pass 2 is the assignment.</p><p>The allocation engine ran as a <strong>Cloud Scheduler-triggered daily job</strong>. Every morning before market open, it processed all 2,500+ accounts and decided: who trades today, and how much capital goes where.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EgV0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EgV0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 424w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 848w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EgV0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two-Pass Allocation Engine Overview&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two-Pass Allocation Engine Overview" title="Two-Pass Allocation Engine Overview" srcset="https://substackcdn.com/image/fetch/$s_!EgV0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 424w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 848w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!EgV0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb97168b0-a6ed-4ac0-82f5-b637533e437e_1600x1040.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h3>Pass 1 &#8212; Eligibility (Constraint Satisfaction)</h3><p>For each user, compute projected available capital:</p><pre><code>projected_available = current_balance
                    - capital_locked_in_open_positions
                    - margin_reserved_for_pending_orders
                    + capital_releasing_from_today's_expiries</code></pre><p>Then sum up the capital required across ALL assigned strategies for the week. If <code>projected_available &lt; total_required_across_all_strategies</code>, the user is marked <strong>ineligible</strong>. They sit out the entire week. No partial bets.</p><p>This is the CSP feasibility check &#8212; can all constraints (strategy capital requirements) be satisfied simultaneously given the available resources (user capital)? If not, the entire assignment is <strong>rejected</strong>. No greedy "fit as many as possible."</p><p>The tricky term is <code>capital_releasing_from_today's_expiries</code>. A position expiring today could settle in profit (releasing more capital than expected) or in loss (consuming more). At the time the algorithm runs &#8212; early morning, before market open &#8212; we don't know the settlement outcome yet. I initially used last closing price to estimate this. Shadow mode later revealed this was insufficient (more on that below).</p><div><hr></div><h3>Pass 2 &#8212; Capital Distribution (Round-Robin Scheduling)</h3><p>For eligible users, capital is distributed across their assigned strategies using a pattern borrowed from <strong>round-robin scheduling</strong> &#8212; the same algorithm OS schedulers use to allocate CPU time across processes, adapted here for capital allocation across strategies with temporal constraints:</p><pre data-params="python"><code>for each eligible user:
    strategies = user.assigned_strategies  # sorted by execution_window
    remaining_capital = user.projected_available

    for each strategy in strategies:
        required = strategy.compute_capital_requirement(user)
        if remaining_capital &gt;= required:
            allocate(user, strategy, required)
            remaining_capital -= required
        else:
            # This shouldn't happen &#8212; Pass 1 already verified total affordability
            # If it does, it's a bug. Log, alert, skip user entirely.
            rollback_all_allocations(user)
            break</code></pre><p>The round-robin ordering matters because strategies have different execution windows. A strategy that trades at 9:30 AM needs capital allocated before one that trades at 2:00 PM. The sort ensures capital is committed in execution order &#8212; same principle as <strong>priority scheduling with deadlines</strong>, where time-constrained jobs get resources first.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aA8R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aA8R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 424w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 848w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aA8R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Capital Distribution Timeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Capital Distribution Timeline" title="Capital Distribution Timeline" srcset="https://substackcdn.com/image/fetch/$s_!aA8R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 424w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 848w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!aA8R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61ab2976-5d49-400c-b92c-d90e68008f3d_1600x700.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The entire job &#8212; Pass 1 + Pass 2 for 2,500 users &#8212; completes in <strong>under a minute</strong>. It's CPU-light; the work is mostly database reads (balances, positions, strategy configs) and in-memory computation. Writes are the allocation decisions themselves, persisted to the database and picked up by the execution pipeline when trading windows open.</p><p>The algorithm itself wasn't the hard part. <strong>The hard part was trusting it.</strong></p><div><hr></div><h2>I Didn't Trust My Own Code</h2><p>We were managing <strong>&#8377;300Cr+ in real transaction volume</strong> across 2,500+ accounts. I'd written the algorithm, tested it, reviewed it. But I couldn't bring myself to flip the switch and let it control real trades on day one.</p><p>So I built <strong>shadow mode</strong> &#8212; also known as <em>dark launching</em> or <em>shadow traffic testing</em> in the broader industry.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OTjn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OTjn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OTjn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Deployment Strategy Spectrum&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Deployment Strategy Spectrum" title="Deployment Strategy Spectrum" srcset="https://substackcdn.com/image/fetch/$s_!OTjn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OTjn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884b20dc-a8e2-48cb-9573-d7969af08998_1600x760.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>If you've worked with deployment strategies, you've probably seen <strong>blue-green deployments</strong> (two identical environments, traffic switches atomically), <strong>canary releases</strong> (route a small percentage of traffic to the new version), and <strong>feature flags</strong> (toggle behavior at runtime). Shadow mode sits in a different spot on this spectrum. Unlike canary, where the new code serves real traffic for a subset of users, shadow mode has <strong>zero production impact</strong>. The new algorithm processes real inputs but its outputs go nowhere &#8212; they're logged, not acted upon. The old algorithm remains fully in control.</p><p>The reason I chose shadow mode over a canary approach: <em>this isn't a system where "5% of users get the new version" is safe.</em> In a web app, if the canary serves a bad page to 5% of users, you roll back and those users refresh. Here, if the new allocation is wrong for even one user, they could end up with an unhedged options position. The cost of a single bad decision isn't a degraded experience &#8212; it's <strong>real financial loss</strong>. Shadow mode was the only deployment pattern where I could validate against real production data with zero risk.</p><p>The idea is simple: run the old algorithm and the new algorithm simultaneously. The old one controls real trades. The new one runs silently, logs every decision it <em>would have</em> made, and writes the output to a separate store. Every morning, I'd compare the two outputs manually.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tGwO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tGwO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tGwO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Shadow Mode Architecture&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Shadow Mode Architecture" title="Shadow Mode Architecture" srcset="https://substackcdn.com/image/fetch/$s_!tGwO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tGwO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63f3db77-4f11-4c50-bbf3-8cabfd842303_1600x900.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>We ran shadow mode for <strong>2&#8211;3 weeks</strong>. And it caught things I wouldn't have found in testing.</p><p><strong>The timing edge case:</strong> A user's balance was technically sufficient at the time the algorithm ran. But they had positions expiring later that same day. Depending on whether those positions settled in profit or loss, the balance could swing either way. The new algorithm was saying "eligible" based on a snapshot, but by the time trades actually executed, the numbers had shifted. Shadow mode surfaced this because I could compare "what the algorithm decided at 8 AM" against "what actually happened by 3:30 PM."</p><p><strong>The rounding problem:</strong> Small-balance users near the eligibility threshold were flickering between eligible and ineligible across runs due to floating-point rounding in the capital calculation. Not a bug you'd catch in a unit test with clean numbers.</p><p>Both of these would have caused real problems &#8212; either users trading when they shouldn't, or users being incorrectly blocked. Shadow mode was the safety net that let me find them without anyone's money on the line.</p><p>After 2&#8211;3 weeks, I gradually migrated users &#8212; effectively moving from shadow mode to a <strong>staged rollout</strong>, shifting user cohorts from the old algorithm to the new one. This is the standard canary progression, but only after shadow mode had already validated correctness.</p><div><hr></div><h2>The Estimation Disaster</h2><p>Here's the part I'm less proud of.</p><p>This project was part of a larger rewrite &#8212; let's call it the <strong>Bundle Service v3</strong>. Original estimate: <strong>1 month</strong>. One week for the tech spec, one week for development, one week for QA, one week for release.</p><p>It took <strong>2.5 months.</strong></p><p>Three things I didn't anticipate:</p><h3>1. Cross-team blast radius</h3><p>The allocation engine didn't exist in isolation. It touched the onboarding service, the PnL generation pipeline, the analytics system &#8212; roughly <strong>5 dependent services</strong> owned by different people. I estimated as if I was only changing my service. I wasn't.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dHkf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dHkf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dHkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cross-Service Dependency Map&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cross-Service Dependency Map" title="Cross-Service Dependency Map" srcset="https://substackcdn.com/image/fetch/$s_!dHkf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dHkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8631fbf6-ac54-4e3f-8217-94d85f5f1619_1600x800.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>2. Legacy code tax</h3><p>The existing system had deprecated data models, dead code paths, and flows that weren't <strong>idempotent</strong>. This was the real blocker for shadow mode. Idempotency &#8212; the property that running an operation multiple times produces the same result as running it once &#8212; is a fundamental requirement in distributed systems (you'll find it in any discussion of exactly-once semantics, Kafka message processing, or payment systems). In our case, the old allocation flow had side effects: it mutated shared state as it ran, so running it twice could produce different results or double-count allocations. You can't safely run old and new algorithms in parallel if the old one has side effects you don't fully understand. I had to make the existing flows idempotent first &#8212; adding idempotency keys, making operations safely re-runnable, ensuring that re-processing a user didn't create duplicate allocations &#8212; before I could even begin the shadow mode comparison. Cleaning this up took weeks I hadn't budgeted for.</p><h3>3. Shadow mode wasn't in the original plan</h3><p>When I sat down and really thought about deploying to &#8377;300Cr+ in live money, I realized I couldn't skip the parallel validation phase. It was the right call &#8212; but it added significant time, and I hadn't scoped it upfront because I hadn't fully internalized the risk when I gave the initial estimate.</p><p>The 2.5x overrun was my estimation failure. <strong>I own that.</strong></p><div><hr></div><h2>What I Changed After This</h2><p><strong>I stopped estimating before I understood the full blast radius.</strong> For any cross-service change now, the first step is mapping every impacted service and talking to its owner before committing to a timeline. This is where the tech spec practice came from &#8212; I started writing detailed design documents for every feature, getting alignment in review before writing a line of code.</p><p><strong>I added a "legacy tax" to every estimate.</strong> In a fast-moving startup, there's always more legacy than you think. If a feature touches old code, I add buffer. <em>Always.</em></p><p><strong>I plan the rollout before the code.</strong> Shadow mode should have been in the original estimate. For anything touching money or critical paths, the deployment strategy is now part of the spec, estimated alongside development &#8212; not added as an afterthought.</p><p><strong>I escalate early with data.</strong> When I realized at the one-week mark that the scope was bigger than expected, I went to my manager with specifics: here are all the services impacted, here are the touch points, here's why shadow mode is non-negotiable for a system handling this much money, and here's the revised timeline. Because I showed up with data instead of vague "it's taking longer," the extended timeline was understood &#8212; not a surprise.</p><div><hr></div><h2>The Patterns Behind the Design</h2><p>Looking back, this project was a crash course in applying well-known engineering patterns to a domain where the cost of getting them wrong is measured in money, not error rates.</p><p><strong>Constraint Satisfaction / 0-1 Knapsack</strong> &#8212; Pass 1 eligibility check. Framing the problem correctly &#8212; this is feasibility checking, not optimization. All-or-nothing means you reject early instead of trying to find the "best partial fit."</p><p><strong>Round-Robin Scheduling</strong> &#8212; Pass 2 capital distribution. Borrowed from OS process scheduling. Temporal ordering (execution windows) maps directly to deadline-based priority scheduling.</p><p><strong>Shadow / Dark Launch</strong> &#8212; Pre-production validation. Zero-risk validation against real data. Chosen over canary because even one bad allocation has unbounded financial cost.</p><p><strong>Staged Rollout / Canary</strong> &#8212; Post-shadow migration. After shadow mode validated correctness, users were migrated in cohorts &#8212; the standard canary progression.</p><p><strong>Idempotency</strong> &#8212; Legacy code cleanup. Required before shadow mode could work. Operations needed to be safely re-runnable &#8212; same principle behind idempotency keys in payment APIs.</p><p>None of these patterns are novel. They're textbook. But knowing <em>when</em> to apply <em>which</em> pattern, and understanding why shadow mode was necessary here but a canary might suffice elsewhere &#8212; that's the engineering judgment part that doesn't show up in a design patterns book.</p><div><hr></div><h2>The Outcome</h2><p>The allocation engine shipped. Multi-portfolio investing went live across <strong>2,500+ accounts</strong>. No user money was impacted during the rollout. Shadow mode caught real edge cases that would have caused problems. Churn reduced.</p><p>And I learned that the scariest part of shipping critical software isn't the algorithm. It's the moment you realize your estimate was wrong, your scope is blowing up, and people's money is on the other side of your deploy button.</p><p><strong>The algorithm was the easy part. Earning the right to trust it &#8212; that was the real engineering.</strong></p><div><hr></div><p><em>I'm a Senior Backend Engineer with 6+ years of experience building distributed systems in Go and Python. I write about the messy reality of backend engineering at</em> <a href="https://mrinal.dev">mrinal.dev</a><em>.</em></p>]]></content:encoded></item><item><title><![CDATA[Grinding LeetCode in the Age of Gutenberg]]></title><description><![CDATA[On the quiet existential comedy of being a software engineer in 2026]]></description><link>https://curiousrhythms.substack.com/p/grinding-leetcode-in-the-age-of-gutenberg</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/grinding-leetcode-in-the-age-of-gutenberg</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Fri, 20 Mar 2026 05:12:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1EWR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1EWR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1EWR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1EWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:726,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Hero &#8212; Grinding LeetCode in the Age of Gutenberg&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Hero &#8212; Grinding LeetCode in the Age of Gutenberg" title="Hero &#8212; Grinding LeetCode in the Age of Gutenberg" srcset="https://substackcdn.com/image/fetch/$s_!1EWR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1EWR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe56a5bdd-0b81-4703-8496-b208ada15091_840x400.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><div><hr></div><p>I'm deep in interview prep right now.</p><p>DSA, system design, machine coding &#8212; the whole sacred ritual of proving I can reverse a linked list under pressure. I've been doing this for weeks. Waking up early, solving graph problems, designing parking lots on whiteboards in my head, rehearsing how I'd explain the tradeoffs between eventual consistency and strong consistency to a stranger who holds my career in their hands for 45 minutes.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://curiousrhythms.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Curious Rhythms! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Meanwhile, the tool I'm chatting with could probably clear the round I'm prepping for.</p><p>I don't say that to be dramatic. I say it because I tested it. I gave Claude a machine coding problem &#8212; the kind where you're supposed to design a system from scratch in Go under time pressure &#8212; and it produced something clean, extensible, and honestly better-structured than what I'd have written under stress. In less time than it takes me to set up the folder structure.</p><p>There's a quiet existential comedy playing out in every engineer's head right now. If you're in tech, you've felt it. That low hum of cognitive dissonance between "I need to grind harder to get hired" and "the thing I'm grinding to prove I can do is being automated while I grind."</p><p>I've been sitting with this discomfort for a while now. And the analogy that keeps coming back to me &#8212; the one that actually helps me think clearly about what's happening &#8212; isn't from tech at all. It's from the 15th century.</p><div><hr></div><h2>The Scribes and the Press</h2><p>When Gutenberg introduced the printing press around 1440, it didn't make scribes disappear overnight. For a while, both coexisted. Scribes kept copying manuscripts. The press kept printing books. Some scribes probably scoffed at the quality of early printed text &#8212; rough, inconsistent, lacking the artistry of hand-lettered pages.</p><p>But the thing is, the scribe's core skill &#8212; "I can copy text beautifully" &#8212; went from being the <em>entire job</em> to being worth exactly nothing. Not gradually. Not over centuries. Within a generation.</p><p>What survived? The people who understood the <em>content</em>. Editorial judgment. Knowing what's worth saying. The ability to curate, verify, and make decisions about what gets published and why. The scholars. The thinkers. The people whose value was never in the act of writing itself but in the thinking behind it.</p><p>The scribes? They're now a Wikipedia footnote.</p><div><hr></div><h2>Software Engineering's Gutenberg Moment</h2><p>I believe software engineering is entering its printing press moment.</p><p>The "I can build it" era is winding down. AI is getting terrifyingly good at translating specs into working code. It doesn't get tired. It doesn't argue about tabs vs spaces. It doesn't need a standup. It doesn't have a bad day where it writes sloppy code because it had a fight with its roommate or slept poorly.</p><p>And it's getting better fast. Not linearly. The kind of fast where the gap between "impressive parlor trick" and "genuinely useful production tool" collapsed in about 18 months.</p><p>But here's where the analogy gets interesting &#8212; because it's not all doom. It's actually clarifying.</p><div><hr></div><h2>What the Press Can't Do</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jHjM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jHjM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jHjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The gap between \&quot;technically works\&quot; and \&quot;trusted in production\&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The gap between &quot;technically works&quot; and &quot;trusted in production&quot;" title="The gap between &quot;technically works&quot; and &quot;trusted in production&quot;" srcset="https://substackcdn.com/image/fetch/$s_!jHjM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jHjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12b67093-89b1-4a2d-96c6-0990a7e817ae_840x520.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I spent the last year and a half as a Senior Backend Engineer at a fintech startup. We built trade automation &#8212; real money, real markets, real consequences. And I can tell you with certainty what AI cannot do, because I've lived in the gap between "technically works" and "trusted in production."</p><p><strong>It can't know what you don't know yet.</strong></p><p>We had a project &#8212; a service migration that was scoped at one month. It turned into two and a half. Not because the code was hard. Because the cross-service dependencies were invisible until we were knee-deep in them. Five services. Legacy fields that nobody documented. Allocation logic that had edge cases baked in by engineers who left two years ago. No AI could have predicted that from the spec. You had to <em>live</em> in the system to feel where it would break.</p><p><strong>It can't wake up at 3 AM with a gut feeling.</strong></p><p>We had a token refresh system that took 20 minutes every morning for 3,000 users. On paper, it worked. In practice, it was a ticking time bomb &#8212; one slow morning away from users missing market open. The decision to re-architect it &#8212; to move to Cloud Tasks with Redis caching and parallel generation &#8212; didn't come from a prompt. It came from the accumulated anxiety of watching those morning logs and thinking, "this won't survive the next 2x."</p><p><strong>It can't navigate organizational chaos.</strong></p><p>Software doesn't exist in a vacuum. It exists inside companies with politics, competing priorities, limited budgets, and humans who disagree. Getting a system from "I built it" to "it's deployed, monitored, and someone will wake up if it breaks" requires navigating a landscape that is fundamentally social, not technical.</p><p><strong>It can't own the consequences.</strong></p><p>When real money moves through a buggy allocation algorithm, someone has to answer for it. Not "explain what went wrong in a post-mortem" &#8212; actually <em>own</em> it. Feel the weight of it. Carry the scar tissue into the next design decision. That accountability is non-transferable.</p><div><hr></div><h2>The Shift</h2><p>The shift isn't from "engineer" to "no engineer." It's from "I write the code" to "I know what's right, I can verify the machine got it right, and my name is on it when it breaks."</p><p>This is a profound change in what it means to be good at this job. The skills that got you hired in 2020 &#8212; raw coding speed, framework knowledge, the ability to implement a B-tree from scratch &#8212; are becoming table stakes at best and irrelevant at worst.</p><p>The skills that will matter are harder to test in interviews and harder to acquire quickly:</p><p><strong>Systems thinking.</strong> The ability to see a problem in its full context &#8212; business constraints, technical tradeoffs, organizational dynamics, failure modes &#8212; and frame it correctly before anyone writes a line of code.</p><p><strong>Domain expertise.</strong> Understanding what "correct" means in your specific domain. In fintech, that's knowing what SEBI compliance means for your order execution pipeline. In healthcare, it's understanding why a 99.9% accuracy rate might not be good enough. In logistics, it's knowing that your routing algorithm needs to account for the fact that drivers take lunch breaks.</p><p><strong>Verification and judgment.</strong> The more powerful AI gets at generating solutions, the more critical it becomes to have humans who can evaluate those solutions. This is almost paradoxical &#8212; AI doesn't eliminate the need for expertise, it raises the stakes of it.</p><div><hr></div><h2>The Asymmetry</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TzvO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TzvO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 424w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 848w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TzvO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The asymmetry isn't capability &#8212; it's intent&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The asymmetry isn't capability &#8212; it's intent" title="The asymmetry isn't capability &#8212; it's intent" srcset="https://substackcdn.com/image/fetch/$s_!TzvO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 424w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 848w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!TzvO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0df26074-739e-4d7a-bb1a-e191c26b76a3_840x380.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Here's the thought I keep coming back to.</p><p>The asymmetry isn't capability &#8212; it's intent.</p><p>AI can do anything I ask, but it has no reason to ask anything of itself. And even when it learns to self-correct &#8212; to catch its own bugs, to iterate without prompting &#8212; someone still has to decide: is this worth building at all?</p><p>That question doesn't come from intelligence. It comes from living in the world and giving a damn.</p><p>It comes from watching your users struggle with a clunky interface and feeling personally offended. From noticing that your morning token refresh is getting slower and deciding, on your own, without anyone asking, that you're going to fix it. From reading a regulatory announcement and immediately thinking about what it means for your architecture.</p><p>That's not a skill. That's not a capability. It's something closer to <em>caring</em> &#8212; and it's the one thing that can't be automated, no matter how good the models get.</p><div><hr></div><h2>So What Do I Do About It?</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aag_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aag_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 424w, https://substackcdn.com/image/fetch/$s_!aag_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 848w, https://substackcdn.com/image/fetch/$s_!aag_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!aag_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aag_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Scribes vs Scholars &#8212; choose your side of the printing press&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Scribes vs Scholars &#8212; choose your side of the printing press" title="Scribes vs Scholars &#8212; choose your side of the printing press" srcset="https://substackcdn.com/image/fetch/$s_!aag_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 424w, https://substackcdn.com/image/fetch/$s_!aag_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 848w, https://substackcdn.com/image/fetch/$s_!aag_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!aag_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3234fa7b-2b94-4799-8746-7b752e02c7d0_840x560.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I'm still solving graph problems. The game today demands it. The interview system hasn't caught up to the reality shift, and I need a job, so I play by the rules as they exist.</p><p>But I'm keeping one eye on which of my skills are "scribe" skills and which are "scholar" skills.</p><p>The scribe skills: memorizing API signatures, implementing standard patterns from scratch, writing boilerplate, configuring infrastructure by hand. These have a shelf life. Not zero &#8212; not yet &#8212; but the expiry date is visible.</p><p>The scholar skills: knowing what to build, spotting what's wrong with someone else's solution (or an AI's solution), navigating the messy human process of shipping software inside an actual organization, carrying the weight of production systems that move real money. These don't expire. They compound.</p><p>The scholars did just fine after Gutenberg.</p><p>The scribes are now a Wikipedia footnote.</p><p>Choose wisely.</p><p>Or, probably, maybe I'm wrong. It all goes to shit from here.</p><div><hr></div><p><em>If you're a software engineer wrestling with the same questions, I'd love to hear how you're thinking about it. What skills are you investing in? What are you letting go of? Drop a comment or reply to this email.</em></p><p><em>And if you're hiring senior backend engineers who think about systems this way &#8212; well, my DMs are open.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://curiousrhythms.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Curious Rhythms! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How I Self-Hosted Open Claw on a $4 Droplet — And Automated Half My Life With It]]></title><description><![CDATA[From bookmark hygiene to on-call triage, here's how a self-hosted AI agent became the most useful thing running on my infrastructure.]]></description><link>https://curiousrhythms.substack.com/p/how-i-self-hosted-open-claw-on-a-7cc</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/how-i-self-hosted-open-claw-on-a-7cc</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Wed, 11 Mar 2026 19:35:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Xn8b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div><hr></div><h2>Table of Contents</h2><ol><li><p><a href="#why-i-wanted-my-own-ai-agent">Why I Wanted My Own AI Agent</a></p></li><li><p><a href="#setting-up-open-claw-on-digital-ocean">Setting Up Open Claw on Digital Ocean</a></p></li><li><p><a href="#the-n8n-layer--why-open-claw-never-touches-my-keys">The N8N Layer &#8212; Why Open Claw Never Touches My Keys</a></p></li><li><p><a href="#automation-1-daily-bookmark-cleanup-raindrop">Automation 1: Daily Bookmark Cleanup (Raindrop)</a></p></li><li><p><a href="#automation-2-daily-planner--calendar-nudges">Automation 2: Daily Planner + Calendar Nudges</a></p></li><li><p><a href="#automation-3-on-call-monitoring-that-actually-helps-poc">Automation 3: On-Call Monitoring That Actually Helps (PoC)</a></p></li><li><p><a href="#the-honest-downsides">The Honest Downsides</a></p></li><li><p><a href="#key-takeaways">Key Takeaways</a></p></li></ol><div><hr></div><h2>Why I Wanted My Own AI Agent</h2><p>I was doing the same things every single day. Manually.</p><p>Every morning: open Raindrop, scroll through yesterday's saved bookmarks, delete the dead ones, file the rest into collections. Every morning: open Google Calendar, mentally map out my day, set reminders for the important meetings. Every on-call shift: get an alert at 2 AM, SSH into a box, grep through logs, figure out which service is screaming, message the team on Discord with what I found.</p><p>None of this required creativity. None of it required judgment that couldn't be codified. It was pure process &#8212; the kind of work that makes you feel busy without being productive.</p><p>I was already using Claude Code for my development cycles &#8212; it's become core to how I build software. But since I'm on the Claude Max subscription, I can't use Claude APIs directly. So I loaded $20 onto my OpenRouter account and pointed Open Claw at a cheap model for these automations. The tasks don't need frontier intelligence &#8212; bookmark categorization and log parsing work fine with smaller models. What I wanted was a push-based agent that runs on my infrastructure, on my schedule, doing things I'd otherwise have to remember to do.</p><h3>Why Self-Hosted</h3><p>Three reasons:</p><ol><li><p><strong>Privacy.</strong> My monitoring logs contain service names, error traces, and infrastructure details. I'm not piping that through a third-party hosted AI without controlling the environment.</p></li></ol><ol start="2"><li><p><strong>Always-on.</strong> I don't want to open a browser tab and paste things in. I want the agent running 24/7, triggered by crons and alerts, doing its job whether I'm awake or not.</p></li></ol><ol start="3"><li><p><strong>Cost.</strong> A $4/month Digital Ocean droplet running Open Claw costs less than a single month of most AI SaaS tools. I bring my own API keys.</p></li></ol><h3>Why Open Claw</h3><p>Open Claw checked the boxes: open source, self-hostable via Docker, supports multiple LLM backends, has a clean API for programmatic access, and plays well with external tools. It's not the only option &#8212; there's LibreChat, LobeChat, and a dozen others &#8212; but Open Claw's function calling and tool-use support made it the best fit for automation workflows.</p><div><hr></div><h2>Setting Up Open Claw on Digital Ocean</h2><p>The setup is surprisingly straightforward. Here's what I'm running:</p><h3>Droplet Specs</h3><ul><li><p><strong>Size:</strong> $4/month (1 vCPU, 512MB RAM, 10GB SSD)</p></li><li><p><strong>Region:</strong> Bangalore (BLR1) &#8212; closest to me</p></li><li><p><strong>OS:</strong> Ubuntu 22.04 LTS</p></li><li><p><strong>Stack:</strong> Docker Compose (Open Claw + N8N) &#183; Tailscale for access</p></li></ul><h3>Installation</h3><p>I went with a basic Ubuntu flavor &#8212; no fancy pre-built images, no marketplace one-click installs. Just a clean Linux box and a terminal.</p><p>I kept a running log of every command I ran during setup &#8212; every <code>apt install</code>, every config file edit, every Docker command. Once everything was working, I asked Claude to compile all those commands and changes into a clean, reproducible setup report. That report lives in a GitHub repo now.</p><p>Why this matters: if this droplet dies tomorrow, or I want to move to a different cloud provider, I don't have to remember what I did three months ago. I just follow the report. Fifteen minutes and I'm back up. It's infrastructure-as-documentation &#8212; not as elegant as Terraform, but a lot more practical for a single $4 VPS.</p><pre data-params="bash"><code># SSH into the droplet
ssh root@your-droplet-ip

# Install Docker
curl -fsSL https://get.docker.com | sh

# Clone and start Open Claw
git clone https://github.com/open-claw/open-claw.git
cd open-claw
cp .env.example .env
# Edit .env with your LLM API keys

docker compose up -d</code></pre><h3>What's Running</h3><p>The <code>docker-compose.yml</code> runs two containers: Open Claw (<code>:8080</code>) and N8N (<code>:5678</code>), both bound to the Tailscale interface only. No Nginx needed &#8212; Tailscale handles encryption at the network layer. Watchtower auto-updates the containers. Total setup time: about 30 minutes.</p><h3>Cost Breakdown</h3><pre><code>| Item                                 | Monthly Cost     |
|--------------------------------------|------------------|
| Digital Ocean Droplet (512MB)        | $4.00            |
| Domain (amortized)                   | ~$1.00           |
| LLM API via OpenRouter (cheap model) | ~$3-5            |
| **Total**                            | **~$8-10/month** |</code></pre><p>Less than a Netflix subscription. For an AI agent that runs 24/7.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xn8b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xn8b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xn8b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Self-Hosted Open Claw on Digital Ocean &#8212; Architecture Overview&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Self-Hosted Open Claw on Digital Ocean &#8212; Architecture Overview" title="Self-Hosted Open Claw on Digital Ocean &#8212; Architecture Overview" srcset="https://substackcdn.com/image/fetch/$s_!Xn8b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Xn8b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8882bf35-63a2-4e9a-b7b2-0ef9b16d0890_1600x925.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Locking It Down &#8212; Tailscale for Zero-Trust Access</h3><p>Once everything was running, I did something that made the whole setup significantly more secure: I turned off all external connections to the droplet.</p><p>No public ports. No exposed Nginx. No open firewalls. The only way to reach my Open Claw instance is through Tailscale &#8212; a mesh VPN that creates a private network between my devices.</p><p>Here's what I did:</p><ol><li><p><strong>Installed Tailscale</strong> on both my MacBook and the Digital Ocean droplet.</p></li><li><p><strong>Dropped all inbound firewall rules</strong> &#8212; the droplet no longer listens on any public IP.</p></li><li><p><strong>Bound Open Claw and N8N to the Tailscale interface only</strong> &#8212; they're accessible via Tailscale IPs (e.g., <code>100.x.y.z:8080</code>), not the public internet.</p></li></ol><p>The result: my droplet is invisible to the internet. Port scans return nothing. There's no attack surface. The only device that can talk to it is my laptop, authenticated through Tailscale's WireGuard-based identity system.</p><p>This also means I don't need Nginx for TLS anymore &#8212; Tailscale handles encryption at the network layer. One less container, one less thing to maintain, one less cert to renew.</p><p>For a $4 VPS running an AI agent with access to my bookmarks, calendar, and monitoring logs &#8212; even through N8N's read-only layer &#8212; this felt like the right level of paranoia.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5ZwZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Network Security &#8212; Tailscale Zero-Trust Access&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Network Security &#8212; Tailscale Zero-Trust Access" title="Network Security &#8212; Tailscale Zero-Trust Access" srcset="https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5ZwZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0c4dae2-c471-4b9b-8626-f1bab844b69b_1600x840.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>The N8N Layer &#8212; Why Open Claw Never Touches My Keys</h2><p>This is the part I want to be very clear about, because it's the most important architectural decision I made.</p><p><strong>Open Claw does not have any API keys, credentials, or tokens.</strong> Not for Raindrop. Not for Google Calendar. Not for my monitoring stack. Not for Discord. Nothing.</p><p>All external API calls go through N8N workflows. N8N is the only component that stores credentials, and it runs in its own Docker container with its own encrypted credential store.</p><h3>How It Works</h3><ol><li><p>Open Claw decides it needs to do something (e.g., "fetch today's calendar events").</p></li><li><p>Open Claw calls an N8N webhook &#8212; a simple HTTP POST with a JSON payload describing the intent.</p></li><li><p>N8N receives the webhook, authenticates against the relevant API using its stored credentials, executes the request, and returns the result.</p></li><li><p>Open Claw receives processed data. Never credentials.</p></li></ol><h3>Why This Matters</h3><ul><li><p><strong>Open Claw gets compromised?</strong> Attacker gets zero credentials. N8N is a separate container, separate network, separate credential store.</p></li><li><p><strong>All access is read-only.</strong> The API tokens stored in N8N have the minimum permissions needed &#8212; read-only for Raindrop, read-only for calendar, read-only for logs. No write, no delete, no mutate.</p></li><li><p><strong>Every call is auditable.</strong> N8N logs every workflow execution with timestamps, payloads, and responses. If something looks wrong, I can trace exactly what happened.</p></li></ul><p>Think of it as the principle of least privilege applied to AI agents. Open Claw is powerful, but it's powerful within a sandbox. It can orchestrate, but it can't authenticate.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ychr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ychr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ychr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ychr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ychr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ychr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Security Model &#8212; Credential Isolation via N8N&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Security Model &#8212; Credential Isolation via N8N" title="Security Model &#8212; Credential Isolation via N8N" srcset="https://substackcdn.com/image/fetch/$s_!ychr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ychr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ychr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ychr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd320fe1-2035-43c6-99a3-c59230a986f1_1600x800.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Automation 1: Daily Bookmark Cleanup (Raindrop)</h2><p>I save 10-20 bookmarks a day. Articles, GitHub repos, documentation pages, random things I find interesting. Within a week, my Raindrop "Unsorted" collection has 100+ items, half of which I've already read or are dead links.</p><p>Before Open Claw, I'd spend 15-20 minutes every morning manually cleaning this up. Now I don't.</p><h3>What Happens Every Morning at 6 AM</h3><p>A cron triggers an N8N workflow that kicks off the following chain:</p><ol><li><p><strong>Fetch all unsorted bookmarks</strong> &#8212; N8N calls Raindrop's API, pulls everything in the "Unsorted" collection.</p></li><li><p><strong>Check for dead links</strong> &#8212; N8N hits each URL with a HEAD request. Anything that returns a 404, 410, or times out after 5 seconds gets flagged.</p></li><li><p><strong>Detect duplicates</strong> &#8212; Open Claw normalizes URLs (strips tracking params, www prefixes, trailing slashes) and identifies duplicates across all collections.</p></li><li><p><strong>Auto-categorize</strong> &#8212; Open Claw reads the page title and URL pattern and assigns each bookmark to the right collection: "Engineering," "Design," "Tools," "Reading List," etc. This is where the LLM shines &#8212; it understands that a GitHub repo about "distributed tracing" belongs in "Engineering," not "Tools."</p></li><li><p><strong>Execute and report</strong> &#8212; N8N moves bookmarks to their collections, removes dead links and duplicates, and Open Claw posts a summary to Discord.</p></li></ol><h3>What the Discord Summary Looks Like</h3><pre><code>&#128203; Raindrop Daily Cleanup &#8212; Mar 12, 2026

Processed: 18 bookmarks
&#9500;&#9472;&#9472; Categorized: 12 (5 Engineering, 3 Tools, 2 Reading, 2 Design)
&#9500;&#9472;&#9472; Dead links removed: 3
&#9500;&#9472;&#9472; Duplicates removed: 2
&#9492;&#9472;&#9472; Skipped (manual review): 1

Needs your attention:
&#8226; "Building a Real-Time..." &#8212; couldn't determine category (ambiguous)

Time taken: 14 seconds</code></pre><p>Fifteen minutes of daily busywork, gone. Every morning I wake up to a clean bookmark library and a one-paragraph summary of what changed.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Vq31!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Vq31!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Vq31!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Automation 1 &#8212; Daily Bookmark Cleanup&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Automation 1 &#8212; Daily Bookmark Cleanup" title="Automation 1 &#8212; Daily Bookmark Cleanup" srcset="https://substackcdn.com/image/fetch/$s_!Vq31!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Vq31!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1366eac4-c13e-4ca1-9de5-bf31d09801eb_1600x800.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Automation 2: Daily Planner + Calendar Nudges</h2><p>This one sounds simple &#8212; "pull calendar events and make a plan" &#8212; but it's become the automation I rely on most.</p><h3>Morning Plan (7 AM)</h3><p>Every morning, Open Claw pulls my Google Calendar events via N8N and generates a structured daily plan. Not just a list of meetings &#8212; an actual plan:</p><pre><code>&#128467; Your Day &#8212; Mar 12, 2026 (Thursday)

08:00 - 09:30  Deep work block (no meetings)
               &#8594; Suggested: Continue the API migration (you left off at the auth middleware)

09:30 - 10:00  Standup (Engineering)
               &#8594; 3 attendees &#183; Google Meet link attached

10:00 - 12:00  Deep work block
               &#8594; Suggested: Review open PRs (4 pending, oldest is 3 days)

12:00 - 13:00  Lunch

13:00 - 13:30  1:1 with [manager name]
               &#8594; Last week you discussed: Q2 roadmap priorities

14:00 - 16:00  Deep work block
               &#8594; Suggested: Write the tech spec for the notification service refactor

16:00 - 16:30  Team sync (Backend)
               &#8594; Agenda shared in #backend-team

17:00          End of scheduled day</code></pre><p>The deep work suggestions are based on context from previous days &#8212; Open Claw remembers what I was working on and picks up where I left off.</p><h3>Calendar Nudges (Throughout the Day)</h3><p>This is the part that makes it feel like having a personal assistant. Fifteen minutes before every calendar event, Open Claw pings me on Discord:</p><pre><code>&#9200; In 15 minutes: 1:1 with [name]
&#128205; Google Meet: [link]
&#128221; Last time you discussed: Q2 roadmap priorities, hiring timeline
&#128161; You had a follow-up action: Share the capacity planning doc</code></pre><p>It's not just "you have a meeting" &#8212; it's "here's the context you need to walk in prepared." The follow-up action reminder alone has saved me from showing up to 1:1s having forgotten what I promised to do.</p><h3>End of Day (9 PM)</h3><p>A quick summary of what was planned vs. what happened:</p><pre><code>&#128202; Day Summary &#8212; Mar 12, 2026

Meetings attended: 3/3
Deep work blocks used: 2/3 (missed the 14:00-16:00 block)
Unplanned: 1 ad-hoc call (30 min)

Tomorrow's first event: Standup at 09:30</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fife!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fife!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Fife!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Fife!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Fife!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fife!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Automation 2 &#8212; Daily Planner + Calendar Nudges&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Automation 2 &#8212; Daily Planner + Calendar Nudges" title="Automation 2 &#8212; Daily Planner + Calendar Nudges" srcset="https://substackcdn.com/image/fetch/$s_!Fife!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Fife!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Fife!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Fife!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa64a1f6c-dfe1-41f7-b3fb-04e652fbf592_1600x840.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Automation 3: On-Call Monitoring That Actually Helps (PoC)</h2><p>This is the big one. And I want to be upfront: <strong>this is currently in PoC mode.</strong> It works. My team and I use it. But it's not production-hardened, and I'm still iterating on the reliability.</p><h3>The Problem</h3><p>On-call is exhausting not because of the alerts themselves, but because of the <strong>triage ritual</strong> that follows every alert:</p><ol><li><p>Get paged.</p></li><li><p>SSH into the relevant box or open the monitoring dashboard.</p></li><li><p>Grep through logs for the error signature.</p></li><li><p>Figure out which service is actually broken vs. which one is just complaining because an upstream dependency is broken.</p></li><li><p>Formulate a hypothesis.</p></li><li><p>Message the team on Discord with what you found and what you think should happen next.</p></li></ol><p>Steps 2-5 are mechanical. They follow a pattern. The experienced on-call engineer is faster at them not because they're smarter, but because they've seen the patterns before and know where to look.</p><p>That's exactly what Open Claw is good at.</p><h3>The Architecture</h3><pre><code>Alert (Zenduty)
    &#9474;
    &#9660;
Open Claw (receives alert webhook)
    &#9474;
    &#9474; "I need logs for service X, last 30 minutes"
    &#9660;
N8N Workflow
    &#9474;
    &#9474; (uses read-only credentials stored in N8N)
    &#9660;
Log Store (Loki/ELK/CloudWatch)
    &#9474;
    &#9474; returns filtered logs
    &#9660;
N8N &#8594; Open Claw
    &#9474;
    &#9474; analyzes logs, identifies patterns
    &#9660;
Discord Message
    &#9474;
    "Here's what I found, here's what I think is wrong,
     here are the suggested next steps"</code></pre><p><strong>The critical detail: Open Claw never authenticates against the log store directly.</strong> It sends an intent ("get me logs for service X") to an N8N workflow. N8N has the read-only credentials. N8N makes the API call. N8N returns the data. Open Claw only ever sees log text &#8212; never tokens, never connection strings, never credentials.</p><h3>What Happens When an Alert Fires</h3><ol><li><p><strong>Alert arrives</strong> &#8212; Zenduty sends a webhook to Open Claw with the alert metadata: service name, severity, error message, timestamp.</p></li></ol><ol start="2"><li><p><strong>Open Claw triggers log retrieval</strong> &#8212; It sends a request to N8N: "Get me the last 30 minutes of logs for <code>payment-service</code>, filtered for ERROR and WARN levels."</p></li></ol><ol start="3"><li><p><strong>N8N fetches logs</strong> &#8212; Using its stored read-only credentials, N8N queries the log store, filters the results, and returns them to Open Claw.</p></li></ol><ol start="4"><li><p><strong>Open Claw analyzes</strong> &#8212; This is where the LLM does its thing. It reads the logs and:</p></li></ol><ul><li><p>Identifies the error pattern (e.g., "connection refused to <code>inventory-service</code> on port 8443")</p></li><li><p>Maps the dependency chain (payment-service depends on inventory-service)</p></li><li><p>Checks if the upstream service is also alerting (often the real problem is two levels up)</p></li><li><p>Formulates a hypothesis ("inventory-service is down, causing payment-service to fail on inventory checks")</p></li></ul><ol start="5"><li><p><strong>Posts to Discord</strong> &#8212; A structured triage summary lands in the team's incident channel:</p></li></ol><pre><code>&#128680; Alert Triage &#8212; payment-service (P2)

Triggered: 2026-03-12 02:14 AM IST
Duration: Alert active for 4 minutes

Root Cause (probable):
  inventory-service is returning connection refused on port 8443.
  payment-service depends on inventory-service for stock validation
  during checkout. All checkout attempts are failing.

Impacted Services:
  &#8226; payment-service (direct) &#8212; checkout flow broken
  &#8226; order-service (indirect) &#8212; new orders can't be created
  &#8226; notification-service (unaffected) &#8212; no dependency on inventory

Evidence:
  &#8226; 47 "connection refused" errors in payment-service logs (last 15 min)
  &#8226; inventory-service last healthy log entry: 02:10 AM
  &#8226; No deployment detected in last 2 hours

Suggested Next Steps:
  1. Check inventory-service pod status (likely crashed or OOMKilled)
  2. If OOMKilled, restart with increased memory limit
  3. If networking issue, check service mesh / DNS resolution
  4. Once inventory-service recovers, payment-service should auto-heal
     (circuit breaker will close after 3 successful health checks)

&#9888;&#65039; This is an AI-generated triage. Verify before acting.</code></pre><h3>How the Team Uses It</h3><p>Right now, it's me and two other engineers on the team using this. We don't blindly follow the suggestions &#8212; we treat Open Claw's triage like a junior engineer's analysis: usually directionally correct, occasionally wrong on specifics, always worth reading before diving in.</p><p>The time savings are real. Before Open Claw, the triage step alone took 10-20 minutes per alert. Now we get a structured analysis within 60 seconds of the alert firing. We skip straight to verification and action.</p><h3>What's Still PoC About It</h3><ul><li><p><strong>Hallucination risk.</strong> Open Claw sometimes identifies the wrong root cause, especially for failure modes it hasn't seen before. The "verify before acting" disclaimer isn't optional.</p></li><li><p><strong>Log volume limits.</strong> For services that produce massive log volumes, the 30-minute window can exceed what the LLM can process. I'm working on smarter filtering.</p></li><li><p><strong>Single-cluster only.</strong> It currently works for one Kubernetes cluster. Multi-cluster support needs more N8N workflow plumbing.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pwg_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pwg_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pwg_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Automation 3 &#8212; On-Call Monitoring + Triage via N8N&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Automation 3 &#8212; On-Call Monitoring + Triage via N8N" title="Automation 3 &#8212; On-Call Monitoring + Triage via N8N" srcset="https://substackcdn.com/image/fetch/$s_!Pwg_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pwg_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32a5f382-fa09-43b7-aac2-0eed4c502ed3_1600x978.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>The Honest Downsides</h2><h3>1. The $4 Droplet Has Limits</h3><p>512MB of RAM is tight. Open Claw and N8N together use most of it at idle. During a monitoring triage (when logs are being processed), it can spike and hit swap. I'm considering upgrading to the $6 droplet (1GB RAM) but haven't needed to yet.</p><h3>2. Hallucinations Are Real</h3><p>Open Claw is an LLM wrapper at its core. It can &#8212; and does &#8212; get things wrong. The bookmark categorization is wrong maybe 5% of the time (fixable with a quick manual correction). The monitoring triage is wrong maybe 15-20% of the time (which is why it's still a PoC with a human-in-the-loop).</p><h3>3. Self-Hosting Means Self-Maintaining</h3><p>Docker containers crash. N8N workflows break when APIs change their response format. I spend maybe 30 minutes a month on maintenance &#8212; not bad, but not zero.</p><h3>4. The Calendar Planner Doesn't Know What I Actually Did</h3><p>It can tell me what was scheduled, but it can't tell me if I actually did the deep work I planned. It's a planner, not a tracker. I still rely on my own discipline for execution.</p><h3>5. N8N Adds Complexity</h3><p>An extra container, extra configuration, extra failure surface. But the security isolation is worth it. I'd rather debug an N8N workflow than worry about my monitoring credentials sitting inside an LLM's context window.</p><div><hr></div><h2>Key Takeaways</h2><ol><li><p><strong>Self-hosting an AI agent is surprisingly accessible.</strong> A $4 droplet, Docker Compose, and 30 minutes of setup. You don't need a GPU. You don't need Kubernetes. You need a VPS and <code>docker compose up</code>.</p></li></ol><ol start="2"><li><p><strong>Never give your AI agent direct access to credentials.</strong> Use N8N (or any workflow tool) as a middleware layer. Open Claw sends intents, N8N executes authenticated calls. Credentials never leave N8N's vault.</p></li></ol><ol start="3"><li><p><strong>Start with low-stakes automations.</strong> Bookmark cleanup was my first automation because the worst case was a miscategorized link &#8212; not a production incident. Build trust in the system before giving it anything consequential.</p></li></ol><ol start="4"><li><p><strong>Read-only credentials are your friend.</strong> Every API token stored in N8N has the minimum permissions needed. Even if everything goes wrong, the blast radius is "it read some data it shouldn't have," not "it deleted my production database."</p></li></ol><ol start="5"><li><p><strong>The monitoring PoC saves real time.</strong> 10-20 minutes of manual triage per alert, reduced to 60 seconds of reading an AI-generated summary. Multiply that by 3-4 alerts per on-call shift, and the math works out fast.</p></li></ol><ol start="6"><li><p><strong>Human-in-the-loop is not optional for high-stakes automations.</strong> Bookmark cleanup can be fully autonomous. On-call triage cannot. Open Claw suggests, humans decide. That boundary is load-bearing.</p></li></ol><ol start="7"><li><p><strong>The $10/month total cost pays for itself in the first week.</strong> Between bookmark cleanup (15 min/day), daily planning (10 min/day), and on-call triage (10-20 min/alert), the time savings are measured in hours per week.</p></li></ol><div><hr></div><p><em>Running on a Digital Ocean droplet that costs less than a coffee. If you want to try something similar, start with the bookmark automation &#8212; it's the easiest win and the lowest risk.</em></p><p><em>Connect with me at [mrinal.dev](https://mrinal.dev). I'm always happy to talk about self-hosted AI, automation workflows, and engineering productivity.</em></p>]]></content:encoded></item><item><title><![CDATA[I Built a Tool to Never Forget What I Learn — Here's How to Use It]]></title><description><![CDATA[I kept solving the same LeetCode problems over and over. So I built Revise &#8212; a browser extension + dashboard that uses spaced repetition to tell you exactly when to revisit what you've studied.]]></description><link>https://curiousrhythms.substack.com/p/i-built-a-tool-to-never-forget-what</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/i-built-a-tool-to-never-forget-what</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Sat, 07 Mar 2026 08:31:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0wgF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>---</p><h2>The Problem That Started It All</h2><p>Here's a cycle every competitive programmer knows too well:</p><p>1. Spend 45 minutes solving a dynamic programming problem on LeetCode.</p><p>2. Feel great about it. Move on.</p><p>3. See the same pattern three weeks later in a contest. Draw a complete blank.</p><p>4. Re-solve it from scratch. Again.</p><p>I was stuck in this loop for months. I'd solve 200+ problems but could barely recall the approaches for half of them. The issue wasn't that I wasn't practicing &#8212; it was that I wasn't <strong>revisiting</strong> at the right time.</p><p>I tried maintaining spreadsheets. I tried setting calendar reminders. I even tried sticky notes on my monitor. Nothing stuck &#8212; ironic, given that the whole goal was to make things stick.</p><p>---</p><h2>What Inspired Me to Build Revise</h2><p>Three things converged:</p><h3>1. The Forgetting Curve Hit Me Hard</h3><p>In 1885, Hermann Ebbinghaus demonstrated that we forget roughly 70% of newly learned information within 24 hours if we don't review it. But &#8212; and this is the key insight &#8212; each review at the right moment makes the memory decay slower. The optimal review schedule isn't "every day" or "every week." It's a curve that stretches out exponentially: 1 day, then 6 days, then 15 days, then 35 days, and so on.</p><p>I already knew about this from using Anki for medical terminology during college. But I never connected it to coding until I realized the pattern: solving problems and forgetting them was literally the Ebbinghaus curve playing out in real-time.</p><h3>2. Anki Was Too Much Friction</h3><p>Anki is incredible for flashcards. But for coding problems, it's clunky. I didn't want to create a flashcard for every LeetCode problem I solved. I didn't want to write the "front" and "back" of a card. I just wanted to:</p><p>Solve a problem</p><p>Click a button</p><p>Have the system figure out when I should revisit it</p><p>That's it. No card creation. No manual data entry. Just <strong>track and schedule</strong>.</p><h3>3. I Wanted It Where I Already Was</h3><p>I didn't want to switch to a separate app every time I finished a problem. I wanted the tracking to happen <strong>right there</strong> &#8212; on LeetCode, on Codeforces, on HackerRank. A browser extension that auto-detects what I'm working on felt like the only workflow that wouldn't get abandoned after a week.</p><p>So I built <a>Revise</a>.</p><p>---</p><h2>What Revise Actually Does</h2><p>Revise is two things:</p><p>1. <strong>A browser extension</strong> (Chrome and Safari) that sits in your toolbar and lets you track anything you study with one click.</p><p>2. <strong>A web dashboard</strong> that shows you what's due for revision, your stats, and your full learning history.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0wgF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0wgF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 424w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 848w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0wgF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0wgF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 424w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 848w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 1272w, https://substackcdn.com/image/fetch/$s_!0wgF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b21773c-a34d-4d78-a311-791ece68ebd4_1524x1368.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The extension auto-detects 10+ learning platforms &#8212; LeetCode, Codeforces, HackerRank, CodeChef, GeeksForGeeks, InterviewBit, AtCoder, NeetCode, AlgoMonster, and DesignGurus. Navigate to any problem on these sites, and the extension picks up the URL and title automatically.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L6mu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L6mu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 424w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 848w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 1272w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L6mu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!L6mu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 424w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 848w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 1272w, https://substackcdn.com/image/fetch/$s_!L6mu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F315a4ec0-1a72-40e8-88c4-0b746617651a_1061x1319.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>But here's the thing &#8212; it's not just for coding. Add any custom platform from the dashboard settings, and you can track Khan Academy lessons, design tutorials, language exercises, whatever you're learning. The SM-2 algorithm doesn't care what the subject is. It only cares about how well you remembered it.</p><p>---</p><h2>The Science Behind It &#8212; SM-2 Algorithm</h2><p>Revise uses the <strong>SM-2 algorithm</strong> &#8212; the same algorithm that powers Anki and SuperMemo, two of the most successful spaced repetition tools ever built.</p><p>Here's how it works in plain English:</p><p>1. <strong>You rate your recall from 1 to 5 stars</strong> after revisiting something:</p><p>&#11088; (1) &#8212; Complete blackout. Couldn't recall anything.</p><p>&#11088;&#11088; (2) &#8212; Struggled heavily. Got it wrong.</p><p>&#11088;&#11088;&#11088; (3) &#8212; Got it right, but with significant effort.</p><p>&#11088;&#11088;&#11088;&#11088; (4) &#8212; Got it right with some hesitation.</p><p>&#11088;&#11088;&#11088;&#11088;&#11088; (5) &#8212; Perfect recall. Effortless.</p><p>2. <strong>The algorithm adjusts two things:</strong></p><p><strong>Interval</strong> &#8212; how many days until your next review. Starts at 1 day, then 6 days, then grows exponentially based on your ratings.</p><p><strong>Easiness Factor (EF)</strong> &#8212; a per-item difficulty multiplier. Items you consistently rate low get reviewed more frequently. Items you find easy gradually space out to weeks, then months.</p><p>3. <strong>If you rate 1 or 2 stars</strong> (quality &lt; 3 in SM-2 terms), the algorithm <strong>resets the interval to 1 day</strong>. You start over. This is harsh but effective &#8212; it ensures you don't advance material you haven't actually internalized.</p><p>The math behind the easiness factor update:</p><pre><code>EF' = EF + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02))</code></pre><p>Where `q` is the quality rating (0-5) and `EF` never drops below 1.3. This formula ensures that the EF converges towards a stable value that reflects the true difficulty of that specific item for you.</p><p>The result: things you find easy come back in 30, 60, 90 days. Things you struggle with come back tomorrow. Over time, your entire library of learned material is maintained with minimal daily effort.</p><p>---</p><h2>How to Get Started (Under 60 Seconds)</h2><h3>Step 1: Sign Up</h3><p>Go to <a>revise.mrinal.dev</a> and click <strong>Get Started Free</strong>.</p><p>Enter your email. No password needed &#8212; Revise uses <strong>magic link authentication</strong>. You'll get an email with a link. Click it. You're in.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XRVD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XRVD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 424w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 848w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 1272w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XRVD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XRVD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 424w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 848w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 1272w, https://substackcdn.com/image/fetch/$s_!XRVD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F612f802a-503b-44b4-9d06-8a4bfb6e64ea_1208x1118.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>No credit card. No trial period. No catch. It's free and open source.</p><h3>Step 2: Install the Browser Extension</h3><p><strong>Chrome:</strong></p><p>1. Download <a>`extension.zip`</a> from GitHub</p><p>2. Unzip it</p><p>3. Go to `chrome://extensions`</p><p>4. Enable <strong>Developer mode</strong> (top right)</p><p>5. Click <strong>Load unpacked</strong> and select the unzipped folder</p><p>6. Pin the extension from the puzzle icon in your toolbar</p><p><strong>Safari:</strong> Available on request &#8212; reach out at dmrinal626@gmail.com for the build.</p><h3>Step 3: Start Tracking</h3><p>That's it. Navigate to any supported platform, click the extension, and you're tracking.</p><p>---</p><h2>Using the Browser Extension</h2><p>The extension is designed to get out of your way. Here's the workflow:</p><h3>1. Navigate to a Problem</h3><p>Go to any problem on a supported platform. The extension auto-detects the platform, URL, and page title.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nPVl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nPVl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 424w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 848w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 1272w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nPVl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nPVl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 424w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 848w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 1272w, https://substackcdn.com/image/fetch/$s_!nPVl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93979af8-36a6-401d-8705-e2a4a5c51fe6_396x526.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>2. Start the Timer</h3><p>Click <strong>Start Timer</strong> when you begin working on the problem. The timer runs in the background &#8212; you can switch tabs, take breaks, whatever. Pause and resume as needed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NuGb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NuGb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 424w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 848w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 1272w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NuGb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b60b4876-c581-4356-910b-262a19090397_401x468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NuGb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 424w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 848w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 1272w, https://substackcdn.com/image/fetch/$s_!NuGb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb60b4876-c581-4356-910b-262a19090397_401x468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>3. Rate and Save</h3><p>When you're done, stop the timer. The extension asks you to:</p><p><strong>Set the difficulty</strong> (Easy / Medium / Hard)</p><p><strong>Rate your recall</strong> (1-5 stars) &#8212; this feeds directly into the SM-2 algorithm</p><p><strong>Add notes</strong> (optional) &#8212; jot down the key insight, the pattern used, or a reminder for future you</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Hia!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Hia!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 424w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 848w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 1272w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Hia!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8Hia!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 424w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 848w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 1272w, https://substackcdn.com/image/fetch/$s_!8Hia!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b1a3c3a-1db4-40aa-8232-41f2b730751b_449x507.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Hit <strong>Save</strong>. The SM-2 algorithm immediately calculates when you should revisit this problem and schedules it.</p><h3>4. Check What's Due</h3><p>The extension badge shows how many items are due for revision today. Click it to see the list. When you revisit a problem, rate your recall again &#8212; the algorithm adjusts and reschedules.</p><p>---</p><h2>The Dashboard &#8212; Your Learning Command Center</h2><p>The dashboard at <a>revise.mrinal.dev/dashboard</a> gives you the full picture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sBVl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sBVl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 424w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 848w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 1272w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sBVl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sBVl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 424w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 848w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 1272w, https://substackcdn.com/image/fetch/$s_!sBVl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee08a7ec-a72f-4979-b4f4-16708ae81c63_2500x1306.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What You'll See:</h3><p><strong>Items Tracked</strong> &#8212; total number of problems/topics you're tracking</p><p><strong>Difficulty Breakdown</strong> &#8212; pie chart of Easy / Medium / Hard distribution</p><p><strong>Platform Distribution</strong> &#8212; how many items from each platform</p><p><strong>Revision Schedule</strong> &#8212; upcoming reviews charted over time</p><p><strong>Daily Activity</strong> &#8212; your study streak and daily patterns</p><p><strong>Full History Table</strong> &#8212; filterable, sortable table of everything you've ever tracked, with next review dates, ratings, time spent, and notes</p><h3>Revising from the Dashboard</h3><p>The dashboard highlights items that are <strong>due today</strong>. Click on any item to revisit it &#8212; the original URL opens in a new tab. After you've reviewed it, update your recall rating directly from the dashboard. The SM-2 algorithm recalculates and updates the schedule.</p><h3>CSV Export</h3><p>Need your data elsewhere? Export your entire history as a CSV with one click.</p><p>---</p><h2>Adding Custom Platforms</h2><p>Revise auto-detects 10+ platforms out of the box, but you're not limited to those. From the dashboard settings, you can add any website:</p><p>1. Go to <strong>Settings</strong> in the dashboard</p><p>2. Click <strong>Add Custom Platform</strong></p><p>3. Enter a <strong>name</strong> (e.g., "Khan Academy") and a <strong>URL pattern</strong> (e.g., `khanacademy.org`)</p><p>4. Save</p><p>Now when you visit Khan Academy and click the extension, it auto-detects the platform name, just like the built-in ones.</p><p>This makes Revise useful beyond coding. Track:</p><p><strong>Math exercises</strong> on Khan Academy</p><p><strong>Design tutorials</strong> on Figma Learn</p><p><strong>Language lessons</strong> on Duolingo or Pimsleur</p><p><strong>System design articles</strong> on your favorite blog</p><p>Literally anything with a URL</p><p>---</p><h2>The Tech Stack</h2><p>For the curious, here's what powers Revise:</p><pre><code>Browser Extension (Chrome / Safari)
        |
        |  REST API
        v
   FastAPI Server  &#8594;  Supabase (Postgres + Auth)
        |
        v
   Web Dashboard (Jinja2 templates)</code></pre><p><strong>Backend:</strong> Python + FastAPI &#8212; lightweight, async, and fast</p><p><strong>Database:</strong> Supabase (managed PostgreSQL) with Row Level Security &#8212; each user's data is completely isolated</p><p><strong>Auth:</strong> Supabase Magic Links &#8212; no passwords to store, no password resets to build</p><p><strong>Frontend:</strong> Server-rendered Jinja2 templates &#8212; no SPA framework, no build step, just HTML/CSS/JS</p><p><strong>Extension:</strong> Vanilla JavaScript &#8212; no React, no Webpack, just a popup that does its job</p><p><strong>Hosting:</strong> The dashboard runs on Vercel/Docker, the API on any container platform</p><p>I deliberately kept the stack simple. No Next.js. No Redux. No GraphQL. The entire server is a single `main.py` file with a few helper modules. The extension is under 500 lines of JavaScript. Simple tools, used well.</p><p>---</p><h2>Self-Hosting for Developers</h2><p>Revise is fully open source under the MIT license. If you want to self-host:</p><p>1. <strong>Clone the repo:</strong></p><pre><code>git clone https://github.com/the-mrinal/code-revision-tracker.git</code></pre><p>2. <strong>Set up Supabase:</strong> Create a free project at <a>supabase.com</a> and run the SQL schema from the README.</p><p>3. <strong>Configure environment variables:</strong></p><pre><code>SUPABASE_URL=your-project-url
   SUPABASE_KEY=your-anon-key
   SUPABASE_SERVICE_KEY=your-service-key</code></pre><p>4. <strong>Run with Docker:</strong></p><pre><code>docker-compose up</code></pre><p>5. <strong>Update the extension</strong> to point to your server URL.</p><p>Full setup instructions are in the <a>GitHub README</a>.</p><p>---</p><h2>What's Next</h2><p>Revise is already useful &#8212; I use it daily and it's changed how I retain information from coding practice. But there's more I want to build:</p><p><strong>Mobile app</strong> &#8212; check what's due and do quick reviews on the go</p><p><strong>Collaborative revision</strong> &#8212; share revision lists with study groups</p><p><strong>AI-powered hints</strong> &#8212; when you revisit a problem, get a nudge about the approach without seeing the full solution</p><p><strong>More analytics</strong> &#8212; retention rate over time, weakest topics, optimal study times</p><p><strong>Firefox extension</strong> &#8212; because not everyone uses Chrome</p><p>---</p><h2>Try It</h2><p>If you're tired of forgetting what you've learned &#8212; whether it's coding problems, math concepts, or anything else &#8212; give Revise a shot.</p><p>[revise.mrinal.dev](https://revise.mrinal.dev) &#8212; sign up in 10 seconds, no password, no payment.</p><p>[GitHub](https://github.com/the-mrinal/code-revision-tracker) &#8212; star the repo, open an issue, or contribute.</p><p>The best time to start retaining what you learn was yesterday. The second best time is today.</p><p>---</p><p>Built by <a>Mrinal</a>. If you found this useful, share it with someone who's stuck in the solve-forget-repeat loop.</p>]]></content:encoded></item><item><title><![CDATA[The CAP Theorem in Practice — How Firestore, Cassandra, and Redis Cluster Actually Handle Partition-Tolerance Trade-offs]]></title><description><![CDATA[Moving beyond 'pick 2 of 3' to understand how real distributed databases make life-or-death decisions when the network splits.]]></description><link>https://curiousrhythms.substack.com/p/the-cap-theorem-in-practice-how-firestore-bd1</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/the-cap-theorem-in-practice-how-firestore-bd1</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Wed, 11 Feb 2026 09:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G4E1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div><hr></div><h2>Table of Contents</h2><ol><li><p><a href="#the-origin-story">The Origin Story</a></p></li><li><p><a href="#the-three-properties--precisely-defined">The Three Properties &#8212; Precisely Defined</a></p></li><li><p><a href="#why-pick-2-of-3-is-a-dangerous-oversimplification">Why "Pick 2 of 3" Is a Dangerous Oversimplification</a></p></li><li><p><a href="#deep-dive-google-cloud-firestore-cp">Deep Dive: Google Cloud Firestore (CP)</a></p></li><li><p><a href="#deep-dive-apache-cassandra-ap-with-tunable-consistency">Deep Dive: Apache Cassandra (AP with Tunable Consistency)</a></p></li><li><p><a href="#deep-dive-redis-cluster-cp-biased-but-leaky">Deep Dive: Redis Cluster (CP-Biased, but Leaky)</a></p></li><li><p><a href="#side-by-side-comparison">Side-by-Side Comparison</a></p></li><li><p><a href="#the-pacelc-extension--what-cap-doesnt-tell-you">The PACELC Extension &#8212; What CAP Doesn't Tell You</a></p></li><li><p><a href="#choosing-the-right-system-for-your-use-case">Choosing the Right System for Your Use Case</a></p></li><li><p><a href="#key-takeaways">Key Takeaways</a></p></li><li><p><a href="#references">References</a></p></li></ol><div><hr></div><h2>The Origin Story</h2><p>In autumn 1998, Eric Brewer &#8212; then a professor at UC Berkeley &#8212; introduced what he called the CAP principle. He formally presented it as a conjecture at the ACM Symposium on Principles of Distributed Computing (PODC) in 2000. Two years later, in 2002, Seth Gilbert and Nancy Lynch at MIT published a formal mathematical proof, transforming Brewer's conjecture into a full-blown theorem.</p><p>The theorem made a deceptively simple claim: a distributed data store cannot simultaneously provide more than two out of three guarantees &#8212; Consistency, Availability, and Partition Tolerance.</p><p>This one sentence reshaped how an entire generation of engineers thought about distributed systems. It also led to one of the most widespread misunderstandings in computer science.</p><div><hr></div><h2>The Three Properties &#8212; Precisely Defined</h2><p>Before we can understand the trade-offs, we need to be precise about what each property actually means. The colloquial definitions that float around blog posts and interview prep sites are often subtly wrong, and those subtle errors lead to real architectural mistakes.</p><h3>Consistency (C) &#8212; Linearizability</h3><p>Consistency in CAP is <strong>not</strong> the same "C" as in ACID. ACID consistency means a transaction moves the database from one valid state to another. CAP consistency is far more specific.</p><p>CAP Consistency means <strong>linearizability</strong>: all operations appear to execute instantaneously at some point between their invocation and their response, and that order is consistent across all nodes. In practical terms:</p><ul><li><p>If Client A writes <code>balance = 500</code> and gets an acknowledgment, then Client B reading <code>balance</code> from <em>any</em> node must see <code>500</code> (or a later value).</p></li><li><p>There is a single, globally agreed-upon order of operations.</p></li><li><p>There are no "stale reads." Ever.</p></li></ul><p>This is an extremely strong guarantee. Most systems that claim to be "consistent" are actually providing weaker models like sequential consistency, causal consistency, or read-your-writes consistency &#8212; none of which satisfy CAP's definition.</p><h3>Availability (A) &#8212; Every Non-Failing Node Responds</h3><p>Availability in CAP means <strong>every request received by a non-failing node must result in a response</strong>. Not an error. Not a timeout. A response with data.</p><p>Crucially, the response doesn't have to contain the <em>latest</em> data. An available system can return stale data. It just can't return nothing or an error.</p><p>This definition is also stronger than it sounds. It means:</p><ul><li><p>You cannot redirect the client to another node (that would be a form of unavailability for the original node).</p></li><li><p>You cannot return an error code saying "try again later."</p></li><li><p>You cannot hang indefinitely waiting for the partition to heal.</p></li></ul><h3>Partition Tolerance (P) &#8212; Surviving Network Splits</h3><p>A network partition occurs when communication between two groups of nodes is completely lost. Messages sent between the groups are either dropped or delayed indefinitely.</p><p>Partition Tolerance means the system <strong>continues to function</strong> (in whatever degraded mode it chooses) when such a partition occurs. It doesn't crash. It doesn't corrupt data. It makes a deliberate choice about how to handle the situation.</p><p>Here's the part most people miss: <strong>partition tolerance is not optional.</strong> In any system deployed across multiple machines, network partitions <em>will</em> happen. Switches fail. Cables get cut. Cloud availability zones lose connectivity. The question is never "should we tolerate partitions?" &#8212; it's "what do we sacrifice when a partition occurs?"</p><p>This is why the real CAP trade-off is: <strong>when a partition happens, do you sacrifice Consistency or Availability?</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G4E1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G4E1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G4E1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;CAP Theorem &#8212; The Real Trade-off&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="CAP Theorem &#8212; The Real Trade-off" title="CAP Theorem &#8212; The Real Trade-off" srcset="https://substackcdn.com/image/fetch/$s_!G4E1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G4E1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F310802e2-b065-41cc-b3cc-1ab91f0e3140_2000x2000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Why "Pick 2 of 3" Is a Dangerous Oversimplification</h2><p>You've probably seen the CAP Venn diagram &#8212; three overlapping circles labeled C, A, and P, with systems placed in the overlapping regions: CP, AP, or CA. This model is not just simplistic &#8212; it's actively misleading. Here's why:</p><h3>1. The Trade-off Only Applies During Partitions</h3><p>During normal operation (no partitions), a well-designed system can provide all three properties simultaneously. Firestore gives you strong consistency <em>and</em> high availability <em>and</em> partition tolerance &#8212; right up until a partition actually occurs.</p><p>The CAP theorem only constrains behavior <strong>during</strong> a partition event. Since partitions are (thankfully) rare in well-maintained infrastructure, a system spends the vast majority of its life in a state where CAP doesn't force any trade-off at all.</p><h3>2. The Choice Is Granular, Not Global</h3><p>A single system doesn't have to make one permanent choice. Different subsystems, different data types, even different queries can make different trade-offs:</p><ul><li><p>Your user authentication might be strongly consistent (CP behavior) because showing the wrong session state is dangerous.</p></li><li><p>Your activity feed might be eventually consistent (AP behavior) because a 2-second delay in seeing a new post is harmless.</p></li><li><p>Your shopping cart might use session-level consistency &#8212; consistent within one user's session, eventually consistent across the system.</p></li></ul><p>Cassandra embodies this perfectly with per-query consistency levels.</p><h3>3. Brewer Himself Corrected the Record</h3><p>In 2012, twelve years after his original conjecture, Brewer published a retrospective article in <em>IEEE Computer</em> titled "CAP Twelve Years Later: How the 'Rules' Have Changed." In it, he explicitly stated:</p><ul><li><p>The "2 of 3" framing is misleading because it oversimplifies the tension among the three properties.</p></li><li><p>Partitions are rare, so there is little reason to forfeit C or A when the system is not partitioned.</p></li><li><p>The choice between C and A can occur many times within the same system at very fine granularity.</p></li><li><p>System designers should focus on <strong>partition management and recovery techniques</strong> rather than accepting permanent trade-offs.</p></li></ul><h3>4. CA Systems Don't Exist in Distributed Contexts</h3><p>The "CA" region of the Venn diagram (consistent + available, not partition tolerant) is essentially a single-node system or a system that gives up when a partition occurs. A traditional single-server PostgreSQL instance is "CA" &#8212; but the moment you distribute it, you must handle partitions, pushing you into CP or AP territory.</p><div><hr></div><h2>Deep Dive: Google Cloud Firestore (CP)</h2><h3>What Firestore Is</h3><p>Firestore is Google's fully managed, serverless NoSQL document database. It evolved from the Google Cloud Datastore and is now the primary database for Firebase. It stores data as documents organized into collections, with support for subcollections, rich querying, and real-time listeners.</p><h3>Architecture</h3><h4>Single-Region Deployment</h4><p>In single-region mode, Firestore distributes your data across multiple <strong>zones</strong> within a single Google Cloud region. Each data "split" (a partition of your dataset) is replicated across zones.</p><p>The replication is managed by the <strong>Paxos consensus algorithm</strong> &#8212; the same algorithm that underpins many of Google's internal systems (Chubby, Spanner, Megastore). Paxos ensures that a majority of replicas must agree before a write is committed.</p><p>Each split has its own Paxos leader, and leaders can be in different zones. This means there's no single point of failure at the zone level.</p><p><strong>SLA</strong>: 99.99% availability.</p><h4>Multi-Region Deployment</h4><p>Multi-region Firestore distributes data across three locations:</p><ol><li><p><strong>Region A</strong> &#8212; Full read-write replica of the entire database.</p></li><li><p><strong>Region B</strong> &#8212; Full read-write replica of the entire database.</p></li><li><p><strong>Region C</strong> &#8212; A <strong>witness replica</strong> that participates in Paxos voting but doesn't store a full copy of the data.</p></li></ol><p>The witness replica is what makes this clever. It breaks ties in Paxos voting without the cost of maintaining a third full replica. The system can survive the complete loss of any single region.</p><p><strong>SLA</strong>: 99.999% availability (five nines &#8212; roughly 5 minutes of downtime per year).</p><p><strong>Trade-off</strong>: Multi-region writes are significantly slower because the write must be committed across geographically distant regions before acknowledgment.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dMjE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dMjE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dMjE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Firestore Architecture (CP) &#8212; Paxos Consensus&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Firestore Architecture (CP) &#8212; Paxos Consensus" title="Firestore Architecture (CP) &#8212; Paxos Consensus" srcset="https://substackcdn.com/image/fetch/$s_!dMjE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dMjE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff65b068d-957a-48fd-99cb-37c00e32fbd5_2000x2000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Consistency Model: Strong (Serializable)</h3><p>Firestore provides <strong>serializable isolation</strong> &#8212; the strongest isolation level. This means:</p><ul><li><p>All transactions appear to execute in some serial order.</p></li><li><p>Each transaction sees a consistent snapshot of the database.</p></li><li><p>Reads always return the most recently committed value.</p></li></ul><p>This is achieved through Paxos-based synchronous replication. A write is not acknowledged to the client until a majority of replicas have durably committed it. This means:</p><ul><li><p>No stale reads.</p></li><li><p>No dirty reads.</p></li><li><p>No phantom reads.</p></li><li><p>No lost updates.</p></li></ul><p>Every transaction gets a commit timestamp, and Firestore guarantees that operations are serialized in commit-timestamp order.</p><h3>What Happens During a Partition</h3><p>When a network partition separates Firestore replicas, the system's CP nature becomes visible:</p><ol><li><p><strong>Writes may become unavailable</strong>: If the Paxos leader for a data split can't reach a majority of replicas (because they're on the other side of the partition), it cannot commit writes. The write request will timeout or return an error.</p></li></ol><ol start="2"><li><p><strong>Reads from the majority partition succeed</strong>: If you're on the side of the partition that has the Paxos majority, reads and writes continue normally.</p></li></ol><ol start="3"><li><p><strong>The minority side becomes unavailable</strong>: Nodes on the minority side of the partition cannot form a Paxos majority. They will refuse to serve reads (because they can't guarantee the data is current) and refuse to accept writes (because they can't commit them).</p></li></ol><ol start="4"><li><p><strong>No data divergence</strong>: Because the minority side refuses to operate, there's never a situation where both sides of the partition accept conflicting writes. When the partition heals, there's nothing to reconcile.</p></li></ol><p>This is the essence of a CP system: <strong>correctness over responsiveness</strong>. Firestore would rather give you an error than give you wrong data.</p><h3>Multi-Region Conflict Handling</h3><p>In multi-region deployments, if the same document is edited concurrently in different regions (a rare edge case that can occur briefly), Firestore uses <strong>last-write-wins (LWW)</strong> resolution based on commit timestamps. However, this is minimized by the Paxos protocol &#8212; true concurrent writes to the same document are serialized before they can conflict.</p><h3>Real-World Implications</h3><p><strong>When to choose Firestore's approach:</strong></p><ul><li><p>Financial applications where showing a wrong balance is worse than showing an error.</p></li><li><p>Inventory systems where overselling (due to stale reads) has real cost.</p></li><li><p>Authentication/authorization where stale permission data is a security risk.</p></li><li><p>Any system where "temporarily unavailable" is better than "silently wrong."</p></li></ul><p><strong>What you sacrifice:</strong></p><ul><li><p>Write latency in multi-region setups (cross-region Paxos rounds).</p></li><li><p>Availability during partitions (affected clients get errors).</p></li><li><p>Throughput on heavily-contested documents (serialization bottleneck).</p></li></ul><div><hr></div><h2>Deep Dive: Apache Cassandra (AP with Tunable Consistency)</h2><h3>What Cassandra Is</h3><p>Apache Cassandra is a distributed wide-column store originally developed at Facebook for inbox search, later open-sourced in 2008. It's designed for high write throughput, horizontal scalability, and continuous availability across multiple data centers. It has no single point of failure by design.</p><h3>Architecture</h3><h4>Fully Peer-to-Peer</h4><p>Unlike Firestore (which has Paxos leaders) or Redis Cluster (which has master nodes), Cassandra is <strong>fully decentralized</strong>. Every node is identical in function. There is no master, no leader, no coordinator that the system depends on. Any node can accept any request for any piece of data.</p><p>When a client sends a request, the node that receives it becomes the <strong>coordinator</strong> for that request. The coordinator determines which nodes hold replicas of the requested data (using consistent hashing) and forwards the request to them.</p><h4>The Gossip Protocol</h4><p>Nodes discover and monitor each other through an <strong>epidemic gossip protocol</strong>:</p><ul><li><p>Every <strong>1 second</strong>, each node initiates a gossip round.</p></li><li><p>It selects up to <strong>3 random nodes</strong> to exchange state information with.</p></li><li><p>The exchanged information includes: cluster membership, node liveness, load information, and schema versions.</p></li><li><p>Information propagates exponentially &#8212; in a 100-node cluster, all nodes converge on new information within seconds.</p></li></ul><p>This peer-to-peer gossip is how Cassandra avoids needing a centralized membership service. It's also how nodes detect failures: if a node hasn't been heard from in a configurable period, gossip marks it as "down."</p><h4>Data Distribution: Consistent Hashing</h4><p>Cassandra uses consistent hashing with virtual nodes (vnodes) to distribute data:</p><ol><li><p>Each piece of data has a partition key.</p></li><li><p>The partition key is hashed to a position on a "ring" (the hash space 0 to 2^63 -1).</p></li><li><p>Each node owns a range of the ring.</p></li><li><p>The data is stored on the node that owns the range containing the hash value.</p></li><li><p>Replicas are placed on subsequent nodes in the ring, governed by the <strong>replication factor (RF)</strong>.</p></li></ol><p>With RF=3, each piece of data exists on three different nodes. Which three depends on the <strong>replication strategy</strong> &#8212; <code>SimpleStrategy</code> for single-datacenter, <code>NetworkTopologyStrategy</code> for multi-datacenter (where you specify RF per datacenter).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A5gf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A5gf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 424w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 848w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A5gf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cassandra Architecture (AP) &#8212; Peer-to-Peer Ring&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cassandra Architecture (AP) &#8212; Peer-to-Peer Ring" title="Cassandra Architecture (AP) &#8212; Peer-to-Peer Ring" srcset="https://substackcdn.com/image/fetch/$s_!A5gf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 424w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 848w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!A5gf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b67dbc7-fa93-400a-a3fb-d7c6ea852e08_2000x1359.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Consistency Model: Tunable Consistency</h3><p>This is Cassandra's most powerful &#8212; and most dangerous &#8212; feature. You choose the consistency level <strong>per operation</strong>.</p><h4>Consistency Levels Explained</h4><p><strong>For writes:</strong></p><pre><code>| Level          | Nodes That Must Acknowledge        | Behavior                                                                                                |
|----------------|------------------------------------|---------------------------------------------------------------------------------------------------------|
| `ANY`          | 1 (including hinted handoff)       | Fastest. Write succeeds even if all replicas are down (hint stored on coordinator). Weakest durability. |
| `ONE`          | 1 replica                          | Fast. Minimal durability guarantee.                                                                     |
| `TWO`          | 2 replicas                         | Moderate.                                                                                               |
| `THREE`        | 3 replicas                         | Slower but more durable.                                                                                |
| `QUORUM`       | &#8970;RF/2&#8971; + 1 replicas                | The sweet spot for most use cases.                                                                      |
| `LOCAL_QUORUM` | Quorum within the local datacenter | Avoids cross-DC latency.                                                                                |
| `EACH_QUORUM`  | Quorum in each datacenter          | Strong cross-DC guarantee.                                                                              |
| `ALL`          | Every replica                      | Slowest. Any single replica failure means the write fails.                                              |</code></pre><p><strong>For reads:</strong></p><p>The same levels apply. <code>ONE</code> means only one replica needs to respond. <code>QUORUM</code> means a majority must respond and the most recent value (by timestamp) is returned.</p><h4>The Strong Consistency Formula</h4><p>Cassandra can provide <strong>strong consistency</strong> (equivalent to linearizability for single-key operations) if:</p><pre><code>R + W &gt; RF</code></pre><p>Where:</p><ul><li><p><strong>R</strong> = read consistency level (number of nodes that must respond to a read)</p></li><li><p><strong>W</strong> = write consistency level (number of nodes that must acknowledge a write)</p></li><li><p><strong>RF</strong> = replication factor (total number of replicas)</p></li></ul><p><strong>Example with RF=3:</strong></p><ul><li><p><code>QUORUM</code> reads (R=2) + <code>QUORUM</code> writes (W=2): 2 + 2 = 4 &gt; 3. <strong>Strong consistency.</strong></p></li><li><p><code>ONE</code> reads (R=1) + <code>ALL</code> writes (W=3): 1 + 3 = 4 &gt; 3. <strong>Strong consistency</strong> (fast reads, slow writes).</p></li><li><p><code>ALL</code> reads (R=3) + <code>ONE</code> writes (W=1): 3 + 1 = 4 &gt; 3. <strong>Strong consistency</strong> (slow reads, fast writes).</p></li><li><p><code>ONE</code> reads (R=1) + <code>ONE</code> writes (W=1): 1 + 1 = 2 &lt; 3. <strong>Eventually consistent.</strong> You might read stale data.</p></li></ul><p><strong>Why this formula works</strong>: If W nodes acknowledged a write and R nodes are queried for a read, and R+W &gt; RF, then at least one node must be in both the write set and the read set. That node has the latest value. Cassandra picks the most recent value (by timestamp) from the responding nodes, so the latest write is always returned.</p><h4>The Per-Query Trade-off in Action</h4><p>This is where Cassandra's design shines for real applications. Consider an e-commerce platform:</p><pre><code>// Writing a new order &#8212; use QUORUM for durability
INSERT INTO orders (...) VALUES (...) USING CONSISTENCY QUORUM;

// Reading order status for the customer who just placed it &#8212; use QUORUM for freshness
SELECT * FROM orders WHERE order_id = ? USING CONSISTENCY QUORUM;

// Reading product recommendations &#8212; use ONE for speed (stale data is fine)
SELECT * FROM recommendations WHERE user_id = ? USING CONSISTENCY ONE;

// Writing analytics events &#8212; use ANY for maximum throughput
INSERT INTO analytics (...) VALUES (...) USING CONSISTENCY ANY;</code></pre><p>Same cluster, same data model, four different consistency trade-offs based on business requirements.</p><h3>The Three Repair Mechanisms</h3><p>Because Cassandra allows writes to proceed without all replicas acknowledging them, replicas can (and do) drift out of sync. Three mechanisms bring them back:</p><h4>1. Hinted Handoff (Write-Path Repair)</h4><p>When a write is destined for a replica that's currently unreachable:</p><ol><li><p>The coordinator stores the write as a "hint" in its own local storage.</p></li><li><p>The hint contains the full mutation data and the target node's identity.</p></li><li><p>When the coordinator detects (via gossip) that the target node is back, it replays the hint.</p></li></ol><p><strong>Limitations:</strong></p><ul><li><p>Hints are stored for a maximum of 3 hours by default (<code>max_hint_window_in_ms</code>).</p></li><li><p>If the target node is down for longer than that, hints are discarded and the data gap remains.</p></li><li><p>Hints consume disk space on the coordinator node.</p></li><li><p>Hinted handoff is "best effort" &#8212; it doesn't guarantee repair.</p></li></ul><h4>2. Read Repair (Read-Path Repair)</h4><p>When a read query contacts multiple replicas and they return different versions of the data:</p><ol><li><p>Cassandra compares timestamps across the responding replicas.</p></li><li><p>It returns the newest version to the client.</p></li><li><p><strong>In the background</strong>, it sends the newest version to any replicas that had stale data.</p></li></ol><p>This means every read is also a potential repair operation. However, it only repairs data that's actually being read &#8212; cold data that nobody queries stays inconsistent.</p><h4>3. Anti-Entropy Repair (Background Full Repair)</h4><p>This is the heavyweight mechanism. Run manually via <code>nodetool repair</code>:</p><ol><li><p>Nodes build <strong>Merkle trees</strong> &#8212; hash trees where each leaf represents a range of data.</p></li><li><p>Nodes exchange and compare Merkle trees.</p></li><li><p>Differences are identified and the most recent version is propagated.</p></li></ol><p>This is the <strong>only</strong> mechanism that guarantees all data across all replicas eventually converges. It's computationally expensive and should be run periodically (Cassandra documentation recommends at least once within the <code>gc_grace_seconds</code> window &#8212; default 10 days &#8212; to prevent zombie data resurrection from deleted records).</p><h3>What Happens During a Partition</h3><p>This is where Cassandra's AP nature is visible:</p><ol><li><p><strong>Both sides of the partition continue operating.</strong> Nodes accept reads and writes regardless of whether they can communicate with nodes on the other side.</p></li></ol><ol start="2"><li><p><strong>Data diverges.</strong> If the same row is written on both sides of the partition, both writes succeed (no coordination is possible). This creates conflicting versions.</p></li></ol><ol start="3"><li><p><strong>Conflict resolution: Last Write Wins (LWW).</strong> When the partition heals and replicas sync up, conflicts are resolved by timestamp. The write with the latest timestamp wins. The "losing" write is silently discarded.</p></li></ol><ol start="4"><li><p><strong>Clock synchronization matters.</strong> Because LWW depends on timestamps, Cassandra assumes NTP (Network Time Protocol) is running on all nodes. Clock skew can cause the "wrong" write to win. In practice, this means running NTP is not optional &#8212; it's a critical infrastructure requirement.</p></li></ol><ol start="5"><li><p><strong>Lightweight Transactions (LWT) for CP behavior.</strong> For operations that absolutely require linearizability (like a unique username check), Cassandra offers LWT using the Paxos protocol. But LWT is 4x slower than regular writes due to the Paxos round trips and should be used sparingly.</p></li></ol><h3>Real-World Implications</h3><p><strong>When to choose Cassandra's approach:</strong></p><ul><li><p>High write throughput requirements (IoT sensor data, event logging, time-series data).</p></li><li><p>Multi-datacenter or multi-region deployments where you need writes to succeed everywhere.</p></li><li><p>Use cases where "always writable" is more important than "always correct."</p></li><li><p>Systems with clear consistency requirements that vary by operation.</p></li></ul><p><strong>What you sacrifice:</strong></p><ul><li><p>No strong consistency by default (you must configure it deliberately).</p></li><li><p>LWW conflict resolution can silently lose data during partitions.</p></li><li><p>Operational complexity &#8212; you must run anti-entropy repairs, manage tombstones, tune consistency levels.</p></li><li><p>Lightweight Transactions are expensive and limited.</p></li></ul><div><hr></div><h2>Deep Dive: Redis Cluster (CP-Biased, but Leaky)</h2><h3>What Redis Cluster Is</h3><p>Redis Cluster is Redis's built-in horizontal scaling solution, available since Redis 3.0 (2015). It automatically shards data across multiple Redis master nodes, with each master having one or more replica nodes for failover.</p><p>Unlike Firestore (managed service) or Cassandra (peer-to-peer), Redis Cluster follows a <strong>master-replica architecture with gossip-based failure detection</strong>.</p><h3>Architecture</h3><h4>Sharding with Hash Slots</h4><p>Redis Cluster divides the keyspace into <strong>16,384 hash slots</strong>. Each master node owns a subset of these slots.</p><p>When a client issues a command:</p><ol><li><p>The key is hashed using CRC16.</p></li><li><p>The hash is taken modulo 16,384 to determine the slot.</p></li><li><p>The command is routed to the master that owns that slot.</p></li><li><p>If the client connects to the wrong node, it receives a <code>MOVED</code> redirect to the correct one.</p></li></ol><h4>Replication Model: Asynchronous</h4><p>This is the single most important architectural detail of Redis Cluster, and it's the source of both its speed and its consistency weakness.</p><p>When a write arrives at a master:</p><ol><li><p>Master executes the write in memory.</p></li><li><p>Master sends acknowledgment to the client.</p></li><li><p>Master <strong>asynchronously</strong> replicates the write to its replicas.</p></li></ol><p>Step 2 happens <strong>before</strong> step 3. The client is told "write succeeded" before any replica has seen the data. This makes writes extremely fast but creates a window of vulnerability.</p><h4>Gossip Protocol</h4><p>Similar to Cassandra, Redis Cluster nodes communicate via gossip:</p><ul><li><p>Nodes periodically ping random other nodes.</p></li><li><p>They exchange cluster state: which nodes are alive, which slots are assigned where, which nodes are failing.</p></li><li><p>The gossip port is the data port + 10000 (e.g., if data port is 6379, gossip port is 16379).</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3JRt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3JRt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3JRt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Redis Cluster (CP-Biased, Leaky) &#8212; Async Replication&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Redis Cluster (CP-Biased, Leaky) &#8212; Async Replication" title="Redis Cluster (CP-Biased, Leaky) &#8212; Async Replication" srcset="https://substackcdn.com/image/fetch/$s_!3JRt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3JRt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9a31828-54f5-4b10-8931-ac502f835ebc_2000x1259.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>What Happens During a Partition</h3><h4>Phase 1: Detection</h4><p>When a master node can't reach another node, it waits for <code>cluster-node-timeout</code> (commonly configured at 15,000ms / 15 seconds) before marking it as potentially failed (<code>PFAIL</code>).</p><p>When enough nodes agree that a node is unreachable (via gossip), it's marked as <code>FAIL</code>.</p><h4>Phase 2: Majority vs. Minority</h4><p>The cluster determines which side of the partition has the majority of master nodes:</p><ul><li><p><strong>Majority partition</strong>: Continues operating. Clients connected to this side experience no disruption (for slots owned by masters in this partition).</p></li><li><p><strong>Minority partition</strong>: Stops accepting writes. Masters on this side detect that they can't reach a majority and begin rejecting write operations after <code>cluster-node-timeout</code>.</p></li></ul><p>This is the CP behavior: the minority side sacrifices availability to prevent split-brain.</p><h4>Phase 3: Failover (If a Master Is Lost)</h4><p>If a master is on the minority side (or completely down), the majority side initiates failover:</p><ol><li><p>Replicas of the lost master detect the failure.</p></li><li><p>A <strong>replica election</strong> begins. Replicas request votes from remaining masters.</p></li><li><p>A replica needs votes from a majority of masters to win the election.</p></li><li><p>The winning replica promotes itself to master and takes ownership of the lost master's hash slots.</p></li><li><p>The cluster announces the new topology via gossip.</p></li></ol><p><strong>Timing</strong>: Failover typically completes within <code>cluster-node-timeout</code> + 1-2 seconds for the election.</p><h3>The Data Loss Problem</h3><p>Because of asynchronous replication, Redis Cluster has a well-documented data loss window. Here's the exact scenario:</p><pre><code>Time T0: Client sends WRITE to Master A
Time T1: Master A executes WRITE, stores in memory
Time T2: Master A sends OK to Client  &#8592; Client thinks write is safe
Time T3: Master A begins replicating to Replica A1
Time T4: Master A crashes before replication completes  &#8592; Write is lost
Time T5: Replica A1 is promoted to master (WITHOUT the write from T1)</code></pre><p>The write acknowledged at T2 is <strong>permanently lost</strong>. The client received confirmation, but the data never made it to any surviving node.</p><h4>How Large Is This Window?</h4><p>The window is typically tiny &#8212; milliseconds between T2 and T3. But under heavy load or network congestion, it can grow. And when it bites, it bites hard: the client has no way to know the write was lost.</p><h3>Mitigation Strategies</h3><h4>1. The <code>WAIT</code> Command</h4><pre><code>SET user:123:balance 500
WAIT 1 5000</code></pre><p><code>WAIT numreplicas timeout</code> blocks the client until the specified number of replicas have acknowledged the write, or the timeout (in milliseconds) expires.</p><p><strong>Caveat</strong>: WAIT reduces the data loss window but doesn't eliminate it entirely. Even if WAIT succeeds, the replica could crash before the next persistence cycle. WAIT also doesn't make the write transactional &#8212; it's an acknowledgment that the replica received the data, not that it's durably persisted.</p><h4>2. <code>min-replicas-to-write</code> Configuration</h4><pre><code>min-replicas-to-write 1
min-replicas-max-lag 10</code></pre><p>This tells the master to refuse writes if fewer than <code>min-replicas-to-write</code> replicas are connected with a replication lag of at most <code>min-replicas-max-lag</code> seconds.</p><p>This prevents the master from accepting writes when it's isolated (no replicas reachable), but it's a blunt instrument &#8212; it makes the master unavailable when replicas are unreachable, shifting the system further toward CP behavior.</p><h4>3. <code>CLUSTER FAILOVER</code> (Manual Failover)</h4><p>For planned maintenance, the <code>CLUSTER FAILOVER</code> command performs a <strong>safe</strong> failover:</p><ol><li><p>The replica tells the old master to stop accepting writes.</p></li><li><p>The replica waits until it has processed all data from the master's replication stream.</p></li><li><p>Only then does the replica promote itself.</p></li></ol><p>This guarantees zero data loss but only works for planned operations, not crashes.</p><h3>The Split-Brain Scenario</h3><p>Despite Redis Cluster's CP bias, a split-brain can occur in edge cases:</p><ol><li><p>Network partitions a cluster into two halves of equal size (even number of masters).</p></li><li><p>Neither side has a clear majority.</p></li><li><p>If <code>cluster-require-full-coverage</code> is set to <code>no</code>, both sides might continue serving reads for their local slots.</p></li></ol><p><strong>Mitigation</strong>: Always maintain an <strong>odd number</strong> of master nodes. This ensures one side always has a clear majority.</p><h3>Real-World Implications</h3><p><strong>When to choose Redis Cluster's approach:</strong></p><ul><li><p>Caching layers where speed is paramount and occasional data loss is tolerable.</p></li><li><p>Session stores where sessions can be regenerated.</p></li><li><p>Rate limiters and counters where approximate counts are acceptable.</p></li><li><p>Real-time leaderboards where losing a few score updates during a failure is acceptable.</p></li></ul><p><strong>What you sacrifice:</strong></p><ul><li><p>Guaranteed durability for every write (async replication gap).</p></li><li><p>Perfect consistency during failover windows.</p></li><li><p>Simplicity &#8212; managing a Redis Cluster requires understanding hash slots, failover mechanics, and split-brain prevention.</p></li></ul><p><strong>What you should NOT use Redis Cluster for:</strong></p><ul><li><p>Sole source of truth for financial transactions.</p></li><li><p>Primary database for data that can't be regenerated.</p></li><li><p>Systems where every single write must survive any failure.</p></li></ul><div><hr></div><h2>Side-by-Side Comparison</h2><pre><code>| Dimension                      | Firestore                                | Cassandra                                 | Redis Cluster                                 |
|--------------------------------|------------------------------------------|-------------------------------------------|-----------------------------------------------|
| **CAP Bias**                   | CP                                       | AP (tunable toward CP)                    | CP (with caveats)                             |
| **Consistency Model**          | Strong (Serializable)                    | Eventual (Tunable per-query)              | Strong for single-key (async replication gap) |
| **Replication Protocol**       | Paxos (synchronous)                      | Gossip + async replication                | Gossip + async replication                    |
| **Replication Type**           | Synchronous                              | Asynchronous                              | Asynchronous                                  |
| **During Partition**           | Minority side becomes unavailable        | Both sides keep serving                   | Minority side stops accepting writes          |
| **Data Loss During Partition** | None                                     | Possible (LWW conflict resolution)        | Possible (unacknowledged writes)              |
| **Conflict Resolution**        | No conflicts (Paxos prevents them)       | Last-Write-Wins (LWW) by timestamp        | Last replica promoted wins                    |
| **Topology**                   | Managed (Paxos leaders per split)        | Peer-to-peer (no masters)                 | Master-replica per shard                      |
| **Write Latency (Normal)**     | Low (single-region), High (multi-region) | Very low at ONE, Moderate at QUORUM       | Very low                                      |
| **Consistency Control**        | Fixed (always strong)                    | Per-query (ONE to ALL)                    | Fixed (async + WAIT for tuning)               |
| **Failure Detection**          | Managed by Google                        | Gossip protocol (1s intervals)            | Gossip + cluster-node-timeout                 |
| **Repair Mechanism**           | Paxos consensus (automatic)              | Hinted handoff, read repair, anti-entropy | Replica promotion + full resync               |
| **Operational Overhead**       | None (fully managed)                     | High (repairs, tuning, tombstones)        | Medium (slot management, failover)            |</code></pre><div><hr></div><h2>The PACELC Extension &#8212; What CAP Doesn't Tell You</h2><p>CAP tells you what happens during partitions. It says nothing about normal operation. But systems make trade-offs <em>all the time</em>, not just during failures.</p><p>In 2010, Daniel Abadi introduced the <strong>PACELC theorem</strong> in a blog post (later formally published in a 2012 paper) to fill this gap:</p><blockquote><p><strong>If there is a Partition (P), how does the system trade off between Availability and Consistency (A and C)?</strong> <strong>Else (E), in normal operation, how does the system trade off between Latency and Consistency (L and C)?</strong></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jq8r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jq8r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jq8r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;PACELC Extension &#8212; What CAP Doesn't Tell You&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="PACELC Extension &#8212; What CAP Doesn't Tell You" title="PACELC Extension &#8212; What CAP Doesn't Tell You" srcset="https://substackcdn.com/image/fetch/$s_!jq8r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jq8r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3f19ab8-bb61-4946-b841-a2f6821bda1d_2000x1150.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This gives us a much richer classification:</p><pre><code>| System                 | If Partition (PAC)                                  | Else (ELC)                                                               | Full Classification            |
|------------------------|-----------------------------------------------------|--------------------------------------------------------------------------|--------------------------------|
| **Firestore**          | PC (chooses Consistency)                            | EC (chooses Consistency, accepts higher latency for multi-region writes) | PC/EC                          |
| **Cassandra (QUORUM)** | PA (chooses Availability at ONE, PC-like at QUORUM) | EL (chooses low Latency at ONE) or EC (chooses Consistency at QUORUM)    | PA/EL or PC/EC depending on CL |
| **Redis Cluster**      | PC (minority unavailable)                           | EL (chooses low Latency via async replication)                           | PC/EL                          |</code></pre><h3>Why PACELC Matters</h3><p>Redis Cluster and Firestore are both "CP" under CAP. But they behave very differently in normal operation:</p><ul><li><p><strong>Firestore (PC/EC)</strong>: Even when everything is fine, it pays a latency cost for consistency (multi-region Paxos rounds).</p></li><li><p><strong>Redis Cluster (PC/EL)</strong>: When everything is fine, it chooses speed (async replication), accepting a small durability risk.</p></li></ul><p>CAP can't distinguish these two very different design philosophies. PACELC can.</p><div><hr></div><h2>Choosing the Right System for Your Use Case</h2><h3>Decision Framework</h3><p><strong>Choose a CP system (like Firestore) when:</strong></p><ul><li><p>Incorrect data is worse than unavailable data.</p></li><li><p>You're dealing with financial transactions, inventory counts, or access control.</p></li><li><p>Regulatory requirements mandate strong consistency.</p></li><li><p>Your users would rather see an error page than wrong information.</p></li></ul><p><strong>Choose an AP system (like Cassandra) when:</strong></p><ul><li><p>Unavailability is worse than stale data.</p></li><li><p>You need writes to succeed across multiple regions without cross-region latency.</p></li><li><p>Your data has natural conflict resolution (timestamps, counters, CRDTs).</p></li><li><p>Your system can tolerate and recover from temporary inconsistency.</p></li></ul><p><strong>Choose a CP-biased-but-fast system (like Redis Cluster) when:</strong></p><ul><li><p>You need sub-millisecond latency.</p></li><li><p>The data is either regenerable or not the primary source of truth.</p></li><li><p>You can tolerate rare data loss during failures.</p></li><li><p>Your use case is caching, sessions, rate limiting, or real-time counters.</p></li></ul><h3>Common Architectural Pattern: Use Multiple Systems</h3><p>Most production systems don't rely on a single database. A common pattern:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i3oU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i3oU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 424w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 848w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i3oU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Real-World Architecture &#8212; Use Multiple Systems&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Real-World Architecture &#8212; Use Multiple Systems" title="Real-World Architecture &#8212; Use Multiple Systems" srcset="https://substackcdn.com/image/fetch/$s_!i3oU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 424w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 848w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!i3oU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0827a19-aefd-4096-b7c7-c85abab27a05_2000x1216.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><pre><code>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;                  Application Layer               &#9474;
&#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
&#9474;  Firestore                  &#9474;    Cassandra       &#9474;    Redis Cluster    &#9474;
&#9474;  (Source of Truth)   &#9474;    (Analytics/Logs) &#9474;    (Cache/Sessions) &#9474;
&#9474;  - User profiles     &#9474;    - Event streams  &#9474;    - API responses   &#9474;
&#9474;  - Orders            &#9474;    - Time-series    &#9474;    - Rate limits     &#9474;
&#9474;  - Account balances  &#9474;    - IoT sensor data&#9474;    - Leaderboards    &#9474;
&#9474;  Strong Consistency  &#9474;    High Write Volume&#9474;    Sub-ms Latency    &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</code></pre><p>Each database handles the data it's best suited for. The CAP trade-off isn't a constraint &#8212; it's a design tool.</p><div><hr></div><h2>Key Takeaways</h2><ol><li><p><strong>The CAP theorem isn't "pick 2 of 3."</strong> It's "when a partition happens (and it will), do you sacrifice consistency or availability?" During normal operation, you can have all three.</p></li></ol><ol start="2"><li><p><strong>Partition tolerance is mandatory.</strong> In any distributed system, network partitions are inevitable. The real engineering question is how you handle them.</p></li></ol><ol start="3"><li><p><strong>Firestore chooses consistency at all costs.</strong> It uses Paxos for synchronous replication, guaranteeing that every read returns the latest write. During partitions, it sacrifices availability &#8212; the minority side goes offline rather than serving stale data.</p></li></ol><ol start="4"><li><p><strong>Cassandra chooses availability by default, but lets you tune it.</strong> Its per-query consistency levels let you balance speed vs. correctness for each operation. The formula R + W &gt; RF gives you strong consistency when you need it, and maximum throughput when you don't.</p></li></ol><ol start="5"><li><p><strong>Redis Cluster is CP in theory but has a consistency gap in practice.</strong> Asynchronous replication creates a window where acknowledged writes can be lost. It's blazing fast but not suitable as a sole source of truth for critical data.</p></li></ol><ol start="6"><li><p><strong>PACELC is the better framework.</strong> It captures what systems do during <em>normal</em> operation (latency vs. consistency trade-off), not just during partitions.</p></li></ol><ol start="7"><li><p><strong>Real architectures use multiple systems.</strong> Match each data type to the database whose trade-offs align with that data's requirements.</p></li></ol><div><hr></div><h2>References</h2><ul><li><p>Brewer, Eric. "CAP Twelve Years Later: How the 'Rules' Have Changed." IEEE Computer, 2012.</p></li><li><p>Gilbert, Seth &amp; Lynch, Nancy. "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services." ACM SIGACT News, 2002.</p></li><li><p>Abadi, Daniel. "Consistency Tradeoffs in Modern Distributed Database System Design." IEEE Computer, 2012.</p></li><li><p>Google Cloud. "Understand Reads and Writes at Scale &#8212; Firestore." Firebase Documentation.</p></li><li><p>Apache Software Foundation. "Apache Cassandra Documentation &#8212; Configuring Data Consistency."</p></li><li><p>Redis Ltd. "Redis Cluster Specification." Redis Documentation.</p></li><li><p>Kleppmann, Martin. <em>Designing Data-Intensive Applications.</em> O'Reilly Media, 2017.</p></li></ul><div><hr></div><p><em>If you found this useful, connect with me &#8212; I'm always happy to talk distributed systems.</em></p>]]></content:encoded></item><item><title><![CDATA[How I Use Claude Code to Ship Production Features — From PRD to Merged PR]]></title><description><![CDATA[The workflow that turned AI-assisted development from a novelty into the way I actually build software every day. Research, tech specs, TDD &#8212; all powered by Claude Code.]]></description><link>https://curiousrhythms.substack.com/p/how-i-use-claude-code-to-ship-production</link><guid isPermaLink="false">https://curiousrhythms.substack.com/p/how-i-use-claude-code-to-ship-production</guid><dc:creator><![CDATA[Mrinal]]></dc:creator><pubDate>Sun, 18 Jan 2026 18:30:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7CsN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>---</p><h2>Table of Contents</h2><p>1. <a>The Problem with "Vibe Coding"</a></p><p>2. <a>The Workflow &#8212; End to End</a></p><p>3. <a>Phase 1: Research &#8212; Understanding Before Building</a></p><p>4. <a>Phase 2: Tech Spec &#8212; Claude as a Thinking Partner</a></p><p>5. <a>Phase 3: Planning &#8212; Test-Driven Development with Intent</a></p><p>6. <a>Phase 4: Implementation &#8212; Tests First, Code Second</a></p><p>7. <a>The CLAUDE.md File &#8212; Your Project's Long-Term Memory</a></p><p>8. <a>What Changed in How I Think About Development</a></p><p>9. <a>The Honest Downsides</a></p><p>10. <a>Key Takeaways</a></p><p>---</p><h2>The Problem with "Vibe Coding"</h2><p>There's a term floating around the AI-assisted development world &#8212; "vibe coding." Open a terminal, start prompting, let the AI write code, accept what looks right, iterate until it works. Ship it.</p><p>I tried this. For about two weeks. Here's what happened:</p><p>1. Claude would generate plausible-looking code that compiled and passed a few basic cases.</p><p>2. I'd ship it. Tests were sparse because I hadn't thought deeply about edge cases &#8212; I was "vibing."</p><p>3. Two days later, a bug would surface in production. The kind of bug that only shows up when a service handles a race condition between two concurrent gRPC calls.</p><p>4. I'd go back and realize the AI-generated code had made an assumption about the codebase that wasn't true. It didn't know that our order service processes refunds asynchronously, or that our inventory service has eventual consistency guarantees.</p><p>The problem wasn't Claude. The problem was me. I was using a powerful tool without a process.</p><p>Vibe coding is to AI-assisted development what copy-pasting from Stack Overflow was to traditional development. It works for toy problems. It falls apart the moment you're dealing with a production codebase with 40+ microservices, shared libraries, and a deployment pipeline that doesn't forgive half-baked changes.</p><p>So I built a process. And it fundamentally changed how I think about writing software.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7CsN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7CsN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7CsN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7CsN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7CsN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2463cdb7-b7f4-4958-86ac-31294f8c980c_2000x1250.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>---</p><h2>The Workflow &#8212; End to End</h2><p>Here's the full pipeline, from the moment a product requirement lands on my desk to the moment I open a pull request:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p3kN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p3kN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 424w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 848w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p3kN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p3kN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 424w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 848w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!p3kN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdba062e6-b1d5-4293-9ffd-9c54c6ec63e3_2000x1550.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each phase has a clear input, a clear output, and a clear handoff. Let me walk through each one.</p><p>---</p><h2>Phase 1: Research &#8212; Understanding Before Building</h2><p>A PRD lands from the product team. Let's say the requirement is: "Allow merchants to set custom refund policies per product category."</p><p>Before I write a single line of code &#8212; before I even think about a solution &#8212; I need to understand what exists. In a codebase with dozens of Go and Python microservices, this isn't trivial. The refund logic might touch the order service, the payment gateway adapter, the notification service, and a shared protobuf definition that six other teams consume.</p><p>This is where `hf-research` comes in.</p><h3>What hf-research Does</h3><p>`hf-research` is a custom slash command built on top of Claude Code (using the <a>HumanLayer</a> framework). When I invoke it, it spawns parallel sub-agents that fan out across the codebase &#8212; searching for relevant files, tracing function calls, reading configuration, and mapping dependencies.</p><p>I'll run it like this:</p><pre><code>/hf-research How does the current refund flow work end-to-end?
Which services are involved? Where are refund policies defined?</code></pre><p>Within minutes, I get back a structured research document that tells me:</p><p><strong>Which files handle refund logic</strong> &#8212; with exact file paths and line numbers.</p><p><strong>Which services communicate during a refund</strong> &#8212; the gRPC call chain, the event bus messages, the database writes.</p><p><strong>Where refund policies are currently hardcoded or configured</strong> &#8212; maybe it's a constant in the order service, maybe it's a database table, maybe it's a feature flag.</p><p><strong>Historical context</strong> &#8212; if there are design documents, past research notes, or architectural decisions recorded in the codebase.</p><h3>Why This Matters</h3><p>Without this step, I'd spend 2-3 hours manually grep-ing through the codebase, opening files, tracing imports, reading old PRs. With `hf-research`, that becomes 5-10 minutes of automated exploration followed by 15 minutes of me reading and internalizing the results.</p><p>But the real value isn't speed &#8212; it's <strong>completeness</strong>. When I manually explore a codebase, I follow my intuition. I check the places I <em>think</em> are relevant. I miss the edge case where the notification service also has refund-related logic because it sends different emails based on refund type. The sub-agents don't have this bias. They search everything.</p><h3>Brainstorming with Claude</h3><p>Once the research document is ready, I don't just read it and move on. I use it as the foundation for a conversation with Claude:</p><pre><code>Based on this research, here's what I'm thinking:
- The current refund flow is tightly coupled to the order service
- Refund policies are hardcoded as constants
- We need to make them configurable per product category

What are the different ways we could architect this?
What are the trade-offs?</code></pre><p>Claude has now read the entire relevant codebase. It's not hallucinating about a generic refund system &#8212; it's reasoning about <em>our</em> refund system, with <em>our</em> service boundaries, <em>our</em> data models, <em>our</em> deployment constraints. The brainstorming is grounded.</p><p>This conversation typically produces 2-3 viable approaches. I evaluate them, push back, ask follow-up questions. By the end, I have a clear mental model of the solution space.</p><p>---</p><h2>Phase 2: Tech Spec &#8212; Claude as a Thinking Partner</h2><p>With the research done and approaches brainstormed, I write the tech spec. This isn't optional. This isn't a formality. This is the most important document in the entire process.</p><h3>What the Tech Spec Contains</h3><p>Every tech spec I write follows the same structure:</p><p><strong>1. Current Problem</strong></p><p>What's broken, limited, or missing? Why can't the current system handle the new requirement? This section forces me to articulate the gap clearly.</p><p><strong>2. What We're Solving</strong></p><p>Scope the solution. What's in scope? What's explicitly <em>not</em> in scope? This prevents scope creep during implementation.</p><p><strong>3. Why the Current Implementation Won't Scale</strong></p><p>This is where the research pays off. Because I've already mapped the current system, I can explain precisely <em>why</em> hardcoded refund policies won't work &#8212; maybe the product catalog is growing from 12 categories to 200, or maybe a new regulatory requirement demands per-category refund windows.</p><p><strong>4. Proposed Solution</strong></p><p>The recommended approach, explained in detail. Data model changes, API contracts, service interactions, migration strategy.</p><p><strong>5. Alternative Approaches</strong></p><p>At least two other approaches I considered, with honest pros and cons for each. This shows the reviewer that I didn't just pick the first idea &#8212; I evaluated the solution space.</p><p><strong>6. C3/C4 Diagrams</strong></p><p>Architecture diagrams at the appropriate level. C3 (component) diagrams for how services interact. C4 (code) diagrams for complex internal flows.</p><h3>How Claude Helps Write the Tech Spec</h3><p>Claude doesn't write the tech spec <em>for</em> me. It writes it <em>with</em> me. The distinction matters.</p><p>I'll draft the "Current Problem" section, then ask Claude to review it: "Does this accurately describe the current refund flow based on what we found in the research?" Claude catches things I miss &#8212; maybe I forgot that the payment gateway has a 30-day refund window constraint, or that the event schema includes fields we'd need to update.</p><p>For the "Alternative Approaches" section, Claude is especially valuable. It generates approaches I wouldn't have considered &#8212; maybe a policy engine pattern, or an event-sourced approach, or leveraging an existing configuration service. I evaluate each one against our constraints (team size, timeline, operational complexity) and make the call.</p><p>For diagrams, I describe what I want and Claude generates the Mermaid or PlantUML markup. I render it, review it, iterate.</p><h3>Leadership Review</h3><p>The tech spec goes through a review process. Senior engineers and engineering leadership read it, ask questions, push back on architectural decisions, and either approve or request changes.</p><p>This is a human-only step. Claude doesn't attend the review meeting. But because Claude helped me think through the problem comprehensively, I walk into the review with answers to questions I might not have anticipated otherwise.</p><p>Once approved, we move to planning.</p><p>---</p><h2>Phase 3: Planning &#8212; Test-Driven Development with Intent</h2><p>This is where the process diverges from how most people use AI coding tools.</p><p>Most developers using AI assistants go straight from "I understand the problem" to "generate the code." I don't. I plan the test-driven development process first.</p><h3>What hf-plan Does</h3><p>`hf-plan` takes the approved tech spec and produces a detailed implementation plan. But it's not just a task list. It's a <strong>TDD-oriented</strong> plan &#8212; it tells you what tests to write and in what order, before describing any implementation code.</p><p>The output looks something like this:</p><pre><code>## Implementation Plan

### Step 1: Define test cases for RefundPolicyService
- Test: CreatePolicy with valid category returns policy ID
- Test: CreatePolicy with duplicate category returns conflict error
- Test: GetPolicy for existing category returns correct policy
- Test: GetPolicy for nonexistent category returns default policy
- Test: UpdatePolicy validates refund window bounds (1-90 days)
- Test: DeletePolicy with active orders returns rejection

### Step 2: Implement RefundPolicyService to pass all tests

### Step 3: Define test cases for order service refund flow changes
- Test: ProcessRefund uses category-specific policy when available
- Test: ProcessRefund falls back to default when no category policy
- Test: ProcessRefund rejects if outside category refund window
...</code></pre><h3>Why Tests First in the Plan</h3><p>The plan doesn't say "implement RefundPolicyService" and then "add tests." It says "define test cases for RefundPolicyService" and then "implement RefundPolicyService to pass all tests."</p><p>This ordering is deliberate. When you think about tests first, you're thinking about <strong>behavior</strong>. What should this service <em>do</em>? What are the edge cases? What should it reject? You're defining the contract before you write the implementation.</p><p>When you think about implementation first, you're thinking about <strong>structure</strong>. How should I organize this code? What patterns should I use? These are important questions, but they should come <em>after</em> you know what the code needs to do.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M1qP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M1qP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 424w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 848w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M1qP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;captionedImage&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M1qP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 424w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 848w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!M1qP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94f71a9c-d44a-44cc-829d-b011c06b7d29_2000x1125.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>---</p><h2>Phase 4: Implementation &#8212; Tests First, Code Second</h2><p>With the plan approved, `hf-implement` takes over. And this is where TDD actually happens.</p><h3>How hf-implement Works</h3><p>`hf-implement` reads the plan and executes it step by step. For each step:</p><p>1. <strong>Write the tests first.</strong> Claude generates test files based on the test cases defined in the plan. These tests compile but fail &#8212; the implementation doesn't exist yet.</p><p>2. <strong>Write the implementation.</strong> Claude writes the production code to make the tests pass. It runs the tests after each significant change to verify progress.</p><p>3. <strong>Iterate until green.</strong> If a test fails, Claude reads the error, adjusts the implementation, and re-runs. This cycle continues until all tests pass.</p><h3>Why This Works Better Than "Generate Everything"</h3><p>When Claude writes tests and implementation simultaneously (the "vibe coding" approach), the tests tend to be shaped around the implementation. They test what the code <em>does</em>, not what it <em>should do</em>. You get 100% pass rate and zero confidence.</p><p>When tests are written first from the plan, they're shaped around the <em>requirements</em>. The implementation has to conform to the tests, not the other way around. If Claude's implementation has a bug, the pre-written tests catch it. If Claude's implementation makes an incorrect assumption about the codebase, the tests (which were designed with the research and tech spec in mind) catch it.</p><p>I've seen this prevent real bugs. Claude once generated an implementation that used an in-memory cache for refund policy lookups &#8212; clean, fast, elegant code. The pre-written tests included a case for "policy updated in database, next lookup returns updated policy." The cached implementation failed that test. Without TDD, that cache staleness bug would've made it to production.</p><p>---</p><h2>The CLAUDE.md File &#8212; Your Project's Long-Term Memory</h2><p>Every project I work on has a `CLAUDE.md` file at the root. This is Claude Code's project-level memory &#8212; instructions, conventions, and context that persist across sessions.</p><h3>What Goes in CLAUDE.md</h3><pre><code># Project: Order Service

## Architecture
- Go 1.22, gRPC, PostgreSQL
- Part of the commerce platform (12 services)
- Communicates with: payment-service, inventory-service, notification-service

## Conventions
- All new endpoints need integration tests in /tests/integration/
- Use structured logging (slog) &#8212; no fmt.Println
- Error types defined in /pkg/errors/ &#8212; don't create new ones without discussion
- Database migrations in /migrations/ &#8212; always reversible

## Known Gotchas
- The RefundProcessor runs async &#8212; don't assume refund completion is synchronous
- payment-service has a 30-second timeout on refund API calls
- inventory-service uses eventual consistency &#8212; reads might be stale by up to 2 seconds

## Testing
- Unit tests: `go test ./...`
- Integration tests: `make test-integration` (requires local Docker)
- E2E tests: `make test-e2e` (requires staging environment)</code></pre><h3>Why It Matters</h3><p>Without `CLAUDE.md`, every new Claude Code session starts from zero. Claude has to re-discover that your project uses structured logging, or that the payment service has a 30-second timeout, or that migrations must be reversible. With `CLAUDE.md`, Claude starts every session with institutional knowledge.</p><p>Think of it as onboarding documentation &#8212; but for your AI pair programmer. The same way you'd tell a new team member "hey, don't use fmt.Println, we use slog," you tell Claude once in `CLAUDE.md` and it remembers forever.</p><p>I update `CLAUDE.md` whenever I discover a new gotcha or establish a new convention. It's a living document. Over months, it accumulates the kind of tacit knowledge that usually only lives in senior engineers' heads.</p><p>---</p><h2>What Changed in How I Think About Development</h2><p>Using Claude Code with this process hasn't just made me faster. It's changed how I approach problems.</p><h3>I Write More Tech Specs, Not Fewer</h3><p>You'd think that having an AI write code would reduce the need for planning. The opposite happened. Because Claude can implement <em>so fast</em>, the bottleneck shifted from "writing code" to "knowing what code to write." The tech spec became the highest-leverage artifact in the entire process.</p><p>A well-written tech spec means Claude produces the right code on the first try. A vague tech spec means Claude produces plausible-looking wrong code that takes longer to debug than writing it from scratch.</p><h3>I Think in Tests Before I Think in Code</h3><p>TDD existed long before AI. But I'll be honest &#8212; I was a "write code first, add tests later" developer for most of my career. The friction of writing tests was high enough that I'd skip them when I was in a flow state.</p><p>With `hf-implement` handling the typing, the friction disappeared. Writing tests first costs me almost nothing in terms of time. And the quality improvement is obvious. I'll never go back.</p><h3>I Treat the Codebase as Readable, Not Just Writable</h3><p>Before Claude Code, my relationship with unfamiliar parts of the codebase was: avoid them. If a feature required touching a service I'd never worked on, I'd groan and spend half a day getting oriented.</p><p>Now, `hf-research` makes any part of the codebase approachable in minutes. I'm no longer afraid of unfamiliar code. I <em>actively seek out</em> context from services I don't own, because I know Claude can map the dependencies and surface the relevant details faster than I can.</p><p>This has made me a better systems thinker. I understand how our services interact at a level I never did before, because the cost of understanding dropped to near zero.</p><p>---</p><h2>The Honest Downsides</h2><p>This wouldn't be a useful blog post if I pretended everything was perfect.</p><h3>1. Context Window Limits Are Real</h3><p>Large codebases don't fit in a context window. `hf-research` mitigates this by using sub-agents that each focus on a subset of the codebase, but there are still moments where Claude loses track of a detail from earlier in the conversation. The fix is good `CLAUDE.md` files and breaking work into smaller, focused sessions.</p><h3>2. You Still Need to Read the Code</h3><p>Claude generates code. You must review it. Every line. Not "glance at it and approve" &#8212; actually read it, understand it, and verify it does what you intended. The tests help (they catch functional bugs), but they don't catch architectural mistakes, security issues, or performance problems. That's still your job.</p><h3>3. The Research Isn't Always Complete</h3><p>`hf-research` searches aggressively, but it can miss things. Undocumented side effects, implicit dependencies, tribal knowledge that isn't in the codebase. When the research seems too clean, I get suspicious and manually verify the critical paths.</p><h3>4. Not Every Task Needs This Process</h3><p>Quick bug fixes, one-line config changes, README updates &#8212; these don't need a four-phase workflow. I use the full process for features that touch multiple services or involve architectural decisions. For everything else, I just use Claude Code directly without the ceremony.</p><p>---</p><h2>Key Takeaways</h2><p>1. <strong>"Vibe coding" doesn't scale.</strong> It works for prototypes and personal projects. For production codebases with multiple services and teams, you need a process.</p><p>2. <strong>Research before you build.</strong> Use `hf-research` (or any systematic approach) to understand the current codebase before proposing changes. The 15 minutes you spend on research saves hours of debugging wrong assumptions.</p><p>3. <strong>Write the tech spec.</strong> Claude is an excellent thinking partner for tech specs &#8212; brainstorming approaches, evaluating trade-offs, generating diagrams. But the decisions are yours. The tech spec is the document where you commit to an approach.</p><p>4. <strong>Plan your TDD before implementing.</strong> Don't let the AI decide what to test. Define your test cases in the plan, then let the AI write them. Tests should be shaped by requirements, not by implementation.</p><p>5. <strong>Tests first, code second.</strong> `hf-implement` writes tests before writing production code. This catches bugs that would otherwise sail through &#8212; including bugs in AI-generated code.</p><p>6. <strong>CLAUDE.md is your project's institutional memory.</strong> Invest in it. Update it. It compounds over time and makes every Claude Code session more effective.</p><p>7. <strong>The human is still the architect.</strong> Claude Code is the most powerful implementation tool I've ever used. But it doesn't make architectural decisions. It doesn't evaluate business trade-offs. It doesn't know which approach your team can maintain. That's your job &#8212; and it always will be.</p><p>---</p><p><em>If you want to try this workflow, start with a `CLAUDE.md` file in your project. That single step will improve every interaction you have with Claude Code. Then gradually add structure &#8212; research, tech specs, TDD planning &#8212; as the complexity of your work demands it.</em></p><p><em>Connect with me at [mrinal.dev](https://mrinal.dev). I'm always happy to talk about engineering workflows.</em><a>mrinal.dev</a>. I'm always happy to talk about engineering workflows.*</p>]]></content:encoded></item></channel></rss>