<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Harikeshav Rameshkumar — blog</title>
    <link>https://harikeshav.me/blog</link>
    <description>Writing on distributed systems, machine learning infrastructure, and the tools I build to work faster.</description>
    <language>en-us</language>
    <lastBuildDate>Sun, 13 Sep 2026 12:22:03 GMT</lastBuildDate>
    <atom:link href="https://harikeshav.me/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Building a Raft KV Store</title>
      <link>https://harikeshav.me/blog/building-a-raft-kv-store</link>
      <guid isPermaLink="true">https://harikeshav.me/blog/building-a-raft-kv-store</guid>
      <pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate>
      <description>Three weeks chasing a liveness bug that only showed up under network partition.</description>
      <category>distributed-systems</category>
      <category>go</category>
      <category>raft</category>
      <content:encoded><![CDATA[<div class="mdx-block" data-ln="9" data-ln-end="12"><p class="my-4 leading-7 text-subtext1">I set out to write a Raft implementation because I wanted to understand the
paper, not because the world needed another one. What I got instead was three
weeks of staring at logs from a five-node cluster that would elect a leader,
run happily for a few minutes, and then quietly stop making progress.</p></div>
<div class="mdx-block" data-ln="14" data-ln-end="14"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="the-symptom"><span class="mr-2 select-none text-mauve">##</span><a href="#the-symptom" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">The symptom</a></h2></div>
<div class="mdx-block" data-ln="16" data-ln-end="18"><p class="my-4 leading-7 text-subtext1">Under a clean run everything worked. The failure only appeared once I started
partitioning nodes at random. The cluster would settle into a state where every
node believed a leader existed, but no entries were being committed.</p></div>
<div class="mdx-block" data-ln="20" data-ln-end="26"><div class="my-5 overflow-x-auto rounded-lg border border-surface0"><table class="w-full border-collapse text-sm"><thead class="bg-surface0/50 text-left text-subtext0"><tr><th class="border-b border-surface0 px-3 py-2 font-semibold">Node</th><th class="border-b border-surface0 px-3 py-2 font-semibold">Term</th><th class="border-b border-surface0 px-3 py-2 font-semibold">State</th><th class="border-b border-surface0 px-3 py-2 font-semibold">Commit index</th></tr></thead><tbody><tr><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">n1</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">14</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">leader</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">812</td></tr><tr><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">n2</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">14</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">follower</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">812</td></tr><tr><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">n3</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">15</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">candidate</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">809</td></tr><tr><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">n4</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">15</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">candidate</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">809</td></tr><tr><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">n5</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">14</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">follower</td><td class="border-b border-surface0/60 px-3 py-2 text-subtext1">812</td></tr></tbody></table></div></div>
<div class="mdx-block" data-ln="28" data-ln-end="29"><p class="my-4 leading-7 text-subtext1">Two nodes stuck as candidates in a higher term, three nodes happily following a
leader from the previous one. Nobody was wrong, exactly. Everybody was stale.</p></div>
<div class="mdx-block" data-ln="31" data-ln-end="31"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="the-tick-loop"><span class="mr-2 select-none text-mauve">##</span><a href="#the-tick-loop" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">The tick loop</a></h2></div>
<div class="mdx-block" data-ln="33" data-ln-end="33"><p class="my-4 leading-7 text-subtext1">Here is the shape of the code that caused it:</p></div>
<div class="mdx-block" data-ln="35" data-ln-end="53"><div class="group relative my-5"><button type="button" aria-label="Copy code" class="absolute right-2 top-2 z-10 flex items-center gap-1 rounded-md border px-2 py-1 text-[0.65rem] transition-all opacity-0 focus-visible:opacity-100 group-hover:opacity-100 border-surface1 bg-surface0/80 text-subtext0 hover:border-mauve/60 hover:text-text"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy h-3 w-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>copy</button><pre class="shiki shiki-themes catppuccin-latte catppuccin-mocha" style="background-color:#eff1f5;--shiki-dark-bg:#1e1e2e;color:#4c4f69;--shiki-dark:#cdd6f4" tabindex="0"><code><span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">func</span><span style="color:#7C7F93;--shiki-dark:#9399B2"> (</span><span style="color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#EBA0AC;--shiki-dark-font-style:italic">r </span><span style="color:#179299;--shiki-dark:#94E2D5">*</span><span style="color:#DF8E1D;--shiki-light-font-style:italic;--shiki-dark:#F9E2AF;--shiki-dark-font-style:italic">Raft</span><span style="color:#7C7F93;--shiki-dark:#9399B2">)</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic"> tick</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span><span style="color:#7C7F93;--shiki-dark:#9399B2"> {</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">	r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">mu</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">Lock</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">	defer</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">mu</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">Unlock</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">	r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">elapsed</span><span style="color:#179299;--shiki-dark:#94E2D5">++</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">	if</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">state </span><span style="color:#179299;--shiki-dark:#94E2D5">==</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> Leader </span><span style="color:#7C7F93;--shiki-dark:#9399B2">{</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">		if</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">elapsed </span><span style="color:#179299;--shiki-dark:#94E2D5">&gt;=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">heartbeatTimeout </span><span style="color:#7C7F93;--shiki-dark:#9399B2">{</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">			r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">elapsed </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#FE640B;--shiki-dark:#FAB387"> 0</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">			r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">broadcastAppendEntries</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">		}</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">		return</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">	}</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">	if</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">elapsed </span><span style="color:#179299;--shiki-dark:#94E2D5">&gt;=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">electionTimeout </span><span style="color:#7C7F93;--shiki-dark:#9399B2">{</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">		r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">elapsed </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#FE640B;--shiki-dark:#FAB387"> 0</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">		r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">becomeCandidate</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">	}</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">}</span></span></code></pre></div></div>
<div class="mdx-block" data-ln="55" data-ln-end="59"><p class="my-4 leading-7 text-subtext1">The bug is not in this function. The bug is that <code>broadcastAppendEntries</code> sent
RPCs while holding <code>r.mu</code>, and the RPC layer blocked on a partitioned peer. The
leader&#x27;s tick loop stalled, heartbeats stopped, and the two reachable followers
timed out and started an election that they could never win — because the
partition kept them from reaching a quorum.</p></div>
<div class="mdx-block" data-ln="61" data-ln-end="61"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="the-fix"><span class="mr-2 select-none text-mauve">##</span><a href="#the-fix" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">The fix</a></h2></div>
<div class="mdx-block" data-ln="63" data-ln-end="64"><blockquote class="my-5 border-l-2 border-mauve/60 bg-mantle/40 py-2 pl-4 pr-3 italic text-subtext0">
<p class="my-4 leading-7 text-subtext1">Never hold a lock across a network call. It is the oldest advice in the book,
and I still had to rediscover it the hard way.</p>
</blockquote></div>
<div class="mdx-block" data-ln="66" data-ln-end="67"><p class="my-4 leading-7 text-subtext1">The repair was to snapshot the state under the lock, release it, and only then
talk to the network:</p></div>
<div class="mdx-block" data-ln="69" data-ln-end="79"><div class="group relative my-5"><button type="button" aria-label="Copy code" class="absolute right-2 top-2 z-10 flex items-center gap-1 rounded-md border px-2 py-1 text-[0.65rem] transition-all opacity-0 focus-visible:opacity-100 group-hover:opacity-100 border-surface1 bg-surface0/80 text-subtext0 hover:border-mauve/60 hover:text-text"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy h-3 w-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>copy</button><pre class="shiki shiki-themes catppuccin-latte catppuccin-mocha" style="background-color:#eff1f5;--shiki-dark-bg:#1e1e2e;color:#4c4f69;--shiki-dark:#cdd6f4" tabindex="0"><code><span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">func</span><span style="color:#7C7F93;--shiki-dark:#9399B2"> (</span><span style="color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#EBA0AC;--shiki-dark-font-style:italic">r </span><span style="color:#179299;--shiki-dark:#94E2D5">*</span><span style="color:#DF8E1D;--shiki-light-font-style:italic;--shiki-dark:#F9E2AF;--shiki-dark-font-style:italic">Raft</span><span style="color:#7C7F93;--shiki-dark:#9399B2">)</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic"> broadcastAppendEntries</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span><span style="color:#7C7F93;--shiki-dark:#9399B2"> {</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">	peers</span><span style="color:#7C7F93;--shiki-dark:#9399B2">,</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> term</span><span style="color:#7C7F93;--shiki-dark:#9399B2">,</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> entries </span><span style="color:#179299;--shiki-dark:#94E2D5">:=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">snapshotForReplication</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">	r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">mu</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">Unlock</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">	defer</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">mu</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">Lock</span><span style="color:#7C7F93;--shiki-dark:#9399B2">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">	for</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> _</span><span style="color:#7C7F93;--shiki-dark:#9399B2">,</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> p </span><span style="color:#179299;--shiki-dark:#94E2D5">:=</span><span style="color:#8839EF;--shiki-dark:#CBA6F7"> range</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> peers </span><span style="color:#7C7F93;--shiki-dark:#9399B2">{</span></span>
<span class="line"><span style="color:#8839EF;--shiki-dark:#CBA6F7">		go</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> r</span><span style="color:#7C7F93;--shiki-dark:#9399B2">.</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">sendAppendEntries</span><span style="color:#7C7F93;--shiki-dark:#9399B2">(</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">p</span><span style="color:#7C7F93;--shiki-dark:#9399B2">,</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> term</span><span style="color:#7C7F93;--shiki-dark:#9399B2">,</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> entries</span><span style="color:#7C7F93;--shiki-dark:#9399B2">)</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">	}</span></span>
<span class="line"><span style="color:#7C7F93;--shiki-dark:#9399B2">}</span></span></code></pre></div></div>
<div class="mdx-block" data-ln="81" data-ln-end="81"><p class="my-4 leading-7 text-subtext1">One line of real change. Three weeks to find it.</p></div>
<div class="mdx-block" data-ln="83" data-ln-end="83"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="what-i-would-do-differently"><span class="mr-2 select-none text-mauve">##</span><a href="#what-i-would-do-differently" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">What I would do differently</a></h2></div>
<div class="mdx-block" data-ln="85" data-ln-end="87"><ul class="my-4 list-disc space-y-2 pl-6 text-subtext1 marker:text-mauve">
<li class="leading-7 pl-1">Write the partition test <em class="text-subtext0">first</em>. The happy path proves nothing.</li>
<li class="leading-7 pl-1">Log the full cluster state on every term change, not just on errors.</li>
<li class="leading-7 pl-1">Treat any lock held for more than a millisecond as a bug worth explaining.</li>
</ul></div>
<div class="mdx-block" data-ln="89" data-ln-end="91"><p class="my-4 leading-7 text-subtext1">If you want to read the paper that started all this, it is
<a href="https://raft.github.io/raft.pdf" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">In Search of an Understandable Consensus Algorithm</a>,
and it is genuinely as readable as it claims to be.</p></div>]]></content:encoded>
    </item>
    <item>
      <title>Why I Rewrote My Nvim Config</title>
      <link>https://harikeshav.me/blog/why-i-rewrote-my-nvim-config</link>
      <guid isPermaLink="true">https://harikeshav.me/blog/why-i-rewrote-my-nvim-config</guid>
      <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
      <description>A 2,000-line init.lua I could no longer explain, and what replaced it.</description>
      <category>neovim</category>
      <category>lua</category>
      <category>tooling</category>
      <content:encoded><![CDATA[<div class="mdx-block" data-ln="9" data-ln-end="11"><p class="my-4 leading-7 text-subtext1">My <code>init.lua</code> had grown to just over two thousand lines. I could not have told
you what half of it did. Every plugin I had ever been curious about was still
in there, disabled by a commented-out line I was afraid to delete.</p></div>
<div class="mdx-block" data-ln="13" data-ln-end="13"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="the-rule-i-gave-myself"><span class="mr-2 select-none text-mauve">##</span><a href="#the-rule-i-gave-myself" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">The rule I gave myself</a></h2></div>
<div class="mdx-block" data-ln="15" data-ln-end="16"><p class="my-4 leading-7 text-subtext1">One rule: <strong class="font-bold text-text">every line has to earn its place.</strong> If I could not explain what a
setting did and why I wanted it, it did not survive the rewrite.</p></div>
<div class="mdx-block" data-ln="18" data-ln-end="18"><p class="my-4 leading-7 text-subtext1">That killed about sixty percent of the config immediately.</p></div>
<div class="mdx-block" data-ln="20" data-ln-end="20"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="structure"><span class="mr-2 select-none text-mauve">##</span><a href="#structure" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">Structure</a></h2></div>
<div class="mdx-block" data-ln="22" data-ln-end="23"><p class="my-4 leading-7 text-subtext1">The old config was one file. The new one is a directory where each module owns
exactly one concern:</p></div>
<div class="mdx-block" data-ln="25" data-ln-end="33"><div class="group relative my-5"><button type="button" aria-label="Copy code" class="absolute right-2 top-2 z-10 flex items-center gap-1 rounded-md border px-2 py-1 text-[0.65rem] transition-all opacity-0 focus-visible:opacity-100 group-hover:opacity-100 border-surface1 bg-surface0/80 text-subtext0 hover:border-mauve/60 hover:text-text"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy h-3 w-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>copy</button><pre class="shiki shiki-themes catppuccin-latte catppuccin-mocha" style="background-color:#eff1f5;--shiki-dark-bg:#1e1e2e;color:#4c4f69;--shiki-dark:#cdd6f4" tabindex="0"><code><span class="line"><span style="color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#9399B2;--shiki-dark-font-style:italic">-- lua/config/lazy.lua</span></span>
<span class="line"><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">require</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">(</span><span style="color:#40A02B;--shiki-dark:#A6E3A1">&quot;lazy&quot;</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">).</span><span style="color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#89B4FA;--shiki-dark-font-style:italic">setup</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">({</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">  spec </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> { { import </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#40A02B;--shiki-dark:#A6E3A1"> &quot;plugins&quot; </span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">} },</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">  defaults </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> { lazy </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#D20F39;--shiki-dark:#F38BA8"> true</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> },</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">  install </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> { colorscheme </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> { </span><span style="color:#40A02B;--shiki-dark:#A6E3A1">&quot;catppuccin&quot; </span><span style="color:#4C4F69;--shiki-dark:#CDD6F4">} },</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">  checker </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> { enabled </span><span style="color:#179299;--shiki-dark:#94E2D5">=</span><span style="color:#D20F39;--shiki-dark:#F38BA8"> false</span><span style="color:#4C4F69;--shiki-dark:#CDD6F4"> },</span></span>
<span class="line"><span style="color:#4C4F69;--shiki-dark:#CDD6F4">})</span></span></code></pre></div></div>
<div class="mdx-block" data-ln="35" data-ln-end="37"><p class="my-4 leading-7 text-subtext1">Each plugin gets its own file under <code>lua/plugins/</code>, returning a spec table.
Adding a plugin is creating a file. Removing one is deleting a file. There is
no central list to keep in sync, which is where the old config rotted.</p></div>
<div class="mdx-block" data-ln="39" data-ln-end="39"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="what-i-actually-kept"><span class="mr-2 select-none text-mauve">##</span><a href="#what-i-actually-kept" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">What I actually kept</a></h2></div>
<div class="mdx-block" data-ln="41" data-ln-end="45"><ul class="my-4 list-disc space-y-2 pl-6 text-subtext1 marker:text-mauve">
<li class="leading-7 pl-1"><code>telescope.nvim</code> — the only fuzzy finder I have never wanted to replace.</li>
<li class="leading-7 pl-1"><code>nvim-treesitter</code> — worth it for <code>textobjects</code> alone.</li>
<li class="leading-7 pl-1"><code>neo-tree.nvim</code> — I use it perhaps once a day, and that once matters.</li>
<li class="leading-7 pl-1"><code>lualine.nvim</code> — mostly for the mode indicator.</li>
<li class="leading-7 pl-1"><code>catppuccin</code> — mocha at night, latte when the sun is out.</li>
</ul></div>
<div class="mdx-block" data-ln="47" data-ln-end="47"><h2 class="scroll-mt-20 font-bold text-text [&amp;_a]:text-inherit [&amp;_a]:no-underline mt-10 mb-3 text-xl sm:text-2xl" id="what-i-dropped"><span class="mr-2 select-none text-mauve">##</span><a href="#what-i-dropped" class="text-blue underline decoration-blue/40 underline-offset-2 transition-colors hover:text-mauve hover:decoration-mauve/60" target="_blank" rel="noreferrer noopener">What I dropped</a></h2></div>
<div class="mdx-block" data-ln="49" data-ln-end="50"><p class="my-4 leading-7 text-subtext1">Every completion plugin except one. Every &quot;productivity&quot; plugin I had installed
after reading a blog post and never opened again. Three separate git integrations.</p></div>
<div class="mdx-block" data-ln="52" data-ln-end="53"><p class="my-4 leading-7 text-subtext1">The result is around four hundred lines, and I can explain all of them. Startup
went from 180ms to 41ms, which was not the point, but I will take it.</p></div>
<div class="mdx-block" data-ln="55" data-ln-end="55"><blockquote class="my-5 border-l-2 border-mauve/60 bg-mantle/40 py-2 pl-4 pr-3 italic text-subtext0">
<p class="my-4 leading-7 text-subtext1">The best config is the one you can delete from confidently.</p>
</blockquote></div>]]></content:encoded>
    </item>
  </channel>
</rss>
