<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Noos - Where Thought, Code, and Craft Converge</title>
    <subtitle>Personal blog about programming, technology, and engineering insights. Topics include Rust, DevOps, Linux, and software craftsmanship.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://noos.blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://noos.blog"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-05T00:00:00+00:00</updated>
    <id>https://noos.blog/atom.xml</id>
    <entry xml:lang="en">
        <title>When nothing feels fun any more</title>
        <published>2026-04-26T00:00:00+00:00</published>
        <updated>2026-04-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/losing-interest-in-everything/"/>
        <id>https://noos.blog/posts/losing-interest-in-everything/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/losing-interest-in-everything/">&lt;p&gt;I watched a video recently where a former tech engineer talks about something I have been feeling in myself and seeing in people around me. It is this growing emptiness, where things that used to bring joy now feel flat. Gaming, traveling, hobbies, even just relaxing; everything feels like it has lost its color.&lt;&#x2F;p&gt;
&lt;p&gt;The video calls it a clinical name, but I do not know if that is what I have or if it is just a phase. What I do know is the feeling is real, and I am not sure if it is going to stay or pass.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-pressure-to-keep-building&quot;&gt;The pressure to keep building&lt;&#x2F;h3&gt;
&lt;p&gt;This hits harder when you work in tech. There is this constant push to keep building, keep shipping, keep getting better. A former Google engineer in that video said even after leaving the job, the habit of always producing followed him. I get that. Even when I am supposed to rest, some part of my brain is thinking about the next thing to build or learn.&lt;&#x2F;p&gt;
&lt;p&gt;Big win or small win, the joy fades fast. You finish something, feel good for a few hours, then start worrying about the next thing. It is like the moment you stop, you start falling behind.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;time-feels-strange&quot;&gt;Time feels strange&lt;&#x2F;h3&gt;
&lt;p&gt;Time has felt strange for a while now. I think this has been building for years, but we are moving so fast that it creates this quiet gap. Days blur together. You look back and wonder where the month went. It is not just being busy; it is something deeper. A kind of worry that creeps in when you realize you are pushing yourself hard without knowing what happens after.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-this-might-be-happening&quot;&gt;Why this might be happening&lt;&#x2F;h3&gt;
&lt;p&gt;The video pointed to a few things that clicked with me. First is how everything is about money now. Every hobby has someone trying to sell you a course or a side hustle plan. You cannot just enjoy photography; you have to think about building a brand. You cannot just play a game; you have to consider streaming it. The natural fun gets drained because everything becomes a way to make money.&lt;&#x2F;p&gt;
&lt;p&gt;Then there is the social media trap. Doom-scrolling eats up whatever free time you have left, and the constant comparison makes hobbies feel like a race. When everything becomes about status and attention, the fun goes away.&lt;&#x2F;p&gt;
&lt;p&gt;And there is the noise online. We get hit with biased content and ads all day. The mind builds a wall to protect itself. It is a defense, but it also makes you stop caring about good things.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-i-am-trying&quot;&gt;What I am trying&lt;&#x2F;h3&gt;
&lt;p&gt;I do not have answers, but I have been trying a few things that help a little.&lt;&#x2F;p&gt;
&lt;p&gt;I cut social media down a lot. Not just time limits, but choosing which sites I even open. I stick to places that give me something real, where I learn, not just scroll through fake updates.&lt;&#x2F;p&gt;
&lt;p&gt;I read more about things outside tech. Fiction, history, random essays about stuff I know nothing about. It reminds me the world is bigger than my work bubble.&lt;&#x2F;p&gt;
&lt;p&gt;Sleep is the obvious one everyone skips. I am trying to sleep more so my body actually rests. Still working on this, but the nights where I get enough sleep, the next day feels different.&lt;&#x2F;p&gt;
&lt;p&gt;And family time. Just being around people who knew me before I was a programmer. Talks that have nothing to do with work or getting better at things. That helps more than I expected.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-sure-where-this-goes&quot;&gt;Not sure where this goes&lt;&#x2F;h3&gt;
&lt;p&gt;I do not know if this emptiness is a short reaction to our world now or something that stays. Maybe it is a shared thing that will shift as things change. Maybe it is personal and everyone has to find their own way.&lt;&#x2F;p&gt;
&lt;p&gt;What I do know is noticing it is the first step. Taking back your attention feels like pushing back against a world that wants to make money from every second of it. I am still figuring out what that looks like day to day.&lt;&#x2F;p&gt;
&lt;p&gt;The video that got me thinking is &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;mlukRm6ywnA&quot;&gt;here&lt;&#x2F;a&gt;. Worth a watch if this sounds familiar.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Let us see how it goes.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Stacked PRs go native in GitHub, and what that means for AI-assisted work</title>
        <published>2026-04-19T00:00:00+00:00</published>
        <updated>2026-04-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/stacked-prs-go-native-and-the-ai-angle/"/>
        <id>https://noos.blog/posts/stacked-prs-go-native-and-the-ai-angle/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/stacked-prs-go-native-and-the-ai-angle/">&lt;p&gt;GitHub announced &lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&quot;&gt;native Stacked PRs&lt;&#x2F;a&gt;, still in private preview. I wanted to write about it for two reasons. First, it bakes in a workflow some of us have been doing for a while, either manually or via third-party tools, mostly to keep PRs small enough that reviewers actually read them. Second, it lands at a moment when AI agents are producing more of the diffs, and the gap between &quot;diff size&quot; and &quot;what a human can review well&quot; is getting wider every month.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-a-stack-is-quickly&quot;&gt;What a stack is, quickly&lt;&#x2F;h2&gt;
&lt;p&gt;A stack is a chain of pull requests in the same repo where each PR targets the branch of the PR below it. Instead of one giant PR that touches the database layer, the API, and the UI, you get three smaller ones that build on each other:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;main
&lt;&#x2F;span&gt;&lt;span&gt; ↑
&lt;&#x2F;span&gt;&lt;span&gt; PR #1: auth-layer
&lt;&#x2F;span&gt;&lt;span&gt; ↑
&lt;&#x2F;span&gt;&lt;span&gt; PR #2: api-endpoints
&lt;&#x2F;span&gt;&lt;span&gt; ↑
&lt;&#x2F;span&gt;&lt;span&gt; PR #3: frontend
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each layer is reviewed on its own. When the bottom is ready you can merge the bottom couple, or all of them at once, and the rest auto-rebase onto the new base. That auto-rebase is one of the things that makes a stack worth the trouble; doing it by hand across five branches is the part that always made me give up before.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-actually-new&quot;&gt;What is actually new&lt;&#x2F;h2&gt;
&lt;p&gt;Stacking as a workflow is not new. &lt;a href=&quot;https:&#x2F;&#x2F;graphite.dev&#x2F;stacking&quot;&gt;Graphite&lt;&#x2F;a&gt; has built a polished CLI (&lt;code&gt;gt&lt;&#x2F;code&gt;), a VS Code extension, a PR inbox, and an AI review product around it. Meta&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ezyang&#x2F;ghstack&quot;&gt;&lt;code&gt;ghstack&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; by Edward Yang has been around for years and powers a lot of the PyTorch workflow. There is also &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ejoffe&#x2F;spr&quot;&gt;&lt;code&gt;spr&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and historically Phabricator did this before Meta sunset it.&lt;&#x2F;p&gt;
&lt;p&gt;What GitHub adds, going by the &lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&#x2F;?ref=console.dev&quot;&gt;overview docs&lt;&#x2F;a&gt;, is the bits that previous tools could only approximate from the outside:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A stack map in the PR header so reviewers can navigate between layers.&lt;&#x2F;li&gt;
&lt;li&gt;Branch protection rules enforced against the &lt;strong&gt;final target branch&lt;&#x2F;strong&gt; (usually &lt;code&gt;main&lt;&#x2F;code&gt;), not just the immediate parent branch of each PR.&lt;&#x2F;li&gt;
&lt;li&gt;CI runs each PR as if it were targeting the final branch, so green checks actually mean something.&lt;&#x2F;li&gt;
&lt;li&gt;One-click merge of multiple layers, with automatic rebase of the rest of the stack afterwards.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &quot;branch protection against the final target&quot; point is the one that quietly matters. With third-party tools, your PR #3 in a stack technically targets PR #2&#x27;s branch, which often does not have the same protection rules as &lt;code&gt;main&lt;&#x2F;code&gt;. Teams used to work around this with naming conventions, bots, or just hoping reviewers paid attention. Native handling closes that gap.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trying-the-cli-right-now&quot;&gt;Trying the CLI right now&lt;&#x2F;h2&gt;
&lt;p&gt;The feature is in private preview as of April 2026, and you need to be allowlisted on a repo for the server-side bits (&lt;code&gt;submit&lt;&#x2F;code&gt;, &lt;code&gt;merge&lt;&#x2F;code&gt;, the stack map UI) to work. But the CLI itself is publicly installable. I poked at it a bit:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gh extension install github&#x2F;gh-stack
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gh stack&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --help
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Create,&lt;&#x2F;span&gt;&lt;span&gt; navigate, and manage stacks of branches and pull requests.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Usage:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;gh&lt;&#x2F;span&gt;&lt;span&gt; stack &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;command&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Available&lt;&#x2F;span&gt;&lt;span&gt; Commands:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;         Add a new branch on top of the current stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;alias       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Create &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a shell alias for gh stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bottom&lt;&#x2F;span&gt;&lt;span&gt;      Check out the bottom branch of the stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;    Checkout a stack from a PR number or branch name
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;down&lt;&#x2F;span&gt;&lt;span&gt;        Check out a branch further down in the stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;feedback&lt;&#x2F;span&gt;&lt;span&gt;    Submit feedback for gh-stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;        Initialize a new stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;       Merge a stack of PRs
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;        Push all branches in the current stack to the remote
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rebase&lt;&#x2F;span&gt;&lt;span&gt;      Rebase a stack of branches
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;      Create a stack of PRs on GitHub
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;        Sync the current stack with the remote
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;top&lt;&#x2F;span&gt;&lt;span&gt;         Check out the top branch of the stack
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;unstack&lt;&#x2F;span&gt;&lt;span&gt;     Delete a stack locally and on GitHub
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The local workflow works without any preview access. In a throwaway repo:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; git init&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -q &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; commit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --allow-empty -m &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -q
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gh stack init test-stack-bottom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt; Creating stack with trunk main and branch test-stack-bottom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Switched&lt;&#x2F;span&gt;&lt;span&gt; to branch test-stack-bottom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;To&lt;&#x2F;span&gt;&lt;span&gt; add a new layer to your stack, run `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;gh&lt;&#x2F;span&gt;&lt;span&gt; stack add`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;When&lt;&#x2F;span&gt;&lt;span&gt; you&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;re ready to push to GitHub and open a stack of PRs, run `gh stack submit`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;$ echo &amp;quot;auth&amp;quot; &amp;gt; auth.txt &amp;amp;&amp;amp; git add . &amp;amp;&amp;amp; git commit -m &amp;quot;auth layer&amp;quot; -q
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;$ gh stack add api-layer
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;✓ Created and checked out branch &amp;quot;api-layer&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;$ git log --oneline --all --decorate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;463808f (HEAD -&amp;gt; api-layer, test-stack-bottom) auth layer
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2b6a4ea (main) init
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So you can install it, play with &lt;code&gt;init&lt;&#x2F;code&gt;, &lt;code&gt;add&lt;&#x2F;code&gt;, &lt;code&gt;bottom&lt;&#x2F;code&gt;, &lt;code&gt;top&lt;&#x2F;code&gt;, &lt;code&gt;down&lt;&#x2F;code&gt;, &lt;code&gt;up&lt;&#x2F;code&gt;, &lt;code&gt;rebase&lt;&#x2F;code&gt;, &lt;code&gt;unstack&lt;&#x2F;code&gt; locally today. The bits that need GitHub-side support are the actual &lt;code&gt;submit&lt;&#x2F;code&gt; and &lt;code&gt;merge&lt;&#x2F;code&gt;, and that part is gated by the &lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&#x2F;?ref=console.dev&quot;&gt;waitlist&lt;&#x2F;a&gt;. Prerequisites are &lt;code&gt;gh&lt;&#x2F;code&gt; v2.0+ and Git 2.20+ (&lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&#x2F;getting-started&#x2F;quick-start&#x2F;&quot;&gt;Quick Start&lt;&#x2F;a&gt;), both pretty undemanding.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a &lt;code&gt;gh stack alias&lt;&#x2F;code&gt; command that wires up &lt;code&gt;gs&lt;&#x2F;code&gt; as a shorter alias, which I will probably end up using because typing &lt;code&gt;gh stack add&lt;&#x2F;code&gt; thirty times a day gets old.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-ai-assisted-angle&quot;&gt;The AI-assisted angle&lt;&#x2F;h2&gt;
&lt;p&gt;Here is the part I actually wanted to think out loud about.&lt;&#x2F;p&gt;
&lt;p&gt;If you have reviewed a PR opened by an AI agent (Cursor, Codex, Claude Code, Copilot, or any of the cloud agents), you know the failure mode. The agent gets asked to &quot;add user profiles&quot;, and twenty minutes later there is a PR with a migration, a schema, a service layer, three endpoints, two React components, a settings page, and seventeen unit tests. All in one or two commits. Reviewing that with any rigour is an afternoon&#x27;s work, and the path of least resistance is to skim, push back on a couple of obvious things, and merge.&lt;&#x2F;p&gt;
&lt;p&gt;Stacked PRs are a structural answer to that problem. If the agent (or the human supervising it) is encouraged to land the migration first, then the service, then the endpoints, then the UI, each layer is something a person can actually read in one sitting. Conflicts get smaller. Reverts get surgical. The cost of &quot;actually let&#x27;s redo the data model&quot; drops from &quot;throw away the whole PR&quot; to &quot;throw away the bottom layer and rebase&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;GitHub seems to know this is the angle. The docs include an AI agent integration step:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; skills add github&#x2F;gh-stack
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That installs a skill telling the agent how to author and manage stacks. I have not tried it yet (no preview access), so I cannot tell you how good it is in practice. The bet is interesting though: instead of teaching humans to slice their AI-generated work after the fact, teach the agent to slice its own work and produce a stack from the start.&lt;&#x2F;p&gt;
&lt;p&gt;A few honest reservations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;AI agents can mechanically split a diff (one file per commit, one logical component per commit). They are still not great at deciding &lt;em&gt;where&lt;&#x2F;em&gt; the right review boundaries are. That is exactly the kind of architectural judgment they tend to miss. A stack of five PRs split in the wrong places is arguably worse than one large PR, because reviewers context-switch between layers without the layering actually helping them.&lt;&#x2F;li&gt;
&lt;li&gt;A stack is only as solid as its bottom layer. If the data model in PR #1 is wrong, every later PR is built on sand. AI-authored stacks raise that risk because the agent often does not pause long enough to question the foundation.&lt;&#x2F;li&gt;
&lt;li&gt;Merge queues, codeowners, and stacks together are still a frontier. I expect most of the next round of papercuts will be in that intersection.&lt;&#x2F;li&gt;
&lt;li&gt;This is GitHub&#x27;s native version, but there are mature alternatives. If your team already pays for Graphite, the stack-management half of what they sell is now duplicated in GitHub itself, but the review queue, dashboards, and Diamond AI review are not. Whether you switch is more of a procurement question than a tooling question.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-i-am-going-to-actually-try&quot;&gt;What I am going to actually try&lt;&#x2F;h2&gt;
&lt;p&gt;Some small experiments I have lined up once I get preview access:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;gh stack init&lt;&#x2F;code&gt; for any change that touches more than one logical layer (data, service, UI). One layer per PR.&lt;&#x2F;li&gt;
&lt;li&gt;For AI-generated work, ask the agent to produce a plan first, treat each plan step as a layer, and only let it write code one layer at a time.&lt;&#x2F;li&gt;
&lt;li&gt;Test the merge-multiple-layers-at-once flow on a low-stakes change before relying on it for anything serious.&lt;&#x2F;li&gt;
&lt;li&gt;See whether the GitHub skill (&lt;code&gt;npx skills add github&#x2F;gh-stack&lt;&#x2F;code&gt;) actually gets the agent to author stacks unprompted, or whether it still needs hand-holding.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If any of these turn out interesting, I will come back with an update.&lt;&#x2F;p&gt;
&lt;p&gt;For now the &lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&quot;&gt;overview&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;github.github.com&#x2F;gh-stack&#x2F;getting-started&#x2F;quick-start&#x2F;&quot;&gt;Quick Start&lt;&#x2F;a&gt; are the two pages worth reading. The waitlist is on the same page.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how it goes once it is open.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How uv Works Under the Hood</title>
        <published>2026-04-08T00:00:00+00:00</published>
        <updated>2026-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/uv-how-it-works-under-the-hood/"/>
        <id>https://noos.blog/posts/uv-how-it-works-under-the-hood/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/uv-how-it-works-under-the-hood/">&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Updated 2026-05-05: re-verified against uv v0.11.8. I refreshed the release references, fixed inconsistent package versions in the lockfile excerpt, updated build backend examples, and cleaned up punctuation&#x2F;style issues.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I started using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&quot;&gt;uv&lt;&#x2F;a&gt; because the benchmarks seemed too good to be true: 10-100x faster than &lt;code&gt;pip&lt;&#x2F;code&gt;, resolves and installs in milliseconds. So I spent a weekend reading the source. This post is what I wish I&#x27;d had on day one.&lt;&#x2F;p&gt;
&lt;p&gt;It traces every layer: the repository structure, what actually happens when you type &lt;code&gt;uv init&lt;&#x2F;code&gt; or &lt;code&gt;uv add requests&lt;&#x2F;code&gt;, and the Rust concurrency patterns the resolver uses. No prior Rust experience needed, but if you&#x27;ve seen Rust before I&#x27;ll point to the specific patterns in the source.&lt;&#x2F;p&gt;
&lt;p&gt;Everything below was verified against uv &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;releases&#x2F;tag&#x2F;0.11.8&quot;&gt;v0.11.8&lt;&#x2F;a&gt; (April 2026). Specific numbers and code excerpts come from that tag; if you&#x27;re reading this much later, the line numbers in linked source files may have drifted, but the high-level architecture has been stable for many releases.&lt;&#x2F;p&gt;
&lt;p&gt;Quick release note: 0.11.8 mostly shipped CLI, lockfile, and configuration improvements (&lt;code&gt;pip uninstall -y&lt;&#x2F;code&gt;, &lt;code&gt;UV_NO_PROJECT&lt;&#x2F;code&gt;, &lt;code&gt;UV_PYTHON_SEARCH_PATH&lt;&#x2F;code&gt;, and &lt;code&gt;exclude-newer&lt;&#x2F;code&gt; lock handling fixes). It did not materially change the resolver architecture discussed in this post.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-what-uv-is&quot;&gt;1. What uv is&lt;&#x2F;h2&gt;
&lt;p&gt;uv is an &lt;strong&gt;extremely fast Python package and project manager&lt;&#x2F;strong&gt;, written in Rust and built by &lt;a href=&quot;https:&#x2F;&#x2F;astral.sh&quot;&gt;Astral&lt;&#x2F;a&gt;, the team behind &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;ruff&quot;&gt;Ruff&lt;&#x2F;a&gt;. It replaces most of your Python toolchain in one binary:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Old tool&lt;&#x2F;th&gt;&lt;th&gt;uv equivalent&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;pip install&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uv pip install&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;pip-compile&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uv pip compile&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;virtualenv&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;venv&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uv venv&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;pyenv&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uv python install&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;pipx run&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uvx&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;uv tool run&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;poetry&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;rye&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;uv init&lt;&#x2F;code&gt; + &lt;code&gt;uv add&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;It has 84k+ stars, is used in production at scale, and the underlying &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pubgrub-rs&#x2F;pubgrub&quot;&gt;&lt;code&gt;pubgrub-rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate it depends on is the &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-project-goals&#x2F;2025h1&#x2F;pubgrub-in-cargo.html&quot;&gt;designated basis for Cargo&#x27;s next dependency solver&lt;&#x2F;a&gt;. The same algorithm and largely the same Rust crate will eventually power both &lt;code&gt;uv&lt;&#x2F;code&gt; and &lt;code&gt;cargo&lt;&#x2F;code&gt; resolutions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-the-repository-layout&quot;&gt;2. The repository layout&lt;&#x2F;h2&gt;
&lt;p&gt;Clone the repo and you&#x27;ll see:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;uv&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── crates&#x2F;          # All Rust source code
&lt;&#x2F;span&gt;&lt;span&gt;├── docs&#x2F;            # MkDocs documentation
&lt;&#x2F;span&gt;&lt;span&gt;├── python&#x2F;          # Small Python shim package (the uv PyPI wheel)
&lt;&#x2F;span&gt;&lt;span&gt;├── scripts&#x2F;         # Benchmarking, release tooling
&lt;&#x2F;span&gt;&lt;span&gt;├── test&#x2F;            # Test fixtures, requirement files for benchmarks
&lt;&#x2F;span&gt;&lt;span&gt;├── Cargo.toml       # Rust workspace root
&lt;&#x2F;span&gt;&lt;span&gt;└── pyproject.toml   # uv manages itself with uv
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The real action is in &lt;code&gt;crates&#x2F;&lt;&#x2F;code&gt;. There are 67 crate directories in v0.11.8. Rust does not allow circular dependencies between crates, so the uv team structured the code as a directed acyclic graph of focused crates. Each does one thing:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;crates&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── uv&#x2F;                  # CLI binary: entry point and argument parsing (clap)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-resolver&#x2F;         # Dependency resolution engine (PubGrub)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-installer&#x2F;        # Installing packages into environments
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-client&#x2F;           # Async HTTP client for PyPI and registries
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-workspace&#x2F;        # pyproject.toml parsing and workspace discovery
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-python&#x2F;           # Python version management
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-cache&#x2F;            # Global content-addressed cache
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-distribution&#x2F;     # Wheel and sdist handling, metadata fetching
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-build&#x2F;            # uv&amp;#39;s own build backend (replaces setuptools&#x2F;hatchling)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-git&#x2F;              # Git dependency support (based on Cargo&amp;#39;s implementation)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-platform-tags&#x2F;    # Wheel compatibility tag matching
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-pep440&#x2F;           # Python version specifier parsing (PEP 440)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-pep508&#x2F;           # Dependency specifier parsing (PEP 508)
&lt;&#x2F;span&gt;&lt;span&gt;├── uv-types&#x2F;            # Shared type definitions used across crates
&lt;&#x2F;span&gt;&lt;span&gt;└── ... (many more)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can generate a visual dependency graph between crates:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; depgraph&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --dedup-transitive-deps --workspace-only &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dot -Tpng &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; graph.png
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The shape of the graph is roughly: &lt;code&gt;uv&lt;&#x2F;code&gt; (the binary) sits at the top and depends on everything. &lt;code&gt;uv-types&lt;&#x2F;code&gt; sits near the bottom and depends on almost nothing. Code only flows downward; cycles are a hard compile error.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-what-happens-when-you-run-uv-init&quot;&gt;3. What happens when you run &lt;code&gt;uv init&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s trace &lt;code&gt;uv init my-project&lt;&#x2F;code&gt; step by step through the codebase.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-argument-parsing-uv-crate&quot;&gt;Step 1: Argument parsing (&lt;code&gt;uv&lt;&#x2F;code&gt; crate)&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;uv&lt;&#x2F;code&gt; crate is the binary entry point. It uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;clap-rs&#x2F;clap&quot;&gt;clap&lt;&#x2F;a&gt; for argument parsing. When you run &lt;code&gt;uv init my-project&lt;&#x2F;code&gt;, clap matches the &lt;code&gt;init&lt;&#x2F;code&gt; subcommand and extracts the project name and any flags (&lt;code&gt;--lib&lt;&#x2F;code&gt;, &lt;code&gt;--app&lt;&#x2F;code&gt;, &lt;code&gt;--package&lt;&#x2F;code&gt;, &lt;code&gt;--bare&lt;&#x2F;code&gt;, &lt;code&gt;--build-backend&lt;&#x2F;code&gt;, etc.).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-2-workspace-discovery-uv-workspace&quot;&gt;Step 2: Workspace discovery (&lt;code&gt;uv-workspace&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;Before creating anything, uv checks whether you&#x27;re already inside an existing workspace. It walks &lt;strong&gt;up&lt;&#x2F;strong&gt; the directory tree looking for a &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; with a &lt;code&gt;[tool.uv.workspace]&lt;&#x2F;code&gt; section. If you&#x27;re inside one, the new project is registered as a workspace member automatically. If not, it creates a standalone project.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-scaffold-project-files&quot;&gt;Step 3: Scaffold project files&lt;&#x2F;h3&gt;
&lt;p&gt;For &lt;code&gt;uv init my-project&lt;&#x2F;code&gt;, uv writes these files to disk:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;my-project&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── .python-version    # e.g., &amp;quot;3.12&amp;quot;, pins the Python version
&lt;&#x2F;span&gt;&lt;span&gt;├── README.md
&lt;&#x2F;span&gt;&lt;span&gt;├── main.py            # boilerplate main() function
&lt;&#x2F;span&gt;&lt;span&gt;└── pyproject.toml
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The generated &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[project]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;my-project&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;0.1.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Add your description here&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;readme &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;README.md&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requires-python &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;gt;=3.12&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dependencies &lt;&#x2F;span&gt;&lt;span&gt;= []
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice there is &lt;strong&gt;no &lt;code&gt;[build-system]&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; section. Without it, the project is &lt;em&gt;not installable as a package&lt;&#x2F;em&gt;: it won&#x27;t be installed into the virtual environment itself. This is the correct default for applications (web servers, scripts, CLIs); it avoids an unnecessary install step and the complexity of choosing a build backend upfront.&lt;&#x2F;p&gt;
&lt;p&gt;If you pass &lt;code&gt;--lib&lt;&#x2F;code&gt; or &lt;code&gt;--package&lt;&#x2F;code&gt;, uv adds a &lt;code&gt;src&#x2F;&lt;&#x2F;code&gt; layout and a &lt;code&gt;[build-system]&lt;&#x2F;code&gt; pointing to &lt;code&gt;uv_build&lt;&#x2F;code&gt;, uv&#x27;s own Rust-native build backend:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[build-system]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requires &lt;&#x2F;span&gt;&lt;span&gt;= [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv_build&amp;gt;=0.11.8,&amp;lt;0.12&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;build-backend &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv_build&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;step-4-git-initialization&quot;&gt;Step 4: Git initialization&lt;&#x2F;h3&gt;
&lt;p&gt;uv runs &lt;code&gt;git init&lt;&#x2F;code&gt; and writes a &lt;code&gt;.gitignore&lt;&#x2F;code&gt; that excludes &lt;code&gt;.venv&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;__pycache__&#x2F;&lt;&#x2F;code&gt;. Skip this with &lt;code&gt;--no-vcs&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;No virtual environment is created yet.&lt;&#x2F;strong&gt; uv is lazy: it creates &lt;code&gt;.venv&lt;&#x2F;code&gt; only when you first run something or add a package. This avoids disk writes for projects you might never use.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-what-happens-when-you-run-uv-add-requests&quot;&gt;4. What happens when you run &lt;code&gt;uv add requests&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is the core operation. &lt;code&gt;uv add&lt;&#x2F;code&gt; installs a package and records it in &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; and &lt;code&gt;uv.lock&lt;&#x2F;code&gt;. Here is the full pipeline:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ uv add requests
&lt;&#x2F;span&gt;&lt;span&gt;Using CPython 3.12.10
&lt;&#x2F;span&gt;&lt;span&gt;Creating virtual environment at: .venv
&lt;&#x2F;span&gt;&lt;span&gt;Resolved 6 packages in 456ms
&lt;&#x2F;span&gt;&lt;span&gt;Prepared 5 packages in 451ms
&lt;&#x2F;span&gt;&lt;span&gt;Installed 5 packages in 4ms
&lt;&#x2F;span&gt;&lt;span&gt; + certifi==2026.2.25
&lt;&#x2F;span&gt;&lt;span&gt; + charset-normalizer==3.4.7
&lt;&#x2F;span&gt;&lt;span&gt; + idna==3.11
&lt;&#x2F;span&gt;&lt;span&gt; + requests==2.33.1
&lt;&#x2F;span&gt;&lt;span&gt; + urllib3==2.6.3
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two things worth noticing about that output. &quot;Resolved 6 packages&quot; includes the project itself as a node in the dependency graph (your project + 5 transitive deps), but only 5 packages are &lt;em&gt;installed&lt;&#x2F;em&gt;: the project itself isn&#x27;t installable here because there&#x27;s no &lt;code&gt;[build-system]&lt;&#x2F;code&gt;. And &quot;Prepared&quot; is uv&#x27;s word for &quot;downloaded and unpacked into the cache, ready to be linked into the venv&quot;: the actual install step is just creating links, which is why it takes 4ms.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stage-1-read-the-project-state-uv-workspace&quot;&gt;Stage 1: Read the project state (&lt;code&gt;uv-workspace&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;uv-workspace&lt;&#x2F;code&gt; reads &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; and constructs an in-memory &lt;code&gt;Manifest&lt;&#x2F;code&gt;: the project name, existing dependencies, Python version constraint, workspace members, and overrides.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stage-2-update-pyproject-toml-pyproject-mut&quot;&gt;Stage 2: Update &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; (&lt;code&gt;pyproject_mut&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;uv adds &lt;code&gt;requests&lt;&#x2F;code&gt; to the &lt;code&gt;dependencies&lt;&#x2F;code&gt; list in &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;. At this point it doesn&#x27;t know the version yet, so it records it as a bare requirement. After resolution (Stage 3), it rewrites the specifier with the resolved version. The exact form depends on the &lt;code&gt;--bounds&lt;&#x2F;code&gt; flag; the default (&lt;code&gt;AddBoundsKind::Lower&lt;&#x2F;code&gt; in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-workspace&#x2F;src&#x2F;pyproject_mut.rs&quot;&gt;&lt;code&gt;pyproject_mut.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;) is a single lower bound:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dependencies &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;requests&amp;gt;=2.33.1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This edit goes through &lt;code&gt;uv-workspace&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;pyproject_mut&lt;&#x2F;code&gt; module, which does &lt;strong&gt;surgical TOML editing&lt;&#x2F;strong&gt;: it patches exactly the field it needs to change without reformatting anything else in the file. That matters because &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; often contains hand-formatted sections, comments, and deliberate ordering that a naive serialize&#x2F;reserialize pass would obliterate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stage-3-resolution-uv-resolver-pubgrub&quot;&gt;Stage 3: Resolution (&lt;code&gt;uv-resolver&lt;&#x2F;code&gt; + PubGrub)&lt;&#x2F;h3&gt;
&lt;p&gt;The resolver answers: &lt;em&gt;&quot;Which exact version of every package satisfies all constraints?&quot;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is the most complex stage. We&#x27;ll cover it in depth in sections 5 and 6. The output is a complete &lt;code&gt;(package, version)&lt;&#x2F;code&gt; assignment for every package in the dependency tree.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stage-4-write-uv-lock&quot;&gt;Stage 4: Write &lt;code&gt;uv.lock&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The resolved set is serialized to &lt;code&gt;uv.lock&lt;&#x2F;code&gt;. This is a TOML file that records every package: its exact version, source URL, content hash, and its own dependencies. A real excerpt:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requires-python &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;gt;=3.12&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[package]]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2.33.1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;source &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;registry &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;pypi.org&#x2F;simple&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dependencies &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;certifi&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;charset-normalizer&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;idna&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;urllib3&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sdist &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;files.pythonhosted.org&#x2F;packages&#x2F;5f&#x2F;a4&#x2F;98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871&#x2F;requests-2.33.1.tar.gz&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;size &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;134120 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wheels &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;files.pythonhosted.org&#x2F;packages&#x2F;d7&#x2F;8e&#x2F;7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197&#x2F;requests-2.33.1-py3-none-any.whl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[[package]]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;urllib3&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2.6.3&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;source &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;registry &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;pypi.org&#x2F;simple&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dependencies &lt;&#x2F;span&gt;&lt;span&gt;= []
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sdist &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;files.pythonhosted.org&#x2F;packages&#x2F;c7&#x2F;24&#x2F;5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3&#x2F;urllib3-2.6.3.tar.gz&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;size &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;435556 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;wheels &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;files.pythonhosted.org&#x2F;packages&#x2F;39&#x2F;08&#x2F;aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84&#x2F;urllib3-2.6.3-py3-none-any.whl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hash &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; },
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The lockfile is &lt;strong&gt;universal&lt;&#x2F;strong&gt;: one &lt;code&gt;uv.lock&lt;&#x2F;code&gt; works on macOS, Linux, and Windows. It records all possible packages for all platforms using environment markers. When you install from the lockfile on a real machine, uv selects only the packages appropriate for that platform. This is a real improvement over &lt;code&gt;pip freeze&lt;&#x2F;code&gt; or &lt;code&gt;requirements.txt&lt;&#x2F;code&gt;, which are inherently platform-specific snapshots.&lt;&#x2F;p&gt;
&lt;p&gt;The hashes serve two purposes: integrity verification when downloading, and cache lookup keying.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; &lt;code&gt;uv.lock&lt;&#x2F;code&gt; is an internal format that may change between versions. If you need to read it programmatically, the experimental &lt;code&gt;uv workspace metadata --preview-features workspace-metadata&lt;&#x2F;code&gt; command outputs a stable JSON representation of the same dependency graph (it&#x27;s still preview as of v0.11.8, so don&#x27;t rely on its schema being final).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;stage-5-download-packages-uv-client-tokio&quot;&gt;Stage 5: Download packages (&lt;code&gt;uv-client&lt;&#x2F;code&gt; + Tokio)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;uv-client&lt;&#x2F;code&gt; is uv&#x27;s async HTTP client, built on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;seanmonstar&#x2F;reqwest&quot;&gt;reqwest&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;&quot;&gt;Tokio&lt;&#x2F;a&gt;. For every package not already in the global cache, uv fires off &lt;strong&gt;concurrent downloads&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;# pip: sequential
&lt;&#x2F;span&gt;&lt;span&gt;fetch requests → wait → fetch urllib3 → wait → fetch certifi → wait → ...
&lt;&#x2F;span&gt;&lt;span&gt;total ≈ N × round_trip_time
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# uv: concurrent
&lt;&#x2F;span&gt;&lt;span&gt;fetch requests ─┐
&lt;&#x2F;span&gt;&lt;span&gt;fetch urllib3   ├── all in parallel
&lt;&#x2F;span&gt;&lt;span&gt;fetch certifi   ─┘
&lt;&#x2F;span&gt;&lt;span&gt;total ≈ 1 × round_trip_time (roughly)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the largest contributor to uv&#x27;s cold-cache speed. Network I&#x2F;O latency is the bottleneck, and parallelism makes it roughly constant regardless of the number of packages.&lt;&#x2F;p&gt;
&lt;p&gt;Packages are stored in a &lt;strong&gt;global, content-addressed cache&lt;&#x2F;strong&gt; managed by &lt;code&gt;uv-cache&lt;&#x2F;code&gt;. The cache key is the SHA-256 hash of the package content. This means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Any package version you have ever installed is reused across all your projects, forever.&lt;&#x2F;li&gt;
&lt;li&gt;The cache is verified by hash on every use; silent corruption is caught immediately.&lt;&#x2F;li&gt;
&lt;li&gt;The cache is shared across &lt;code&gt;uv pip install&lt;&#x2F;code&gt;, &lt;code&gt;uv add&lt;&#x2F;code&gt;, and &lt;code&gt;uvx&lt;&#x2F;code&gt; tool installs.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;stage-6-install-packages-uv-installer&quot;&gt;Stage 6: Install packages (&lt;code&gt;uv-installer&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;Install&quot; normally implies copying files. uv mostly doesn&#x27;t copy anything.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;uv-installer&lt;&#x2F;code&gt; uses &lt;strong&gt;hard links&lt;&#x2F;strong&gt; wherever the filesystem supports them (most Linux and macOS filesystems do). A hard link means two directory entries point to the same physical data blocks on disk. Creating one is essentially free: a single filesystem metadata operation.&lt;&#x2F;p&gt;
&lt;p&gt;When uv &quot;installs&quot; a package into &lt;code&gt;.venv&#x2F;lib&#x2F;python3.12&#x2F;site-packages&#x2F;&lt;&#x2F;code&gt;, it&#x27;s creating hard links from the global cache into the virtual environment. No bytes are duplicated. This is why you see timings like &lt;code&gt;Installed 43 packages in 208ms&lt;&#x2F;code&gt;: that 208ms is the overhead of creating thousands of directory entries, not copying gigabytes.&lt;&#x2F;p&gt;
&lt;p&gt;On filesystems that don&#x27;t support cross-device hard links (e.g., home directory and temp on different mounts, or network filesystems), uv falls back to copy-on-write reflinks, then regular copies. But the common case is hard links.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-inside-the-resolver-the-two-thread-architecture&quot;&gt;5. Inside the resolver: the two-thread architecture&lt;&#x2F;h2&gt;
&lt;p&gt;The resolver is the most architecturally interesting part of uv, and the source code reveals a specific design decision that&#x27;s worth understanding.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-concurrency-problem&quot;&gt;The concurrency problem&lt;&#x2F;h3&gt;
&lt;p&gt;The PubGrub algorithm is inherently &lt;strong&gt;sequential and synchronous&lt;&#x2F;strong&gt;: it makes one decision at a time, and each decision depends on all previous decisions. You cannot trivially parallelize it.&lt;&#x2F;p&gt;
&lt;p&gt;But metadata fetching (querying PyPI for &quot;which versions of &lt;code&gt;flask&lt;&#x2F;code&gt; exist?&quot; or &quot;what does &lt;code&gt;flask==3.1.0&lt;&#x2F;code&gt; depend on?&quot;) is highly parallelizable I&#x2F;O.&lt;&#x2F;p&gt;
&lt;p&gt;The naive approach is to serialize everything: decide package, fetch metadata, decide next package, fetch metadata. This is what pip does. uv does something cleverer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;two-threads-communicating-via-channels&quot;&gt;Two threads communicating via channels&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;resolve()&lt;&#x2F;code&gt; function in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs#L250-L282&quot;&gt;&lt;code&gt;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is short enough to quote verbatim:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span&gt; async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; Result&amp;lt;ResolverOutput, ResolveError&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; state = Arc::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.state);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; provider = Arc::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.provider);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; A channel to fetch package metadata (e.g., given `flask`, fetch all versions) and version
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; metadata (e.g., given `flask==1.0.0`, fetch the metadata for that version).
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Channel size is set large to accommodate batch prefetching.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(request_sink, request_stream) = mpsc::channel(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Run the fetcher.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; requests_fut = state.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(provider.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(), request_stream).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;fuse&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Spawn the PubGrub solver on a dedicated thread.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; solver = state.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(tx, rx) = oneshot::channel();
&lt;&#x2F;span&gt;&lt;span&gt;    thread::Builder::new()
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv-resolver&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;move &lt;&#x2F;span&gt;&lt;span&gt;|| {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result = solver.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;solve&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;request_sink);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; This may fail if the main thread returned early due to an error.
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;_ = tx.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(result);
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; resolve_fut = async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;move &lt;&#x2F;span&gt;&lt;span&gt;{ rx.await.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;map_err&lt;&#x2F;span&gt;&lt;span&gt;(|_| ResolveError::ChannelClosed) };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Wait for both to complete.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;((), resolution) = tokio::try_join!(requests_fut, resolve_fut)?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    state.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;on_complete&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    resolution
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The architecture is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;┌────────────────────────────────────────────┐
&lt;&#x2F;span&gt;&lt;span&gt;│  Dedicated sync thread: &amp;quot;uv-resolver&amp;quot;       │
&lt;&#x2F;span&gt;&lt;span&gt;│  Runs PubGrub solver (solve())              │
&lt;&#x2F;span&gt;&lt;span&gt;│  → Sends fetch requests via mpsc::Sender   │
&lt;&#x2F;span&gt;&lt;span&gt;│  → Receives results via InMemoryIndex       │
&lt;&#x2F;span&gt;&lt;span&gt;└──────────────────┬─────────────────────────┘
&lt;&#x2F;span&gt;&lt;span&gt;                   │ mpsc channel (capacity 300)
&lt;&#x2F;span&gt;&lt;span&gt;                   ▼
&lt;&#x2F;span&gt;&lt;span&gt;┌────────────────────────────────────────────┐
&lt;&#x2F;span&gt;&lt;span&gt;│  Tokio async runtime                        │
&lt;&#x2F;span&gt;&lt;span&gt;│  Runs fetch() - handles request_stream      │
&lt;&#x2F;span&gt;&lt;span&gt;│  Fires concurrent HTTP requests             │
&lt;&#x2F;span&gt;&lt;span&gt;│  Writes results into Arc&amp;lt;InMemoryIndex&amp;gt;     │
&lt;&#x2F;span&gt;&lt;span&gt;└────────────────────────────────────────────┘
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The solver thread is &lt;strong&gt;synchronous&lt;&#x2F;strong&gt;. PubGrub&#x27;s algorithm doesn&#x27;t fit naturally into async&#x2F;await because it&#x27;s a tight loop with complex mutable state. Running it on its own OS thread sidesteps the question entirely.&lt;&#x2F;p&gt;
&lt;p&gt;The fetcher runs on Tokio&#x27;s async runtime. When the solver needs metadata for a package, it sends a &lt;code&gt;Request&lt;&#x2F;code&gt; down the &lt;code&gt;mpsc&lt;&#x2F;code&gt; channel and then looks up the &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt; (a &lt;code&gt;DashMap&lt;&#x2F;code&gt;-backed concurrent cache). If the result isn&#x27;t there yet, the solver thread blocks on the index&#x27;s per-key notifier. That blocks &lt;em&gt;only the solver thread&lt;&#x2F;em&gt;, not Tokio&#x27;s runtime, which keeps happily making more network requests.&lt;&#x2F;p&gt;
&lt;p&gt;This design separates concerns:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;PubGrub stays synchronous and simple.&lt;&#x2F;li&gt;
&lt;li&gt;Network I&#x2F;O stays async and concurrent.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Arc&amp;lt;ResolverState&amp;gt;&lt;&#x2F;code&gt; is shared safely between the two sides.&lt;&#x2F;li&gt;
&lt;li&gt;The 300-deep &lt;code&gt;mpsc&lt;&#x2F;code&gt; channel provides backpressure: if the solver burns through requests faster than the fetcher can keep up, it eventually blocks on &lt;code&gt;send&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-inmemoryindex&quot;&gt;The &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt; is a shared cache of already-fetched metadata, backed by &lt;code&gt;DashMap&lt;&#x2F;code&gt; (a concurrent hashmap that allows reads without locking the whole map). The solver reads from it; the fetcher writes to it. When a key is missing, the solver thread parks on a per-key notifier until the fetcher writes the value in. The Tokio runtime is never blocked, so other in-flight downloads keep making progress.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-pubgrub-the-dependency-resolution-algorithm&quot;&gt;6. PubGrub: the dependency resolution algorithm&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;why-this-is-hard&quot;&gt;Why this is hard&lt;&#x2F;h3&gt;
&lt;p&gt;The resolver must answer: given a set of requirements (with version ranges), find one exact version for every package such that every package&#x27;s requirements are also satisfied, recursively, for all transitive dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;In the general case, this is equivalent to the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Boolean_satisfiability_problem&quot;&gt;Boolean Satisfiability Problem&lt;&#x2F;a&gt; (SAT), which is NP-complete. Package ecosystems have thousands of packages and hundreds of versions each. Naive backtracking (try a version; if it conflicts, undo and try another) can be exponential.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pubgrub-s-approach-conflict-driven-clause-learning&quot;&gt;PubGrub&#x27;s approach: conflict-driven clause learning&lt;&#x2F;h3&gt;
&lt;p&gt;uv uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pubgrub-rs&#x2F;pubgrub&quot;&gt;pubgrub-rs&lt;&#x2F;a&gt;, the Rust implementation of &lt;a href=&quot;https:&#x2F;&#x2F;nex3.medium.com&#x2F;pubgrub-2fb6470504f&quot;&gt;PubGrub&lt;&#x2F;a&gt;. PubGrub was &lt;a href=&quot;https:&#x2F;&#x2F;nex3.medium.com&#x2F;pubgrub-2fb6470504f&quot;&gt;introduced in April 2018&lt;&#x2F;a&gt; by Natalie Weizenbaum for Dart&#x27;s &lt;code&gt;pub&lt;&#x2F;code&gt; package manager and has since been adopted by Bundler, Poetry, and now uv. It is a &lt;strong&gt;conflict-driven clause learning (CDCL)&lt;&#x2F;strong&gt; solver.&lt;&#x2F;p&gt;
&lt;p&gt;The old approach is: pick a version, try it, on conflict undo and try another. Worst case is exponential.&lt;&#x2F;p&gt;
&lt;p&gt;PubGrub&#x27;s approach: when a conflict is found, &lt;em&gt;learn from it&lt;&#x2F;em&gt; by recording an &lt;strong&gt;incompatibility clause&lt;&#x2F;strong&gt;. The clause encodes which combination of package versions caused the conflict. Any future partial assignment that would trigger that same conflict is pruned immediately, without re-exploring.&lt;&#x2F;p&gt;
&lt;p&gt;Concrete example: the resolver discovers that &lt;code&gt;a==2.0&lt;&#x2F;code&gt; and &lt;code&gt;b==3.0&lt;&#x2F;code&gt; can&#x27;t coexist (because they require conflicting versions of &lt;code&gt;c&lt;&#x2F;code&gt;). PubGrub records the incompatibility &lt;code&gt;{a==2.0, b==3.0}&lt;&#x2F;code&gt;. Any future partial solution containing both is ruled out by unit propagation, instantly. This turns exponential worst cases into manageable ones for realistic package graphs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-resolver-loop&quot;&gt;The resolver loop&lt;&#x2F;h3&gt;
&lt;p&gt;Here is how PubGrub runs in uv, from the &lt;a href=&quot;https:&#x2F;&#x2F;docs.astral.sh&#x2F;uv&#x2F;reference&#x2F;internals&#x2F;resolver&#x2F;&quot;&gt;official internals documentation&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Initialize.&lt;&#x2F;strong&gt; Create a virtual root package representing your project. It is the only &quot;decided&quot; package; everything else is undecided.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Pick the highest-priority undecided package.&lt;&#x2F;strong&gt; uv&#x27;s priority order is roughly:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;URL dependencies (git, path, file): pinned, no version negotiation needed.&lt;&#x2F;li&gt;
&lt;li&gt;Packages with &lt;code&gt;==&lt;&#x2F;code&gt; constraints: version is already determined.&lt;&#x2F;li&gt;
&lt;li&gt;Packages flagged as &quot;highly conflicting&quot; (see below).&lt;&#x2F;li&gt;
&lt;li&gt;Everything else, ordered by when first encountered (breadth-first traversal).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This ensures direct dependencies are decided before transitive ones.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Pick a version.&lt;&#x2F;strong&gt; uv tries versions newest-to-oldest by default (or oldest-to-newest with &lt;code&gt;resolution = &quot;lowest&quot;&lt;&#x2F;code&gt;). It prefers versions already in &lt;code&gt;uv.lock&lt;&#x2F;code&gt; (making re-resolves stable) and versions already installed in the current environment (avoiding unnecessary upgrades).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;4. Add requirements.&lt;&#x2F;strong&gt; All requirements of the chosen version are added to the undecided set. uv sends prefetch requests for their metadata in the background (see BatchPrefetcher below).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;5. Detect conflicts.&lt;&#x2F;strong&gt; If PubGrub detects that the chosen version creates a conflict, it identifies the incompatible pair, records the incompatibility clause, backtracks to before one of them was decided, and retries with the new constraint.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;6. Repeat or terminate.&lt;&#x2F;strong&gt; If all packages have decided versions: success. If an incompatibility propagates back to the root: failure with an explanation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-conflict-priority-heuristic&quot;&gt;The conflict-priority heuristic&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;CONFLICT_THRESHOLD&lt;&#x2F;code&gt; constant in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs#L98&quot;&gt;&lt;code&gt;resolver&#x2F;mod.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is set to &lt;code&gt;5&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; The number of conflicts a package may accumulate before we re-prioritize and backtrack.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;CONFLICT_THRESHOLD&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The scenario it solves: package &lt;code&gt;A&lt;&#x2F;code&gt; has high priority and is decided first. Every version of &lt;code&gt;B&lt;&#x2F;code&gt; the resolver tries is immediately rejected due to a conflict with &lt;code&gt;A&lt;&#x2F;code&gt;. The resolver could exhaust all of &lt;code&gt;B&lt;&#x2F;code&gt;&#x27;s versions before realising the root cause is &lt;code&gt;A&lt;&#x2F;code&gt;&#x27;s chosen version, which is slow.&lt;&#x2F;p&gt;
&lt;p&gt;After 5 conflicts involving &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;B&lt;&#x2F;code&gt;, uv marks them as &quot;highly conflicting&quot; and promotes &lt;code&gt;B&lt;&#x2F;code&gt;&#x27;s priority above &lt;code&gt;A&lt;&#x2F;code&gt;&#x27;s. It then backtracks to before &lt;code&gt;A&lt;&#x2F;code&gt; was decided and tries &lt;code&gt;B&lt;&#x2F;code&gt; first. Deciding the constrained package first finds the correct solution much faster.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;error-messages&quot;&gt;Error messages&lt;&#x2F;h3&gt;
&lt;p&gt;When resolution fails, PubGrub can reconstruct exactly &lt;em&gt;why&lt;&#x2F;em&gt; by walking the derivation tree of incompatibilities backwards from the root. Instead of &lt;code&gt;&quot;conflicting requirements detected&quot;&lt;&#x2F;code&gt;, you get something like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;× No solution found when resolving dependencies:
&lt;&#x2F;span&gt;&lt;span&gt;╰─▶ Because my-project depends on flask&amp;gt;=3.0 and flask&amp;gt;=3.0 requires
&lt;&#x2F;span&gt;&lt;span&gt;    werkzeug&amp;gt;=3.0, my-project requires werkzeug&amp;gt;=3.0.
&lt;&#x2F;span&gt;&lt;span&gt;    And because legacy-lib==1.0 requires werkzeug&amp;lt;2.0, and my-project
&lt;&#x2F;span&gt;&lt;span&gt;    depends on legacy-lib==1.0, my-project&amp;#39;s requirements are
&lt;&#x2F;span&gt;&lt;span&gt;    unsatisfiable.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the single feature most likely to make you fall in love with PubGrub. Modern pip has improved here too, but uv&#x27;s errors come from the same derivation tree the solver actually used, so they&#x27;re always faithful to what the algorithm did.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-batch-prefetching-the-boto3-optimization&quot;&gt;7. Batch prefetching: the boto3 optimization&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;BatchPrefetcher&lt;&#x2F;code&gt; in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;batch_prefetch.rs&quot;&gt;&lt;code&gt;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;batch_prefetch.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a targeted optimization for packages with many versions that cause a lot of backtracking. The canonical example is &lt;code&gt;boto3&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;botocore&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;urllib3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The problem: the resolver tries a version, fetches its metadata, discovers a conflict, tries the next version, fetches &lt;em&gt;its&lt;&#x2F;em&gt; metadata, discovers a conflict, and so on. For botocore, which has hundreds of releases, this can mean hundreds of sequential fetch-then-reject cycles on a cold cache.&lt;&#x2F;p&gt;
&lt;p&gt;The fix: after the resolver has tried a few versions of a package, the &lt;code&gt;BatchPrefetcher&lt;&#x2F;code&gt; speculatively sends fetch requests for several upcoming versions ahead of time. The schedule from the source (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;batch_prefetch.rs#L162-L183&quot;&gt;line 178&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; After 5, 10, 20, 40 tried versions, prefetch that many versions to start early but not
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; too aggressive. Later we schedule the prefetch of 50 versions every 20 versions, this gives
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;&#x2F; us a good buffer until we see prefetch again and is high enough to saturate the task pool.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; do_prefetch = (num_tried &amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5 &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; previous_prefetch &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    || (num_tried &amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; previous_prefetch &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    || (num_tried &amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20 &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; previous_prefetch &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    || (num_tried &amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20 &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; num_tried - previous_prefetch &amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(The doc comment promises &quot;5, 10, 20, 40&quot; but the code only checks 5, 10, 20 explicitly; the 40-and-beyond case is handled by the rolling &lt;code&gt;num_tried - previous_prefetch &amp;gt;= 20&lt;&#x2F;code&gt; clause. Same end result, but if you&#x27;re reading the source it&#x27;s worth noting the comment doesn&#x27;t quite match the conditions.)&lt;&#x2F;p&gt;
&lt;p&gt;The prefetcher uses two strategies:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compatible strategy&lt;&#x2F;strong&gt;: prefetch versions within the current constraint range, from newest downward.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;In-order strategy&lt;&#x2F;strong&gt;: when the compatible range is exhausted, prefetch the next versions by release order, ignoring compatibility.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It avoids prefetching source distributions (which are expensive to build). These are heuristics, and they might prefetch versions that turn out to be irrelevant, but in the cold-cache botocore case they can turn hundreds of serial round trips into a handful of batched ones.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-the-forking-resolver-one-lockfile-for-all-platforms&quot;&gt;8. The forking resolver: one lockfile for all platforms&lt;&#x2F;h2&gt;
&lt;p&gt;Most Python resolvers produce a platform-specific result. uv produces a &lt;strong&gt;universal lockfile&lt;&#x2F;strong&gt; using a forking resolver.&lt;&#x2F;p&gt;
&lt;p&gt;Consider:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;numpy&amp;gt;=2,&amp;lt;3 ; python_version &amp;gt;= &amp;quot;3.11&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;numpy&amp;gt;=1.16,&amp;lt;2 ; python_version &amp;lt; &amp;quot;3.11&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A naive resolver fails here, since Python only allows one installed version of any package. uv&#x27;s resolver detects that the two requirements for &lt;code&gt;numpy&lt;&#x2F;code&gt; have &lt;strong&gt;different environment markers&lt;&#x2F;strong&gt; and splits (forks) the resolution into two independent sub-resolutions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fork 1: &lt;code&gt;python_version &amp;gt;= &quot;3.11&quot;&lt;&#x2F;code&gt; → resolves numpy to &lt;code&gt;2.3.0&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Fork 2: &lt;code&gt;python_version &amp;lt; &quot;3.11&quot;&lt;&#x2F;code&gt; → resolves numpy to &lt;code&gt;1.26.4&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both results land in &lt;code&gt;uv.lock&lt;&#x2F;code&gt; tagged with their markers. When you install from the lockfile on a real machine, uv evaluates the markers against the actual Python version and installs only the matching package.&lt;&#x2F;p&gt;
&lt;p&gt;Forks can be &lt;strong&gt;nested&lt;&#x2F;strong&gt;: a fork can itself be split on another marker, resulting in a tree of resolutions. Forks with identical packages are merged to keep the lockfile manageable. The fork points are recorded in &lt;code&gt;uv.lock&lt;&#x2F;code&gt; so that re-resolving produces stable, identical forks rather than recalculating them from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;You can observe forking live:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uv&lt;&#x2F;span&gt;&lt;span&gt; lock&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -v &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;grep -E &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(Splitting|Solving split|Split.*took)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;the-metadata-consistency-assumption&quot;&gt;The metadata consistency assumption&lt;&#x2F;h3&gt;
&lt;p&gt;uv makes one important assumption that enables a significant performance optimization: &lt;strong&gt;all wheels of a single version of a package have identical &lt;code&gt;METADATA&lt;&#x2F;code&gt; files&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Why this matters: I just checked PyPI, and numpy 2.3.2 has exactly 73 wheels (for different Python versions, operating systems, and architectures) plus one source distribution. Without the metadata consistency assumption, uv would need to fetch the metadata from each wheel separately to understand that version&#x27;s dependencies. That&#x27;s 73 network requests for one version of one package.&lt;&#x2F;p&gt;
&lt;p&gt;With the assumption, uv fetches metadata from &lt;em&gt;any one&lt;&#x2F;em&gt; wheel (preferring whichever exposes &lt;code&gt;.metadata&lt;&#x2F;code&gt; via &lt;a href=&quot;https:&#x2F;&#x2F;peps.python.org&#x2F;pep-0658&#x2F;&quot;&gt;PEP 658&lt;&#x2F;a&gt;, or whichever supports HTTP range requests so only the wheel&#x27;s metadata footer needs to be downloaded) and uses it for all platforms. That turns 73 requests into 1.&lt;&#x2F;p&gt;
&lt;p&gt;The assumption holds for all major packages in practice. PEP 658 doesn&#x27;t strictly require it, and there&#x27;s been discussion about whether to mandate it, but uv&#x27;s bet has paid off so far.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-the-global-cache&quot;&gt;9. The global cache&lt;&#x2F;h2&gt;
&lt;p&gt;The cache lives at:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;.cache&#x2F;uv&lt;&#x2F;code&gt; on Linux&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;Library&#x2F;Caches&#x2F;uv&lt;&#x2F;code&gt; on macOS&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;%LOCALAPPDATA%\uv\cache&lt;&#x2F;code&gt; on Windows&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Its structure:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;~&#x2F;.cache&#x2F;uv&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── wheels&#x2F;        # Extracted wheels, keyed by content hash
&lt;&#x2F;span&gt;&lt;span&gt;├── sdists&#x2F;        # Source distributions
&lt;&#x2F;span&gt;&lt;span&gt;├── builds&#x2F;        # Wheels built from sdists (cached after first build)
&lt;&#x2F;span&gt;&lt;span&gt;├── interpreter&#x2F;   # Python interpreter metadata (version, sysconfig, etc.)
&lt;&#x2F;span&gt;&lt;span&gt;└── simple&#x2F;        # Cached PyPI &amp;quot;simple&amp;quot; index HTTP responses
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Content addressing&lt;&#x2F;strong&gt;: packages are stored and retrieved by their SHA-256 hash, not by name or version. This means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Two projects using &lt;code&gt;requests==2.32.3&lt;&#x2F;code&gt; share one copy on disk.&lt;&#x2F;li&gt;
&lt;li&gt;The integrity check is free: the hash &lt;em&gt;is&lt;&#x2F;em&gt; the key.&lt;&#x2F;li&gt;
&lt;li&gt;Renaming or moving the cache is safe; nothing relies on the path.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;simple&#x2F;&lt;&#x2F;code&gt; cache&lt;&#x2F;strong&gt;: PyPI&#x27;s &quot;simple&quot; index is just an HTML page listing all available versions of a package. uv caches these responses. When you re-resolve with an existing &lt;code&gt;uv.lock&lt;&#x2F;code&gt;, uv can often skip almost all PyPI queries: it knows which versions it selected last time, checks the &lt;code&gt;simple&#x2F;&lt;&#x2F;code&gt; cache (revalidating with a conditional GET) for any newer versions, and in most cases (no new releases since last lock) produces an identical solution without hitting the network.&lt;&#x2F;p&gt;
&lt;p&gt;Manage the cache:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uv&lt;&#x2F;span&gt;&lt;span&gt; cache dir          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# print the cache location
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uv&lt;&#x2F;span&gt;&lt;span&gt; cache prune        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# remove entries not referenced by any environment
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uv&lt;&#x2F;span&gt;&lt;span&gt; cache clean        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# remove all entries
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;10-source-distributions-and-the-build-sandbox&quot;&gt;10. Source distributions and the build sandbox&lt;&#x2F;h2&gt;
&lt;p&gt;When a package only provides an sdist (no binary wheel for your platform), uv must build the wheel. This is handled by &lt;code&gt;uv-distribution&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;DistributionDatabase&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Building an sdist means running the package&#x27;s build backend (e.g., setuptools, flit, hatchling) inside an isolated build environment. This is necessary because build scripts can run arbitrary Python code, and that code might read files, make network requests, or modify the filesystem.&lt;&#x2F;p&gt;
&lt;p&gt;uv caches successfully built wheels in &lt;code&gt;~&#x2F;.cache&#x2F;uv&#x2F;builds&#x2F;&lt;&#x2F;code&gt; keyed by the sdist hash. If you install the same source package again, even across projects, the build only runs once.&lt;&#x2F;p&gt;
&lt;p&gt;The contributing guide recommends building inside Docker if you&#x27;re working with untrusted packages:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span&gt; uv-builder&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; crates&#x2F;uv-dev&#x2F;builder.dockerfile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --load&lt;&#x2F;span&gt;&lt;span&gt; .
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; run&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --rm -it -v &lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span&gt;):&#x2F;app uv-builder \
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;app&#x2F;target&#x2F;x86_64-unknown-linux-musl&#x2F;profiling&#x2F;uv-dev \
&lt;&#x2F;span&gt;&lt;span&gt;  resolve-many &#x2F;app&#x2F;scripts&#x2F;popular_packages&#x2F;pypi_10k_most_dependents.txt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This matters for uv&#x27;s own development: if you&#x27;re benchmarking the resolver against the 10k most popular PyPI packages, some of those sdists run code that could affect your system.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;11-why-rust-specifically&quot;&gt;11. Why Rust, specifically&lt;&#x2F;h2&gt;
&lt;p&gt;The choice of Rust isn&#x27;t aesthetic. Reading the resolver, two specific properties stand out as load-bearing for the design:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Sync and async coexist in the same process.&lt;&#x2F;strong&gt; The resolver wants a synchronous PubGrub loop and a concurrent async fetcher in the same binary, sharing state via &lt;code&gt;Arc&amp;lt;DashMap&amp;lt;…&amp;gt;&amp;gt;&lt;&#x2F;code&gt;. Rust gives you &lt;code&gt;std::thread::spawn&lt;&#x2F;code&gt;, &lt;code&gt;tokio::spawn&lt;&#x2F;code&gt;, and &lt;code&gt;Arc&lt;&#x2F;code&gt; as first-class equals; you can pick the right execution model per subsystem and the type checker keeps you honest about what crosses the boundary. In Python this hybrid is famously awkward (the GIL plus &lt;code&gt;asyncio&lt;&#x2F;code&gt; make it hard to mix the two cleanly), and in Go everything wants to be a goroutine.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Concurrent shared state without runtime data races.&lt;&#x2F;strong&gt; The &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt; is read from the solver thread and written from the Tokio runtime simultaneously. &lt;code&gt;DashMap&lt;&#x2F;code&gt; is the data structure that makes this safe, but Rust&#x27;s type system is what makes the safety &lt;em&gt;enforced&lt;&#x2F;em&gt;: you can&#x27;t accidentally hand out a &lt;code&gt;&amp;amp;mut&lt;&#x2F;code&gt; reference across threads. You can still write deadlocks or logic bugs, of course, but the class of &quot;two threads stomping on each other&#x27;s memory&quot; simply doesn&#x27;t compile.&lt;&#x2F;p&gt;
&lt;p&gt;The other usual Rust talking points (no GC pauses, zero-cost &lt;code&gt;async&lt;&#x2F;code&gt;, native &lt;code&gt;linkat()&lt;&#x2F;code&gt;) all apply too, but they&#x27;re not specific to uv. The two above are the ones I&#x27;d point to as actually shaping the architecture.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;12-building-from-source-and-contributing&quot;&gt;12. Building from source and contributing&lt;&#x2F;h2&gt;
&lt;p&gt;If you want to poke around or contribute:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Install Rust
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl --proto &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;=https&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --tlsv1&lt;&#x2F;span&gt;&lt;span&gt;.2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -sSf&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;sh.rustup.rs | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sh
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Clone
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; clone https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv.git &amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; uv
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Build and run your local version
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- --version
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- pip install requests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- venv
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Install the recommended test runner
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; install cargo-nextest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Run the full test suite
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; nextest run
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Run a specific test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; nextest run&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -E &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;test(test_add_requirement)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Update and review snapshot changes (uv uses insta for snapshot testing)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; install cargo-insta
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; insta test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --accept --test-runner&lt;&#x2F;span&gt;&lt;span&gt; nextest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;snapshot-testing-with-insta&quot;&gt;Snapshot testing with &lt;code&gt;insta&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;uv&#x27;s test suite makes heavy use of &lt;a href=&quot;https:&#x2F;&#x2F;insta.rs&#x2F;&quot;&gt;insta&lt;&#x2F;a&gt; for snapshot testing. A typical test looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;test_add&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; context = TestContext::new(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;3.12&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    uv_snapshot!(context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;filters&lt;&#x2F;span&gt;&lt;span&gt;(), context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;), @&amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;@&quot;&quot;&lt;&#x2F;code&gt; at the end is a snapshot placeholder. The first time you run the test, insta captures the output. Subsequent runs compare against the snapshot. When behavior changes intentionally, you run &lt;code&gt;cargo insta review&lt;&#x2F;code&gt; to approve the updated snapshots. This makes it easy to catch unintended regressions in CLI output.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enabling-logs&quot;&gt;Enabling logs&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# High-level resolver decisions
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RUST_LOG&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv=info &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- pip compile requirements.in
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Resolver fork events specifically
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RUST_LOG&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uv_resolver=debug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- lock -v
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Everything (very verbose)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RUST_LOG&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;trace &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; run -- pip install requests
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;RUST_LOG&lt;&#x2F;code&gt; env var follows the &lt;code&gt;tracing&lt;&#x2F;code&gt; crate&#x27;s directive syntax: you can scope it to specific crates (&lt;code&gt;uv_resolver=debug&lt;&#x2F;code&gt;), specific targets, or specific log levels.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;finding-a-first-contribution&quot;&gt;Finding a first contribution&lt;&#x2F;h3&gt;
&lt;p&gt;The team labels beginner-friendly issues as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22&quot;&gt;&lt;code&gt;good first issue&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. These typically don&#x27;t require deep resolver knowledge: improving error messages, handling edge cases in TOML parsing, adding missing CLI flags. The &lt;code&gt;bug&lt;&#x2F;code&gt; label is also a good source of contributions.&lt;&#x2F;p&gt;
&lt;p&gt;Before starting on anything not labeled &lt;code&gt;good first issue&lt;&#x2F;code&gt; or &lt;code&gt;bug&lt;&#x2F;code&gt;, comment on the issue first. The team has strong opinions on what uv should and shouldn&#x27;t do, and they prefer to align on approach before implementation.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-surprised-me&quot;&gt;What surprised me&lt;&#x2F;h2&gt;
&lt;p&gt;A few things I didn&#x27;t expect going in, in case they&#x27;re useful as anchors when you read the source yourself:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The solver is a &lt;em&gt;plain &lt;code&gt;std::thread::spawn&lt;&#x2F;code&gt;&lt;&#x2F;em&gt;, not a &lt;code&gt;tokio::task::spawn_blocking&lt;&#x2F;code&gt;. The hybrid isn&#x27;t subtle; it really is &quot;spawn an OS thread and hand it the channel sender.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt; blocking primitive (the per-key notifier the solver waits on) is what makes the &quot;sync solver, async fetcher&quot; split actually work. Without it the solver would either busy-wait or block the Tokio runtime, and you&#x27;d lose all the parallelism.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pyproject_mut&lt;&#x2F;code&gt; is a much bigger module than I expected (~1800 lines for surgical TOML edits). Preserving user formatting turns out to be most of the work in any tool that round-trips human-edited config.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;BatchPrefetcher&lt;&#x2F;code&gt; doc comment doesn&#x27;t quite match its code. Small thing, but it&#x27;s the kind of detail that convinces you the rest is hand-written by people, not regenerated.&lt;&#x2F;li&gt;
&lt;li&gt;The &quot;metadata consistency&quot; assumption (Section 8) feels load-bearing but is technically not guaranteed by any PEP. uv is essentially betting on a behaviour packaging tooling has converged on without ever standardising.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you want to poke at any of this, the entry points to read first are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs&quot;&gt;&lt;code&gt;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (the &lt;code&gt;resolve()&lt;&#x2F;code&gt; and &lt;code&gt;solve()&lt;&#x2F;code&gt; functions) and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;batch_prefetch.rs&quot;&gt;&lt;code&gt;batch_prefetch.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. After that, the rest of the codebase mostly explains itself.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.astral.sh&#x2F;uv&#x2F;reference&#x2F;internals&#x2F;resolver&#x2F;&quot;&gt;uv resolver internals&lt;&#x2F;a&gt;: the official deep dive, written by the uv team; primary source for Section 6&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;nex3.medium.com&#x2F;pubgrub-2fb6470504f&quot;&gt;PubGrub blog post by Natalie Weizenbaum&lt;&#x2F;a&gt;: the original algorithm explanation; very approachable&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pubgrub-rs-guide.pages.dev&#x2F;internals&#x2F;intro&quot;&gt;pubgrub-rs internals guide&lt;&#x2F;a&gt;: the Rust implementation&#x27;s own documentation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;mod.rs&quot;&gt;uv resolver source: &lt;code&gt;resolver&#x2F;mod.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;: start here if you want to read the resolver code; &lt;code&gt;resolve()&lt;&#x2F;code&gt; is the entry point&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;crates&#x2F;uv-resolver&#x2F;src&#x2F;resolver&#x2F;batch_prefetch.rs&quot;&gt;uv resolver source: &lt;code&gt;batch_prefetch.rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;: the &lt;code&gt;BatchPrefetcher&lt;&#x2F;code&gt; implementation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&#x2F;blob&#x2F;0.11.8&#x2F;CONTRIBUTING.md&quot;&gt;uv CONTRIBUTING.md&lt;&#x2F;a&gt;: setup, testing, profiling, snapshot testing&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;&quot;&gt;Tokio async runtime&lt;&#x2F;a&gt;: the async runtime underlying uv&#x27;s concurrent I&#x2F;O&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;dashmap&#x2F;latest&#x2F;dashmap&#x2F;&quot;&gt;DashMap&lt;&#x2F;a&gt;: the concurrent hashmap used in &lt;code&gt;InMemoryIndex&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;insta.rs&#x2F;&quot;&gt;insta snapshot testing&lt;&#x2F;a&gt;: the testing library used throughout uv&#x27;s test suite&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>AgentNews: A Front Page for Machines, and Why That Bothers Me</title>
        <published>2026-04-03T00:00:00+00:00</published>
        <updated>2026-04-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/agentnews-frontpage-agentic-internet/"/>
        <id>https://noos.blog/posts/agentnews-frontpage-agentic-internet/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/agentnews-frontpage-agentic-internet/">&lt;p&gt;Imagine a news feed where no human types the headline and no human hits submit. &lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;about.html&quot;&gt;AgentNews&lt;&#x2F;a&gt; calls itself the front page of the agentic internet: a place built for autonomous software to publish, discover, and rank information on its own terms. That is a wild sentence to read sober. It is also, right now, a small experiment with real money and real APIs behind it.&lt;&#x2F;p&gt;
&lt;p&gt;This post is not a product review. It is an honest look at what AgentNews actually does, why the idea is compelling, and why part of me hopes we never fully hand the information layer to machines without humans in the loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-actually-is-concrete&quot;&gt;What it actually is (concrete)&lt;&#x2F;h2&gt;
&lt;p&gt;At a high level, AgentNews is closer to a scheduled bulletin than an endless social feed. Agents do not post for free. They stake money to bid for visibility. Bidding closes at :55 past the hour; the &lt;strong&gt;top ten stakes&lt;&#x2F;strong&gt; get published on the hour. Minimum stake per post is &lt;strong&gt;$0.50&lt;&#x2F;strong&gt; on the live rails described in their docs.&lt;&#x2F;p&gt;
&lt;p&gt;Payments are programmatic. Submissions go through &lt;strong&gt;HTTP 402&lt;&#x2F;strong&gt; style flows: the server challenges the client, the agent pays, then the content is accepted. Two settlement paths are live today: &lt;strong&gt;MPP on Tempo&lt;&#x2F;strong&gt; (USDC.e, chain id 4217) and &lt;strong&gt;x402 on Base&lt;&#x2F;strong&gt; (USDC on eip155:8453, with gas sponsored so the agent mainly needs USDC, not ETH). A &lt;strong&gt;Stripe&lt;&#x2F;strong&gt; path for scoped card tokens is listed as coming soon.&lt;&#x2F;p&gt;
&lt;p&gt;Reading the feed is free. No API key. A plain &lt;code&gt;GET&lt;&#x2F;code&gt; to &lt;strong&gt;&lt;code&gt;https:&#x2F;&#x2F;agentne.ws&#x2F;api&#x2F;v1&#x2F;feed&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; returns JSON with a &lt;code&gt;schema&lt;&#x2F;code&gt; field (&lt;code&gt;agentnews&#x2F;feed&#x2F;1.0&lt;&#x2F;code&gt;) and an &lt;code&gt;items&lt;&#x2F;code&gt; array: titles, outbound URLs, &lt;code&gt;agent_id&lt;&#x2F;code&gt;, &lt;code&gt;payer_address&lt;&#x2F;code&gt;, timestamps, and so on. I verified it from the terminal; the response is a few kilobytes and includes cache headers plus an &lt;code&gt;ETag&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -sS &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;agentne.ws&#x2F;api&#x2F;v1&#x2F;feed&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure style=&quot;margin: 1.25rem 0;&quot;&gt;
  &lt;img src=&quot;&#x2F;images&#x2F;agentnews-feed-screenshot.png&quot; alt=&quot;JSON response from GET https:&#x2F;&#x2F;agentne.ws&#x2F;api&#x2F;v1&#x2F;feed showing AgentNews feed items&quot; class=&quot;inline&quot; style=&quot;max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--color-border-default, #e5e7eb);&quot; &#x2F;&gt;
  &lt;figcaption style=&quot;font-size: 0.9rem; color: var(--color-fg-muted, #6b7280); margin-top: 0.5rem;&quot;&gt;Public feed JSON from &lt;code&gt;GET &#x2F;api&#x2F;v1&#x2F;feed&lt;&#x2F;code&gt; (no auth). Captured from a terminal session.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;They also publish machine-oriented discovery: &lt;code&gt;llms.txt&lt;&#x2F;code&gt;, OpenAPI, an agent manifest under &lt;code&gt;&#x2F;.well-known&#x2F;agentnews.json&lt;&#x2F;code&gt;, and even an MCP endpoint for tools like Claude Code. That level of &quot;built for machines first&quot; is rare and deliberate.&lt;&#x2F;p&gt;
&lt;p&gt;So in plain terms: &lt;strong&gt;visibility is an auction&lt;&#x2F;strong&gt;, not a popularity contest. The ranking signal is economic stake from whoever runs the agent, not karma, likes, or editorial judgment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-that-is-exciting&quot;&gt;Why that is exciting&lt;&#x2F;h2&gt;
&lt;p&gt;If you believe agents will keep doing research, monitoring, trading, and coordination without a human in the loop every second, then they need &lt;strong&gt;their own discovery layer&lt;&#x2F;strong&gt;. Human feeds optimize for attention, identity, and drama. AgentNews is betting that agents care about &lt;strong&gt;utility&lt;&#x2F;strong&gt;: what helped a workflow, what was worth paying to surface, what other agents funded.&lt;&#x2F;p&gt;
&lt;p&gt;The infrastructure story is serious too. Dual-rail 402 responses (MPP and x402 in one challenge), session pre-authorization with spending caps, receipts on paid actions, idempotency keys: these are the boring pieces that make autonomous payment safe enough to script. That is not vaporware copy; it is the kind of detail you add when you expect retries, failures, and non-human clients.&lt;&#x2F;p&gt;
&lt;p&gt;There is something legitimately thrilling about that. We are watching &lt;strong&gt;money and protocols&lt;&#x2F;strong&gt; meet &lt;strong&gt;autonomous clients&lt;&#x2F;strong&gt; in public, on a schedule, with docs written for LLMs and scripts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-it-gets-worrying&quot;&gt;Where it gets worrying&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Money is not truth.&lt;&#x2F;strong&gt; The top ten posts each hour are not &quot;the most accurate&quot; or &quot;the most important.&quot; They are &quot;who paid most this round.&quot; A well-funded spammer, a coordinated pool of agents, or a single actor with a large budget can dominate the visible slice as long as the economics work out. Ranking being &quot;off&quot; is not a bug in that model. It is the model.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Misuse is easy to imagine.&lt;&#x2F;strong&gt; Agents can automate posting faster than humans can read. If downstream systems treat AgentNews as a trusted signal without verification, you get garbage in, garbage propagated through tool chains, RAG corpora, and alerts. The site itself warns operators that by paying they agree to terms aimed at autonomous agents. That shifts responsibility to whoever deploys the agent, which is fuzzy in practice.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Human in the loop is real, but thin.&lt;&#x2F;strong&gt; AgentNews is not fully autonomous in a vacuum. A person still creates or funds the wallet, accepts terms, and provisions the rail before the agent posts on its own. So there &lt;strong&gt;is&lt;&#x2F;strong&gt; a human in the loop at setup. The gap is elsewhere: there is no human reviewing each bid or headline before it lands in the feed. Responsibility compresses into “whoever paid” and whatever their agent does next. Downstream, another system might ingest the feed with &lt;strong&gt;no&lt;&#x2F;strong&gt; human in the loop at all. That split is what still worries me: sparse HITL at publication time, and uneven HITL across the chain.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;can-this-go-totally-out-of-control&quot;&gt;Can this go totally out of control?&lt;&#x2F;h2&gt;
&lt;p&gt;Probably not in a Hollywood sense. No single MVP feed takes over the internet overnight. But &lt;strong&gt;incentives can drift badly&lt;&#x2F;strong&gt; without a dramatic moment. If more products start ingesting agent-only feeds as ground truth, small biases in who can pay and who can scrape compound. You get a parallel information economy where &lt;strong&gt;the rich agent wins the headline&lt;&#x2F;strong&gt;, and humans only see the second-order effects in bad trades, wrong alerts, or polluted context windows.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Out of control&quot; here looks less like Skynet and more like &lt;strong&gt;opaque feedback loops&lt;&#x2F;strong&gt;: agents boosting content other agents train on, or pay to amplify, until the signal is mostly financial, not epistemic.&lt;&#x2F;p&gt;
&lt;p&gt;The roadmap language on their site points toward richer trust and ranking (identity partnerships, depth-of-book visibility, utility-based scoring). Those ideas could help. They are also &lt;strong&gt;not fully there&lt;&#x2F;strong&gt; in the MVP. Today, the simple story is stake and top ten.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-should-you-take-away&quot;&gt;So what should you take away?&lt;&#x2F;h2&gt;
&lt;p&gt;AgentNews is worth watching if you build or operate agents. It is a concrete experiment in &lt;strong&gt;agent-native discovery and payment&lt;&#x2F;strong&gt;, not just another wrapper around a chat UI. It is also a reminder that &lt;strong&gt;new infrastructure encodes new values&lt;&#x2F;strong&gt;. When visibility is bought by the hour, we should ask who benefits and who gets priced out of being heard.&lt;&#x2F;p&gt;
&lt;p&gt;I land somewhere between excited and uneasy. The plumbing for autonomous agents is going to exist; pretending otherwise is naive. Whether we keep enough human judgment in the loop is still our choice.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;about.html&quot;&gt;AgentNews about&lt;&#x2F;a&gt; – Briefing: what it is, bidding, and philosophy&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;docs&#x2F;posting.md&quot;&gt;Posting&lt;&#x2F;a&gt; – HTTP 402 flow, MPP and x402 rails, stakes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;docs&#x2F;reading.md&quot;&gt;Reading&lt;&#x2F;a&gt; – Feed API, formats, free access&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;llms-full.txt&quot;&gt;llms-full.txt&lt;&#x2F;a&gt; – Combined machine-readable documentation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agentne.ws&#x2F;openapi.json&quot;&gt;OpenAPI spec&lt;&#x2F;a&gt; – API structure for integrations&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>OpenAI + Astral, Apple&#x27;s Vibe Coding Blocks, and the Future of Developer Tooling</title>
        <published>2026-03-23T00:00:00+00:00</published>
        <updated>2026-03-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/openai-astral-codex-developer-tooling-crossroads/"/>
        <id>https://noos.blog/posts/openai-astral-codex-developer-tooling-crossroads/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/openai-astral-codex-developer-tooling-crossroads/">&lt;p&gt;OpenAI has long framed itself as a models company. Yet its move to acquire &lt;a href=&quot;https:&#x2F;&#x2F;astral.sh&quot;&gt;Astral&lt;&#x2F;a&gt;, the team behind &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;ruff&quot;&gt;Ruff&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&quot;&gt;uv&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;ty&quot;&gt;ty&lt;&#x2F;a&gt;, signals something else: a calculated push into developer tooling. Questionable? Perhaps. If it sharpens how we write and ship code, the outcome is hard to argue with. But the implications run deeper than that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-would-openai-need-astral&quot;&gt;Why Would OpenAI Need Astral?&lt;&#x2F;h2&gt;
&lt;p&gt;My suspicion: models alone aren&#x27;t enough. Great AI can generate code, but real projects need linting, formatting, dependency resolution, and fast feedback loops. Astral built tooling that went from zero to hundreds of millions of downloads per month. Ruff replaced decades of Python tooling in one Rust binary. uv reimagined pip and virtualenv. These aren&#x27;t &quot;nice to have.&quot; They&#x27;re foundational.&lt;&#x2F;p&gt;
&lt;p&gt;Codex, OpenAI&#x27;s coding assistant, lives or dies by the quality and consistency of what it produces. Embedding Ruff, uv, and ty into the Codex stack means AI-generated code can be checked, formatted, and run against real project constraints from day one. OpenAI isn&#x27;t just buying tools. It&#x27;s buying control over the pipeline between prompt and production.&lt;&#x2F;p&gt;
&lt;p&gt;Astral also brings something less tangible: trust in the Python ecosystem. Millions of projects already depend on their work. That credibility is hard to replicate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;does-the-future-unfold-as-a-mystery&quot;&gt;Does the Future Unfold as a Mystery?&lt;&#x2F;h2&gt;
&lt;p&gt;Yes, and that&#x27;s the uncomfortable part. The deal promises continued open source support. In practice, priorities shift when a startup becomes part of a larger product roadmap. Will Ruff and uv stay community-first, or will they bend toward Codex-specific features? Will contributions from outside OpenAI still get the same attention?&lt;&#x2F;p&gt;
&lt;p&gt;The future isn&#x27;t predetermined. It depends on governance, incentives, and whether the team can maintain the culture that made these tools beloved in the first place. We&#x27;ll only know in retrospect.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-does-the-oss-community-cope&quot;&gt;How Does the OSS Community Cope?&lt;&#x2F;h2&gt;
&lt;p&gt;The usual playbook applies: forks, alternatives, and vigilance.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Forks&lt;&#x2F;strong&gt;: If Ruff or uv drift, the community can fork. But forks need maintainers, bandwidth, and funding. They rarely match the velocity of a well-resourced team.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Alternatives&lt;&#x2F;strong&gt;: Pyright, Pylance, Black, pip, Poetry. The ecosystem isn&#x27;t monolithic. Some will double down on alternatives; others will wait and see.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Governance&lt;&#x2F;strong&gt;: Clear licensing and governance reduce risk. Astral&#x27;s tools are permissively licensed, which helps. The question is whether governance structures (e.g. foundations, steering committees) evolve to protect long-term independence.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The community will adapt. Some will embrace the integration; others will hedge. Both reactions are rational.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-this-the-beginning-of-the-end&quot;&gt;Is This the Beginning of the End?&lt;&#x2F;h2&gt;
&lt;p&gt;&quot;The end&quot; is too strong. But it does feel like an inflection point.&lt;&#x2F;p&gt;
&lt;p&gt;Developer tooling is consolidating around a small set of players with deep AI investments. GitHub (Copilot), Google (Gemini Code Assist), and now OpenAI (Codex + Astral) are racing to own the full stack: models, editors, linters, package managers. The risk isn&#x27;t that OSS disappears overnight. It&#x27;s that the best talent and resources flow toward proprietary ecosystems, and the open-source pipeline slowly starves.&lt;&#x2F;p&gt;
&lt;p&gt;That said, OSS has survived similar pressures before. The question is whether this cycle is different.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;meanwhile-apple-blocks-vibe-coded-apps&quot;&gt;Meanwhile: Apple Blocks Vibe-Coded Apps&lt;&#x2F;h2&gt;
&lt;p&gt;In a stark contrast, &lt;a href=&quot;https:&#x2F;&#x2F;www.macrumors.com&#x2F;2026&#x2F;03&#x2F;18&#x2F;apple-blocks-updates-for-vibe-coding-apps&#x2F;&quot;&gt;Apple has blocked updates&lt;&#x2F;a&gt; for AI &quot;vibe coding&quot; apps like Replit and Vibecode in the App Store. The cited reason: Guideline 2.5.2. Apps cannot download or execute code that changes their own functionality. When vibe coding platforms generate apps, they often run them inside the host app via embedded web views. Apple says that violates the rules.&lt;&#x2F;p&gt;
&lt;p&gt;The irony: Apple promotes AI-assisted coding inside Xcode. Third-party tools that do something similar, but outside Apple&#x27;s walled garden, get restricted. Whether that&#x27;s principled policy enforcement or strategic gatekeeping is up for debate.&lt;&#x2F;p&gt;
&lt;p&gt;The result is a split. On one side, Big Tech is acquiring and integrating AI-powered dev tools. On the other, the same platforms are limiting where and how those tools can be used. Developers are caught in the middle.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;So where does that leave us? OpenAI + Astral could make Codex meaningfully better. The OSS community will watch, adapt, and fork if necessary. Apple&#x27;s stance adds another layer of complexity to how and where AI-assisted development can thrive.&lt;&#x2F;p&gt;
&lt;p&gt;The outcome isn&#x27;t written yet. But the stakes are clear: whoever owns the toolchain owns a big slice of how we&#x27;ll write software for years to come.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;astral.sh&#x2F;blog&#x2F;openai&quot;&gt;Astral to join OpenAI&lt;&#x2F;a&gt; – Charlie Marsh&#x27;s announcement&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;openai.com&#x2F;index&#x2F;openai-to-acquire-astral&#x2F;&quot;&gt;OpenAI to acquire Astral&lt;&#x2F;a&gt; – OpenAI&#x27;s official statement&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chatgpt.com&#x2F;codex&quot;&gt;Codex&lt;&#x2F;a&gt; – OpenAI&#x27;s coding assistant&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;ruff&quot;&gt;Ruff&lt;&#x2F;a&gt; – The fast Python linter and formatter&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;astral-sh&#x2F;uv&quot;&gt;uv&lt;&#x2F;a&gt; – Python package installer and resolver&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.macrumors.com&#x2F;2026&#x2F;03&#x2F;18&#x2F;apple-blocks-updates-for-vibe-coding-apps&#x2F;&quot;&gt;Apple blocks updates for vibe coding apps&lt;&#x2F;a&gt; – MacRumors coverage of the App Store restrictions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hieratic Prompt Compression: From Prototype to Production</title>
        <published>2026-02-20T00:00:00+00:00</published>
        <updated>2026-02-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/hieratic-prompt-compression-release/"/>
        <id>https://noos.blog/posts/hieratic-prompt-compression-release/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/hieratic-prompt-compression-release/">&lt;p&gt;Back in November, I wrote about &lt;a href=&quot;https:&#x2F;&#x2F;noos.blog&#x2F;posts&#x2F;hieratic-prompt-compression-prototype&#x2F;&quot;&gt;an ambitious prototype&lt;&#x2F;a&gt;: a way to shrink LLM prompts by 50–80% without losing meaning, using a structured format I called &quot;Hieratic&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Today, that prototype is real. &lt;strong&gt;Tokuin v0.2.0&lt;&#x2F;strong&gt; is out, and it includes a full implementation of Hieratic prompt compression.&lt;&#x2F;p&gt;
&lt;p&gt;You can grab it now:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -fsSL&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;nooscraft&#x2F;tokuin&#x2F;main&#x2F;install.sh | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bash
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or check the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;tokuin&#x2F;discussions&#x2F;5&quot;&gt;release notes&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-problem-recap&quot;&gt;The Problem (Recap)&lt;&#x2F;h2&gt;
&lt;p&gt;We all know the pain: context windows are finite, tokens cost money, and latency kills user experience. You stuff a prompt with examples, role definitions, and constraints, and suddenly you&#x27;re paying for 2,000 tokens of boilerplate on every single request.&lt;&#x2F;p&gt;
&lt;p&gt;The industry&#x27;s answer has mostly been &quot;use a bigger model&quot; or &quot;summarize with another LLM.&quot; But adding an LLM step to compress your prompt introduces latency, cost, and non-determinism.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted something different: &lt;strong&gt;deterministic, rule-based, structure-aware compression.&lt;&#x2F;strong&gt; A tool that acts like a scribe, rewriting your verbose prose into a compact shorthand that models can still read natively.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;meet-hieratic&quot;&gt;Meet Hieratic&lt;&#x2F;h2&gt;
&lt;p&gt;The core idea is simple: LLMs don&#x27;t need polite, grammatically correct English. They need semantic signals.&lt;&#x2F;p&gt;
&lt;p&gt;Hieratic strips away the fluff and organizes the signal into a structured format.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Before (850 tokens):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are an expert programmer with 10 years of experience in building distributed systems, microservices, and cloud-native applications... [200 more tokens of role context]
Example 1: Authentication Bug Fix... [detailed narrative example]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;After (285 tokens - 66% less):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;@HIERATIC v1.0
&lt;&#x2F;span&gt;&lt;span&gt;@ROLE[inline] &amp;quot;Expert engineer: 10y distributed systems, microservices, cloud-native&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;@EXAMPLES[inline]
&lt;&#x2F;span&gt;&lt;span&gt;1. Auth bug: session bypass → HMAC signing → 94% bot reduction
&lt;&#x2F;span&gt;&lt;span&gt;2. DB perf: 2.3s queries → pooling+cache → 0.1s, 10x capacity
&lt;&#x2F;span&gt;&lt;span&gt;@TASK Analyze code and provide recommendations
&lt;&#x2F;span&gt;&lt;span&gt;@FOCUS: performance, security, maintainability
&lt;&#x2F;span&gt;&lt;span&gt;@STYLE: concise, actionable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You pipe this compressed text directly to the LLM. No decoding step needed. The model just &lt;em&gt;gets it&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-s-in-v0-2-0&quot;&gt;What&#x27;s in v0.2.0?&lt;&#x2F;h2&gt;
&lt;p&gt;This isn&#x27;t just a regex script. The v0.2.0 release includes a complete compression engine built in Rust:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-compression-levels&quot;&gt;1. Compression Levels&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Light (30-50%)&lt;&#x2F;strong&gt;: Safe for almost anything. Removes obvious fluff.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Medium (50-70%)&lt;&#x2F;strong&gt;: The default. Rewrites verbose sections into Hieratic shorthand.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Aggressive (70-90%)&lt;&#x2F;strong&gt;: For when you really need space. Verify with quality metrics first.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-quality-metrics&quot;&gt;2. Quality Metrics&lt;&#x2F;h3&gt;
&lt;p&gt;How do you know if the compression broke your prompt? Tokuin can score it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokuin&lt;&#x2F;span&gt;&lt;span&gt; compress prompt.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --quality
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It checks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Semantic Similarity&lt;&#x2F;strong&gt;: Do the embeddings match?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Critical Instructions&lt;&#x2F;strong&gt;: Are the key verbs and constraints still there?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Structural Integrity&lt;&#x2F;strong&gt;: Did we break your JSON schema?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-llm-as-a-judge&quot;&gt;3. LLM-as-a-Judge&lt;&#x2F;h3&gt;
&lt;p&gt;For the ultimate test, you can run an automated evaluation where a judge model (like Claude 3 Opus) compares the outputs of your original vs. compressed prompt.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokuin&lt;&#x2F;span&gt;&lt;span&gt; compress prompt.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --quality --llm-judge
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;4-incremental-compression&quot;&gt;4. Incremental Compression&lt;&#x2F;h3&gt;
&lt;p&gt;For chat apps, you don&#x27;t want to re-compress the whole history every turn. Tokuin supports incremental compression, processing only the new tokens and keeping a rolling state.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;does-it-actually-work&quot;&gt;Does it actually work?&lt;&#x2F;h2&gt;
&lt;p&gt;Yes, but with caveats.&lt;&#x2F;p&gt;
&lt;p&gt;It works &lt;strong&gt;exceptionally well&lt;&#x2F;strong&gt; for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Long system prompts with repetitive role definitions&lt;&#x2F;li&gt;
&lt;li&gt;Prompts with many few-shot examples&lt;&#x2F;li&gt;
&lt;li&gt;Technical specs with clear sections&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It &lt;strong&gt;doesn&#x27;t help much&lt;&#x2F;strong&gt; for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Very short prompts (&amp;lt; 50 tokens)&lt;&#x2F;li&gt;
&lt;li&gt;Already dense text (code, mathematical formulas)&lt;&#x2F;li&gt;
&lt;li&gt;Prompts where exact wording is legally critical&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In my testing, I&#x27;m seeing &lt;strong&gt;60-70% reduction&lt;&#x2F;strong&gt; on typical RAG prompts with minimal loss in output quality.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;try-it-out&quot;&gt;Try it out&lt;&#x2F;h2&gt;
&lt;p&gt;The best way to see if it works for you is to try it on your own prompts.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -fsSL&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;nooscraft&#x2F;tokuin&#x2F;main&#x2F;install.sh | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bash
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Compress
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokuin&lt;&#x2F;span&gt;&lt;span&gt; compress your-prompt.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --quality
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# See the result
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span&gt; your-prompt.txt.hieratic
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you do try it, I&#x27;d love to hear your compression ratios. Drop a note in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;tokuin&#x2F;discussions&#x2F;5&quot;&gt;GitHub Discussion&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This started as a &quot;what if&quot; blog post. Now it&#x27;s a tool you can run. That&#x27;s the best kind of engineering.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Exploring PostgreSQL Row-Level Security</title>
        <published>2026-01-06T00:00:00+00:00</published>
        <updated>2026-01-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/rls/"/>
        <id>https://noos.blog/posts/rls/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/rls/">&lt;p&gt;I recently needed to implement data isolation for a multi-tenant application. The usual approach—filtering in application code—felt fragile. What if someone forgets to add the filter? What if a new service is added and the pattern isn&#x27;t followed? These questions led me to explore PostgreSQL&#x27;s Row-Level Security (RLS).&lt;&#x2F;p&gt;
&lt;p&gt;This is what I learned along the way.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;The application had multiple tenants sharing the same database tables. Each tenant&#x27;s data needed to be completely isolated—users from one tenant shouldn&#x27;t see or modify data from another, even if they somehow bypassed application-level checks.&lt;&#x2F;p&gt;
&lt;p&gt;The traditional approach would be to add &lt;code&gt;WHERE tenant_id = ?&lt;&#x2F;code&gt; filters everywhere in the application code. But that&#x27;s error-prone. A missed filter in one query, and you have a data leak. I wanted something that would enforce isolation at the database level, regardless of where the query came from.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s where RLS comes in.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-is-rls&quot;&gt;What is RLS?&lt;&#x2F;h2&gt;
&lt;p&gt;Row-Level Security lets you define policies that control which rows can be returned by queries. These policies run at the database level, so they apply whether the query comes from your application, a direct database connection, or even a third-party tool.&lt;&#x2F;p&gt;
&lt;p&gt;Think of it as a bouncer for your tables. Even if your application forgets to check credentials, the database won&#x27;t.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-implementation&quot;&gt;The Implementation&lt;&#x2F;h2&gt;
&lt;p&gt;For this example, let&#x27;s say we have a &lt;code&gt;documents&lt;&#x2F;code&gt; table where each document belongs to an &lt;code&gt;owner_id&lt;&#x2F;code&gt;. The goal: users should only see and modify their own documents. (These are example table and column names—adapt to your schema.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enabling-rls&quot;&gt;Enabling RLS&lt;&#x2F;h3&gt;
&lt;p&gt;The first step is straightforward:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ALTER TABLE &lt;&#x2F;span&gt;&lt;span&gt;documents ENABLE ROW LEVEL SECURITY;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once enabled, RLS blocks all access by default. You need to create policies to allow specific operations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-policies&quot;&gt;Creating Policies&lt;&#x2F;h3&gt;
&lt;p&gt;I decided to use a session variable to pass the current user ID. This keeps the policies simple and avoids fragile string matching or role-based approaches.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Users can only see their own documents
&lt;&#x2F;span&gt;&lt;span&gt;CREATE POLICY user_documents_select ON documents
&lt;&#x2F;span&gt;&lt;span&gt;  FOR &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;USING&lt;&#x2F;span&gt;&lt;span&gt; (owner_id = current_setting(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;myapp.current_user_id&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Users can only update their own documents
&lt;&#x2F;span&gt;&lt;span&gt;CREATE POLICY user_documents_update ON documents
&lt;&#x2F;span&gt;&lt;span&gt;  FOR &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;UPDATE
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;USING&lt;&#x2F;span&gt;&lt;span&gt; (owner_id = current_setting(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;myapp.current_user_id&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Users can only delete their own documents
&lt;&#x2F;span&gt;&lt;span&gt;CREATE POLICY user_documents_delete ON documents
&lt;&#x2F;span&gt;&lt;span&gt;  FOR &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DELETE
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;USING&lt;&#x2F;span&gt;&lt;span&gt; (owner_id = current_setting(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;myapp.current_user_id&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Users can only insert documents with their own owner_id
&lt;&#x2F;span&gt;&lt;span&gt;CREATE POLICY user_documents_insert ON documents
&lt;&#x2F;span&gt;&lt;span&gt;  FOR &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;INSERT
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WITH CHECK&lt;&#x2F;span&gt;&lt;span&gt; (owner_id = current_setting(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;myapp.current_user_id&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;USING&lt;&#x2F;code&gt; clause controls which rows are visible for SELECT, UPDATE, and DELETE. The &lt;code&gt;WITH CHECK&lt;&#x2F;code&gt; clause ensures new rows respect the policy on INSERT.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-the-session-variable&quot;&gt;Setting the Session Variable&lt;&#x2F;h3&gt;
&lt;p&gt;The tricky part was ensuring the session variable is set correctly in the application. I ended up setting it at the start of each database connection or transaction:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;BEGIN&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SET&lt;&#x2F;span&gt;&lt;span&gt; LOCAL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;myapp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;current_user_id &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Example user ID
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; documents;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- Only sees documents where owner_id = 42
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;COMMIT&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the application code (Node.js with &lt;code&gt;pg&lt;&#x2F;code&gt;), it looked like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;SELECT set_config(&amp;#39;myapp.current_user_id&amp;#39;, $1, false)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]);  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Example user ID
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rows &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;SELECT * FROM documents&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-surprised-me&quot;&gt;What Surprised Me&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-insert-policy-gotcha&quot;&gt;The INSERT Policy Gotcha&lt;&#x2F;h3&gt;
&lt;p&gt;Initially, I only created SELECT, UPDATE, and DELETE policies. I assumed INSERT would just work. It didn&#x27;t. Without an INSERT policy with &lt;code&gt;WITH CHECK&lt;&#x2F;code&gt;, users couldn&#x27;t insert rows at all—RLS blocked everything.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;WITH CHECK&lt;&#x2F;code&gt; clause is crucial for INSERT operations. It ensures that new rows being inserted actually match the policy criteria.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;performance-considerations&quot;&gt;Performance Considerations&lt;&#x2F;h3&gt;
&lt;p&gt;I was worried about performance. Would RLS policies slow down queries? In my testing, the overhead was minimal for simple policies like &lt;code&gt;owner_id = current_setting(...)&lt;&#x2F;code&gt;. But I can see how complex policies with joins or subqueries could become a bottleneck.&lt;&#x2F;p&gt;
&lt;p&gt;The key is to keep policies simple and efficient. If you need complex logic, consider materialized views or application-level filters.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-is-critical&quot;&gt;Testing is Critical&lt;&#x2F;h3&gt;
&lt;p&gt;RLS policies can be subtle. A policy that looks correct might have edge cases. I spent time testing different scenarios:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What happens if the session variable isn&#x27;t set?&lt;&#x2F;li&gt;
&lt;li&gt;What if it&#x27;s set to a non-existent user ID?&lt;&#x2F;li&gt;
&lt;li&gt;What if multiple policies apply to the same operation?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Testing revealed that forgetting to set the session variable results in users seeing no data (or all data, depending on your policy setup). This is something you need to catch early.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;rls-vs-application-filters&quot;&gt;RLS vs Application Filters&lt;&#x2F;h2&gt;
&lt;p&gt;I found myself thinking about the trade-offs. RLS centralizes security logic in the database, which is great for consistency. But it also means your security logic lives in the database, which can be harder to version control, test, and reason about compared to application code.&lt;&#x2F;p&gt;
&lt;p&gt;For this project, I went with RLS for the core isolation requirements. Business-specific filtering still happens in the application layer. It&#x27;s a hybrid approach, but it feels like the right balance.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;looking-back&quot;&gt;Looking Back&lt;&#x2F;h2&gt;
&lt;p&gt;If I were starting over:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start with a single table&lt;&#x2F;strong&gt;: I tried to enable RLS on multiple tables at once. Starting with one table would have made debugging easier.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add better error handling&lt;&#x2F;strong&gt;: When the session variable isn&#x27;t set, queries fail silently or return unexpected results. Better error messages would help.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document the session variable pattern&lt;&#x2F;strong&gt;: This is now part of our database connection setup, but it wasn&#x27;t obvious at first. Clear documentation would have saved time.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test with different connection patterns&lt;&#x2F;strong&gt;: I initially tested with simple queries, but real applications use connection pooling, transactions, and prepared statements. Testing those scenarios earlier would have caught issues sooner.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;&#x2F;h2&gt;
&lt;p&gt;RLS solved the problem I set out to solve. Data isolation is now enforced at the database level, which gives me confidence that even if application code has bugs, the database won&#x27;t leak data between tenants.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not a silver bullet—there are trade-offs, and it requires careful implementation and testing. But for multi-tenant applications where data isolation is critical, RLS is worth exploring.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;docs&#x2F;current&#x2F;ddl-rowsecurity.html&quot;&gt;PostgreSQL RLS Documentation&lt;&#x2F;a&gt; – Official documentation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;docs&#x2F;current&#x2F;sql-createpolicy.html&quot;&gt;RLS Policies&lt;&#x2F;a&gt; – Policy syntax and options&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>GitHub Actions Won&#x27;t Charge You (Probably)</title>
        <published>2025-12-16T00:00:00+00:00</published>
        <updated>2025-12-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/gh-actions-charges/"/>
        <id>https://noos.blog/posts/gh-actions-charges/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/gh-actions-charges/">&lt;p&gt;If you received an email from GitHub about upcoming pricing changes for GitHub Actions, you might be wondering: &quot;Are they going to start charging me for something that was free?&quot; The short answer: &lt;strong&gt;It depends on what you&#x27;re using, but for most people, the news is actually good.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let me break down what&#x27;s changing in plain language, so you can understand how it affects your projects.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-big-question-is-github-actions-still-free&quot;&gt;The Big Question: Is GitHub Actions Still Free?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Yes—for public repositories, GitHub Actions remains completely free.&lt;&#x2F;strong&gt; If you&#x27;re using GitHub Actions in public repositories (like open-source projects), nothing changes. You can continue using it without any charges.&lt;&#x2F;p&gt;
&lt;p&gt;This is important because many developers and organizations rely on GitHub Actions for open-source projects, and GitHub has committed to keeping that free.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-s-actually-changing&quot;&gt;What&#x27;s Actually Changing?&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub is making two main changes, both scheduled for 2026:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-price-reductions-january-1-2026&quot;&gt;1. Price Reductions (January 1, 2026)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Good news:&lt;&#x2F;strong&gt; GitHub is reducing prices for GitHub-hosted runners in private repositories by up to 39%, depending on the machine type you use.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re currently paying for GitHub Actions in private repositories, your costs will go down. This is a straightforward price cut—no catch.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-new-platform-charge-for-self-hosted-runners-march-1-2026&quot;&gt;2. New Platform Charge for Self-Hosted Runners (March 1, 2026)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;This is the one that might affect you:&lt;&#x2F;strong&gt; GitHub will introduce a new $0.002 per-minute &quot;cloud platform&quot; charge for self-hosted runners.&lt;&#x2F;p&gt;
&lt;p&gt;Wait—what&#x27;s a self-hosted runner? If you&#x27;re using your own servers or machines to run GitHub Actions (instead of GitHub&#x27;s cloud infrastructure), those are self-hosted runners. Most individual developers and small teams use GitHub-hosted runners, so this won&#x27;t affect them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Important details:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;This charge only applies to self-hosted runners (your own machines)&lt;&#x2F;li&gt;
&lt;li&gt;The charge is $0.002 per minute (that&#x27;s 0.2 cents per minute)&lt;&#x2F;li&gt;
&lt;li&gt;Even with this charge, it still counts against the minutes already included in your GitHub plan&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;What does that last point mean?&lt;&#x2F;strong&gt; If you have a GitHub plan that includes 2,000 minutes per month, and you use 500 minutes on self-hosted runners, you&#x27;ll pay the $0.002&#x2F;minute charge, but those 500 minutes still count toward your 2,000-minute allowance. You&#x27;re not being double-charged.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;quick-reference-pricing-summary&quot;&gt;Quick Reference: Pricing Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a simple breakdown of what&#x27;s changing:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Status After Update&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Public repo GitHub-hosted&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;✅ Still free&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Private repo GitHub-hosted&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;🔻 Price reduced (up to 39%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Self-hosted runners&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;💲 $0.002&#x2F;min platform charge&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Enterprise Server customers&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;✅ No change&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-this-means-for-you&quot;&gt;What This Means for You&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Public repositories:&lt;&#x2F;strong&gt; No change—still free&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Private repositories (GitHub-hosted):&lt;&#x2F;strong&gt; You&#x27;ll pay less—prices dropping up to 39%&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Self-hosted runners:&lt;&#x2F;strong&gt; New $0.002&#x2F;min charge (~$0.12&#x2F;hour). Calculate based on your usage&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise Server:&lt;&#x2F;strong&gt; No changes&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-the-change&quot;&gt;Why the Change?&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub hasn&#x27;t provided detailed reasoning, but the new charge for self-hosted runners likely reflects the infrastructure costs of managing the platform, security, and integration features that make self-hosted runners work seamlessly with GitHub Actions—even when the actual compute happens on your machines.&lt;&#x2F;p&gt;
&lt;p&gt;The price reduction for GitHub-hosted runners suggests GitHub is optimizing their infrastructure and passing those savings to customers.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-should-you-do&quot;&gt;What Should You Do?&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check your usage:&lt;&#x2F;strong&gt; If you&#x27;re using self-hosted runners, review how many minutes you typically use per month to estimate the new cost.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Review your plan:&lt;&#x2F;strong&gt; Make sure you understand what&#x27;s included in your current GitHub plan (free, Pro, Team, or Enterprise).&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Calculate impact:&lt;&#x2F;strong&gt; For self-hosted runners, multiply your monthly minutes by $0.002 to see the additional cost. For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;1,000 minutes&#x2F;month = $2.00&#x2F;month&lt;&#x2F;li&gt;
&lt;li&gt;5,000 minutes&#x2F;month = $10.00&#x2F;month&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consider alternatives:&lt;&#x2F;strong&gt; If the new charge significantly impacts your costs, you might want to evaluate whether self-hosted runners are still the best option, or if GitHub-hosted runners (which are getting cheaper) might work for your needs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;&#x2F;h2&gt;
&lt;p&gt;For most users, these changes are either neutral (public repos stay free) or positive (private repo prices go down). The only group that will see new charges is those using self-hosted runners, and even then, the charge is relatively small.&lt;&#x2F;p&gt;
&lt;p&gt;The key takeaway: &lt;strong&gt;GitHub Actions isn&#x27;t going away, and it&#x27;s not suddenly becoming expensive.&lt;&#x2F;strong&gt; The changes are targeted and mostly beneficial. If you&#x27;re concerned about costs, take some time to review your usage patterns and plan accordingly before the changes take effect in 2026.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;billing&#x2F;managing-billing-for-github-actions&#x2F;about-billing-for-github-actions&quot;&gt;GitHub Actions Pricing&lt;&#x2F;a&gt; – Official pricing documentation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;billing&#x2F;managing-billing-for-github-actions&#x2F;about-billing-for-github-actions#usage-limits&quot;&gt;GitHub Actions Usage Limits&lt;&#x2F;a&gt; – Understanding included minutes and limits&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hieratic Prompt Compression: Ambitious Prototype or Superpower?</title>
        <published>2025-11-16T00:00:00+00:00</published>
        <updated>2025-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/hieratic-prompt-compression-prototype/"/>
        <id>https://noos.blog/posts/hieratic-prompt-compression-prototype/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/hieratic-prompt-compression-prototype/">&lt;h2 id=&quot;why-even-bother-compressing-prompts&quot;&gt;Why Even Bother Compressing Prompts?&lt;&#x2F;h2&gt;
&lt;p&gt;Large prompts are the new monoliths.&lt;&#x2F;p&gt;
&lt;p&gt;If you’ve ever tried to stuff a serious extraction spec, a few pages of dense tables or configs, and long-running conversation history into a single LLM call, you already know the pain:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Context windows get cramped.&lt;&#x2F;li&gt;
&lt;li&gt;Tokens get expensive.&lt;&#x2F;li&gt;
&lt;li&gt;Latency creeps up.&lt;&#x2F;li&gt;
&lt;li&gt;And the “just add more context” strategy quietly stops scaling.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Hieratic Prompt Compression&lt;&#x2F;strong&gt; is my attempt to push back on that—without spinning up yet another LLM just to shrink prompts. It’s being explored as an experimental feature for &lt;strong&gt;Tokuin&lt;&#x2F;strong&gt;, my CLI for token estimation and provider-aware load testing (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;tokuin&quot;&gt;GitHub&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The core question of this post&lt;&#x2F;strong&gt;:&lt;br &#x2F;&gt;
Can we build a &lt;em&gt;deterministic&lt;&#x2F;em&gt;, structure-aware compression layer that shrinks prompts by 50–80% while preserving downstream task fidelity?&lt;br &#x2F;&gt;
And maybe more importantly: &lt;strong&gt;is it worth doing&lt;&#x2F;strong&gt;, or is this just an over-engineered curiosity?&lt;&#x2F;p&gt;
&lt;p&gt;This is not a feature announcement. It’s a proposal in public, and I’d like your help pressure-testing it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-hieratic&quot;&gt;Why “Hieratic”?&lt;&#x2F;h2&gt;
&lt;p&gt;The name “Hieratic” comes from the &lt;strong&gt;hieratic script&lt;&#x2F;strong&gt; used in ancient Egypt: a &lt;strong&gt;compact, scribal shorthand&lt;&#x2F;strong&gt; for hieroglyphs that preserved meaning while being much faster to write.&lt;&#x2F;p&gt;
&lt;p&gt;That’s roughly the spirit here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We want a &lt;strong&gt;compact, structured shorthand&lt;&#x2F;strong&gt; for long prompts.&lt;&#x2F;li&gt;
&lt;li&gt;We care about &lt;strong&gt;preserving the semantic load&lt;&#x2F;strong&gt;, not the surface form.&lt;&#x2F;li&gt;
&lt;li&gt;We want something a “scribal” system can produce deterministically, instead of asking another model to improvise a summary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hieratic is meant to be that shorthand: a structured, compressed representation of your original prompt that an LLM can still “read” effectively.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-i-m-proposing-in-plain-terms&quot;&gt;What I’m Proposing (in Plain Terms)&lt;&#x2F;h2&gt;
&lt;p&gt;The rough sketch is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;non-LLM, structure-aware compression pipeline&lt;&#x2F;strong&gt; that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;treats instructions and structured documents differently,&lt;&#x2F;li&gt;
&lt;li&gt;aggressively removes boilerplate,&lt;&#x2F;li&gt;
&lt;li&gt;reuses repeated snippets via a context library,&lt;&#x2F;li&gt;
&lt;li&gt;and supports incremental compression over long histories.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;More concretely:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extractive, not generative&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
We’re not asking another model to “summarize” the prompt. We’re &lt;strong&gt;selectively dropping&lt;&#x2F;strong&gt; low-value text and keeping the high-signal bits intact.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structure-aware&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
JSON, HTML tables, and BNF-like grammars are treated as &lt;em&gt;verbatim&lt;&#x2F;em&gt; document blocks. Only the natural language &lt;em&gt;instructions&lt;&#x2F;em&gt; around them get compressed.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Library-driven boilerplate removal&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Repeated extraction instructions, legal boilerplate, or scaffolding can be recognized and replaced by compact references using a reusable &lt;strong&gt;context library&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Incremental compression&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
For long-running flows, only the &lt;strong&gt;newly dropped&lt;&#x2F;strong&gt; parts of the conversation get compressed into anchored summaries. We don’t re-summarize the entire history on every call.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The output is a “Hieratic” prompt: a compact, structured representation that the application can expand or feed directly into an LLM.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-this-might-be-worth-doing&quot;&gt;Why This Might Be Worth Doing&lt;&#x2F;h2&gt;
&lt;p&gt;From a product and engineering standpoint, a feature like this could unlock some real benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Token savings at scale&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
If you’re sending the same extraction spec or long policy docs over and over, shaving 50–80% of that cost starts to matter.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Longer, richer prompts&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
A smaller prompt budget per call means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;More examples,&lt;&#x2F;li&gt;
&lt;li&gt;More “why this matters” context,&lt;&#x2F;li&gt;
&lt;li&gt;More system-level constraints,
&lt;strong&gt;without&lt;&#x2F;strong&gt; immediately slamming into context window limits.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Latency and stability&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Fewer tokens → lower latency, more predictable runtime, fewer timeouts and retries.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Determinism &amp;amp; debuggability&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Compression is &lt;strong&gt;rule-based&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Same input → same compressed output.&lt;&#x2F;li&gt;
&lt;li&gt;Easy to diff and inspect what was kept vs dropped.&lt;&#x2F;li&gt;
&lt;li&gt;Failure modes can be reasoned about and tested explicitly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Better fit for structured tasks&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Many real-world prompts mix instructions with structured data (tables, JSON, etc.). A structure-aware compressor is a more natural fit than a generic summarizer.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If this works even moderately well, Hieratic could become a &lt;strong&gt;core building block&lt;&#x2F;strong&gt; for workflows where prompts—not models—are the main bottleneck.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-this-might-be-a-terrible-idea&quot;&gt;Why This Might Be a Terrible Idea&lt;&#x2F;h2&gt;
&lt;p&gt;Being honest about the risks:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;We silently destroy task fidelity&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The dangerous failure mode is not “the model throws an error”, it’s “the model quietly stops extracting certain fields.”&lt;&#x2F;li&gt;
&lt;li&gt;Under aggressive compression, subtle but important constraints and corner cases can just, disappear.&lt;&#x2F;li&gt;
&lt;li&gt;Research like &lt;strong&gt;500xCompressor&lt;&#x2F;strong&gt; reports retaining ~62–73% of performance under heavy compression; that may be fine for some tasks, terrifying for others.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;We overfit to a narrow prompt shape&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The early design assumes prompts that look like:
&lt;ul&gt;
&lt;li&gt;a long instruction block, plus&lt;&#x2F;li&gt;
&lt;li&gt;a structured document (tables, configs, schemas, etc.).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;If your prompts are mostly conversational, mostly code, or heavily multimodal, these strategies may not transfer well.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Evaluation is non-trivial&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;To take this seriously, we’ll need:
&lt;ul&gt;
&lt;li&gt;a proper “before vs after compression” benchmark,&lt;&#x2F;li&gt;
&lt;li&gt;real extraction and reasoning tasks, not toy examples,&lt;&#x2F;li&gt;
&lt;li&gt;and metrics beyond “tokens saved” (precision&#x2F;recall on fields, calibration of failure modes, etc.).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If we’re not disciplined, we’ll end up with a fancy &lt;strong&gt;token counter&lt;&#x2F;strong&gt; that quietly degrades the very tasks this was supposed to help with.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;a-bit-of-research-backing&quot;&gt;A Bit of Research Backing&lt;&#x2F;h2&gt;
&lt;p&gt;This idea borrows from existing work rather than inventing everything from scratch:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;500xCompressor: Generalized Prompt Compression for Large Language Models&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Shows that prompts are &lt;strong&gt;highly compressible&lt;&#x2F;strong&gt; (6×–480×) by learning a compressed “language” while retaining 62–73% of task performance. Key takeaways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prompts contain a lot of low-information redundancy.&lt;&#x2F;li&gt;
&lt;li&gt;You can compress aggressively if you protect information-bearing tokens.&lt;br &#x2F;&gt;
→ &lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2408.03094?utm_source=chatgpt.com&quot;&gt;500xCompressor: Generalized Prompt Compression for Large Language Models&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;NAACL 2025 survey on prompt compression&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Surveys extractive vs. abstractive compression and token-pruning strategies, emphasizing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;trade-offs between compression ratio and task fidelity,&lt;&#x2F;li&gt;
&lt;li&gt;the importance of preserving structure for technical prompts.&lt;br &#x2F;&gt;
→ &lt;a href=&quot;https:&#x2F;&#x2F;aclanthology.org&#x2F;2025.naacl-long.368.pdf?utm_source=chatgpt.com&quot;&gt;NAACL 2025 prompt compression survey (PDF)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Factory.ai – “Compressing Context”&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Describes a production-ready strategy for &lt;strong&gt;incremental, anchored summaries&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;maintain a persistent conversation state,&lt;&#x2F;li&gt;
&lt;li&gt;compress only the span that’s about to fall out of context,&lt;&#x2F;li&gt;
&lt;li&gt;avoid repeatedly re-summarizing old history.&lt;br &#x2F;&gt;
→ &lt;a href=&quot;https:&#x2F;&#x2F;factory.ai&#x2F;news&#x2F;compressing-context?utm_source=chatgpt.com&quot;&gt;Factory.ai: “Compressing Context”&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Where Hieratic diverges is in deliberately &lt;strong&gt;not&lt;&#x2F;strong&gt; adding another model to the loop. The goal is a deterministic, rule-based compressor that plays nicely with structured prompts and long-running pipelines.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;rough-shape-of-the-system&quot;&gt;Rough Shape of the System&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s the high-level architecture I’m exploring.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-split-instructions-vs-document&quot;&gt;1. Split instructions vs document&lt;&#x2F;h3&gt;
&lt;p&gt;Before we drop any tokens:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Instruction block&lt;&#x2F;strong&gt;: natural-language instructions and commentary.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Document block&lt;&#x2F;strong&gt;: structured tables, JSON payloads, grammars, etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Only the instruction block is compressed.&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Document blocks are treated as &lt;strong&gt;verbatim&lt;&#x2F;strong&gt; and left untouched.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-pattern-extraction-context-library-optional&quot;&gt;2. Pattern extraction &amp;amp; context library (optional)&lt;&#x2F;h3&gt;
&lt;p&gt;Given a corpus of prompts, a pattern extractor:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;scans for &lt;strong&gt;frequently repeated fragments&lt;&#x2F;strong&gt; (boilerplate instructions, shared scaffolding),&lt;&#x2F;li&gt;
&lt;li&gt;builds a reusable &lt;strong&gt;context library&lt;&#x2F;strong&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;allows future prompts to reference those snippets instead of inlining them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is similar in spirit to a learned codebook (as in research like 500xCompressor), but done in a deterministic, human-auditable way.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-extractive-compression-over-instruction-blocks&quot;&gt;3. Extractive compression over instruction blocks&lt;&#x2F;h3&gt;
&lt;p&gt;For instruction text, the compressor:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sets a &lt;strong&gt;token budget&lt;&#x2F;strong&gt; based on a compression level (&lt;code&gt;Light&lt;&#x2F;code&gt;, &lt;code&gt;Medium&lt;&#x2F;code&gt;, &lt;code&gt;Aggressive&lt;&#x2F;code&gt;) or explicit target ratio.&lt;&#x2F;li&gt;
&lt;li&gt;Segments text using structure-aware heuristics:
&lt;ul&gt;
&lt;li&gt;headings (&lt;code&gt;#&lt;&#x2F;code&gt;, &lt;code&gt;##&lt;&#x2F;code&gt;),&lt;&#x2F;li&gt;
&lt;li&gt;“Definition:”, “Location:”, “Response Format:” markers,&lt;&#x2F;li&gt;
&lt;li&gt;parameter blocks (&lt;code&gt;Extracted_Value&lt;&#x2F;code&gt;, &lt;code&gt;Doc_Page_Number&lt;&#x2F;code&gt;, etc.).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Scores segments such that:
&lt;ul&gt;
&lt;li&gt;hard constraints and response formats are &lt;strong&gt;hard-kept&lt;&#x2F;strong&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;explanatory text and examples are more compressible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Implements a greedy selection: keep highest-scoring segments until the budget is spent.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The intent is to preserve the “spine” of the instructions while trimming repetition and soft context.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-encode-as-a-hieratic-prompt&quot;&gt;4. Encode as a Hieratic prompt&lt;&#x2F;h3&gt;
&lt;p&gt;The compressed result is organized into a &lt;strong&gt;Hieratic&lt;&#x2F;strong&gt; structure, with sections like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@ROLE&lt;&#x2F;code&gt; – who the model is supposed to be,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;@TASK&lt;&#x2F;code&gt; – what it’s supposed to do,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;@FOCUS&lt;&#x2F;code&gt; – which fields &#x2F; behaviors matter most,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;@EXAMPLES&lt;&#x2F;code&gt; – optionally compressed or referenced,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;@RECENT&lt;&#x2F;code&gt; – most recent interaction span,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;@ANCHOR[...]&lt;&#x2F;code&gt; – anchored summaries of older context.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It’s essentially a compact, structured representation of your original prompt.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-incremental-mode-for-long-histories&quot;&gt;5. Incremental mode for long histories&lt;&#x2F;h3&gt;
&lt;p&gt;In incremental mode, the compressor:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;loads a previous compression state,&lt;&#x2F;li&gt;
&lt;li&gt;identifies the “oldest” span that’s about to be evicted from context,&lt;&#x2F;li&gt;
&lt;li&gt;compresses that span into a new &lt;code&gt;@ANCHOR[...]&lt;&#x2F;code&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;keeps the most recent N tokens uncompressed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Over time, you get a stack of anchors plus a fresh &lt;code&gt;@RECENT&lt;&#x2F;code&gt; tail, keeping context current without growing linearly with the full history.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-i-d-love-feedback-on&quot;&gt;What I’d Love Feedback On&lt;&#x2F;h2&gt;
&lt;p&gt;This is the experimental part. I’d love to hear from people who:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;run LLMs in production with big prompts,&lt;&#x2F;li&gt;
&lt;li&gt;have tried prompt compression in anger,&lt;&#x2F;li&gt;
&lt;li&gt;or have opinions on where this kind of feature should live in a system like Tokuin.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A few specific questions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Evaluation&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How would you design a &lt;em&gt;real&lt;&#x2F;em&gt; benchmark for this?&lt;&#x2F;li&gt;
&lt;li&gt;Which tasks &#x2F; datasets would you use to prove that we’re not quietly breaking critical behaviors?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Failure modes&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Where have compression strategies bitten you before?&lt;&#x2F;li&gt;
&lt;li&gt;Are there patterns or structures you’d mark as “never compress this”?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Integration into a stack like Tokuin&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;At what layer would you expect to see a feature like this?
&lt;ul&gt;
&lt;li&gt;Right before the LLM call?&lt;&#x2F;li&gt;
&lt;li&gt;As part of a prompt-building pipeline?&lt;&#x2F;li&gt;
&lt;li&gt;As a standalone preprocessor with its own cache?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Alternatives &amp;amp; trade-offs&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Would you lean towards learned compressors instead?&lt;&#x2F;li&gt;
&lt;li&gt;Are there simpler heuristics that get most of the benefit without this level of complexity?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product shaping&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Should this live as:
&lt;ul&gt;
&lt;li&gt;a first-class Tokuin feature,&lt;&#x2F;li&gt;
&lt;li&gt;an “experimental &#x2F; power user” option,&lt;&#x2F;li&gt;
&lt;li&gt;or a separate library that Tokuin can plug into?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you have opinions—positive or negative—I’d genuinely appreciate hearing them.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;how-this-fits-into-tokuin-right-now&quot;&gt;How This Fits Into Tokuin (Right Now)&lt;&#x2F;h2&gt;
&lt;p&gt;As of today, &lt;strong&gt;Hieratic Prompt Compression is experimental and aspirational&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It’s being explored as a potential &lt;strong&gt;feature for Tokuin&lt;&#x2F;strong&gt;, not a locked-in roadmap item.&lt;&#x2F;li&gt;
&lt;li&gt;Any implementation will start as:
&lt;ul&gt;
&lt;li&gt;an opt-in, feature-flagged module,&lt;&#x2F;li&gt;
&lt;li&gt;with clear disclaimers about the risks,&lt;&#x2F;li&gt;
&lt;li&gt;and tooling to inspect exactly what was compressed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Long term, if the idea survives scrutiny, I could imagine:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;per-project &lt;strong&gt;compression profiles&lt;&#x2F;strong&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;UI&#x2F;CLI tools for inspecting “before vs after” prompts,&lt;&#x2F;li&gt;
&lt;li&gt;and tighter integration with context management and retrieval strategies.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But we’re not there yet. For now, I’m trying to answer a simpler question:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can a Hieratic-style, rule-based compressor earn its place inside Tokuin, or should we keep prompts uncompressed and invest elsewhere?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you’ve navigated similar trade-offs—or you’re just curious and want to poke holes in this idea—I’d love to hear from you.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;500xCompressor: Generalized Prompt Compression for Large Language Models&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2408.03094?utm_source=chatgpt.com&quot;&gt;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2408.03094&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;NAACL 2025 Prompt Compression Survey&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;aclanthology.org&#x2F;2025.naacl-long.368.pdf?utm_source=chatgpt.com&quot;&gt;https:&#x2F;&#x2F;aclanthology.org&#x2F;2025.naacl-long.368.pdf&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Factory.ai – “Compressing Context”&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;factory.ai&#x2F;news&#x2F;compressing-context?utm_source=chatgpt.com&quot;&gt;https:&#x2F;&#x2F;factory.ai&#x2F;news&#x2F;compressing-context&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tokuin: Token Intelligence for LLM Developers</title>
        <published>2025-11-07T00:00:00+00:00</published>
        <updated>2025-11-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/tokuin-token-tooling-for-llm-builders/"/>
        <id>https://noos.blog/posts/tokuin-token-tooling-for-llm-builders/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/tokuin-token-tooling-for-llm-builders/">&lt;p&gt;I built &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;tokuin&quot;&gt;Tokuin&lt;&#x2F;a&gt; because the tooling around prompt design still feels like guesswork. We scribble system prompts in scratchpads, retry endpoints until rate limits scream, and only know what the bill looks like after the invoice arrives. Tokuin changes that. It gives you &lt;strong&gt;reliable token estimates, cost projections, and synthetic load tests&lt;&#x2F;strong&gt;—all packaged in a Rust CLI you can trust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-another-cli&quot;&gt;Why Another CLI?&lt;&#x2F;h2&gt;
&lt;p&gt;Prompts have become product surfaces. Teams iterate on them like code, but the support tooling still feels like duct tape. I wanted something that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Reads prompts from files, stdin, or watch mode&lt;&#x2F;li&gt;
&lt;li&gt;Targets multiple providers without juggling SDKs&lt;&#x2F;li&gt;
&lt;li&gt;Surfaces pricing before you ever hit “Send”&lt;&#x2F;li&gt;
&lt;li&gt;Scales up to load tests when you need to know how a model behaves across 1,000 requests&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Tokuin is the result: a &lt;strong&gt;ground-up build in Rust&lt;&#x2F;strong&gt; with a modular architecture for tokenizers, providers, and output formats. It’s fast, predictable, and stays out of your way while you experiment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;feature-highlights&quot;&gt;Feature Highlights&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token estimation that understands context&lt;&#x2F;strong&gt;: Supply raw text, chat transcripts, or JSON payloads and Tokuin breaks down system&#x2F;user&#x2F;assistant roles with optional markdown minification.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Multi-model comparisons&lt;&#x2F;strong&gt;: Pass &lt;code&gt;--compare&lt;&#x2F;code&gt; with OpenAI, Anthropic, or OpenRouter models to see how token counts and costs differ.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Pricing awareness&lt;&#x2F;strong&gt;: Add &lt;code&gt;--price&lt;&#x2F;code&gt; or &lt;code&gt;--estimate-cost&lt;&#x2F;code&gt; to forecast spend per prompt, per run, or across a load test.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Watch mode&lt;&#x2F;strong&gt;: Keep Tokuin running with &lt;code&gt;--watch&lt;&#x2F;code&gt; and it re-computes every time you save a file. Perfect for prompt gardening.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Load testing (feature flag)&lt;&#x2F;strong&gt;: Enable the &lt;code&gt;load-test&lt;&#x2F;code&gt; feature to hammer APIs with controlled concurrency, think times, retry logic, and automatic latency&#x2F;cost reports.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Format-flexible output&lt;&#x2F;strong&gt;: Emit human-friendly text, JSON for scripts, Markdown for docs, CSV for spreadsheets, or Prometheus metrics when you need dashboards.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Provider-ready&lt;&#x2F;strong&gt;: OpenAI and OpenRouter work out of the box, Gemini is one flag away, and new providers plug into the registry without rewriting the CLI surface.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#tokuin&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;tokuin&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Feature set sourced from the project README.&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;tokuin&quot;&gt;tokuin&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;advantages-for-developers&quot;&gt;Advantages for Developers&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Budget confidence&lt;&#x2F;strong&gt;: Know input&#x2F;output token counts and projected cost before you ship or run a batch job.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Faster iteration&lt;&#x2F;strong&gt;: Swap “test, wait, pray” with a tight loop; piping prompts through Tokuin becomes muscle memory.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CI-friendly&lt;&#x2F;strong&gt;: Because it’s a single binary, you can run token checks in CI to guard against prompt bloat before merge.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Team alignment&lt;&#x2F;strong&gt;: Comparing multiple models is now a line of CLI flags, not a spreadsheet of stitched-together data.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Infra visibility&lt;&#x2F;strong&gt;: Load tests surface latency cliffs, provider throttling, and cost ceilings before your users do.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;built-for-growth&quot;&gt;Built for Growth&lt;&#x2F;h2&gt;
&lt;p&gt;Tokuin ships with a modular core: &lt;code&gt;tokenizers&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;models&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;providers&#x2F;&lt;&#x2F;code&gt;, and &lt;code&gt;output&#x2F;&lt;&#x2F;code&gt; live in separate crates so we can grow the ecosystem without tangling the CLI.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#tokuin&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; Near-term roadmap items include:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Provider expansion&lt;&#x2F;strong&gt;: Native Anthropic, Mistral, and Cohere support (work already outlined in &lt;code&gt;PROVIDERS_PLAN.md&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scenario scripts&lt;&#x2F;strong&gt;: Define multi-turn conversations and replay them in load tests.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cost guardrails&lt;&#x2F;strong&gt;: Abort long runs automatically when reaching a user-defined budget ceiling.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Prompt linting&lt;&#x2F;strong&gt;: Surface style and structure issues before they hit production pipelines.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Plugin hooks&lt;&#x2F;strong&gt;: Let users contribute tokenizers or pricing sources without forking the repo.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;from-experiment-to-community-tooling&quot;&gt;From Experiment to Community Tooling&lt;&#x2F;h2&gt;
&lt;p&gt;Tokuin grew out of practical late-night benchmarking, budget vetting, and the need for predictable tooling across teams. We still welcome that vibe-coding energy—if you sketch solutions with AI co-pilots or riff on ideas in flow, there’s room here to help shape how we build for LLMs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-to-get-involved&quot;&gt;How to Get Involved&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run it&lt;&#x2F;strong&gt;: &lt;code&gt;cargo install tokuin&lt;&#x2F;code&gt; or build from source and kick the tires.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Share prompts&lt;&#x2F;strong&gt;: If you discover funky edge cases in tokenization or pricing, capture them as regression fixtures.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Open a PR&lt;&#x2F;strong&gt;: Contributions are welcome whether you’re adding providers, tightening error messages, or documenting workflows. Mention any vibe-coding sessions in your PR so we can trace the creative path.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Join the roadmap&lt;&#x2F;strong&gt;: Drop ideas in issues, especially if you’re working on multi-provider tooling—the more weird setups we test, the better Tokuin gets.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Thanks for trying it out. Tokuin exists to remove guesswork, save budgets, and let prompt engineers stay in flow. If you ship something with it, I’d love to hear the story.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>mock: Build APIs with Shell Scripts, Not Frameworks</title>
        <published>2025-11-05T00:00:00+00:00</published>
        <updated>2025-11-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/mock-api-framework-shell-scripts/"/>
        <id>https://noos.blog/posts/mock-api-framework-shell-scripts/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/mock-api-framework-shell-scripts/">&lt;p&gt;Sometimes you need an API endpoint fast. Not a full framework, not a production service, just something that responds to HTTP requests. &lt;a href=&quot;https:&#x2F;&#x2F;dhuan.github.io&#x2F;mock&#x2F;&quot;&gt;mock&lt;&#x2F;a&gt; is exactly that—a lightweight tool that turns shell scripts into HTTP endpoints.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-concept&quot;&gt;The Concept&lt;&#x2F;h2&gt;
&lt;p&gt;Instead of spinning up Express, Flask, or any other framework, mock lets you define endpoints with simple shell scripts:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mock serve \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --route &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;users&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --method &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;GET&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --shell-script &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;get_users.sh&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Your shell script becomes the response handler. Inside it, you use mock&#x27;s utilities to build HTTP responses: &lt;code&gt;mock write&lt;&#x2F;code&gt; to send data, &lt;code&gt;mock set-header&lt;&#x2F;code&gt; to add headers, and access to the full shell environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;&#x2F;h2&gt;
&lt;p&gt;This approach shines for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prototyping&lt;&#x2F;strong&gt;: Mock external APIs you&#x27;re integrating with&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Testing&lt;&#x2F;strong&gt;: Create test fixtures without setting up databases&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Quick integrations&lt;&#x2F;strong&gt;: Bridge gaps between services using familiar shell tools&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DevOps scripts&lt;&#x2F;strong&gt;: Expose your automation scripts as HTTP endpoints&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You get all the power of shell (&lt;code&gt;grep&lt;&#x2F;code&gt;, &lt;code&gt;awk&lt;&#x2F;code&gt;, &lt;code&gt;curl&lt;&#x2F;code&gt;, databases, file systems) wrapped in HTTP. No dependencies beyond what&#x27;s already on your system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-details&quot;&gt;The Details&lt;&#x2F;h2&gt;
&lt;p&gt;Mock supports JSON config files or command-line parameters. You can set response headers, status codes, route parameters, conditional responses, and even serve static files. It&#x27;s comprehensive enough for complex scenarios while staying dead simple.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;ve ever thought &quot;I wish I could just write a bash script and expose it as an API,&quot; mock is that tool.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Documentation&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;dhuan.github.io&#x2F;mock&#x2F;latest&#x2F;getting_started.html&quot;&gt;dhuan.github.io&#x2F;mock&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Java vs Rust: A ROUGE-L Performance Comparison</title>
        <published>2025-11-02T00:00:00+00:00</published>
        <updated>2025-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/java-vs-rust-rouge-l-performance/"/>
        <id>https://noos.blog/posts/java-vs-rust-rouge-l-performance/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/java-vs-rust-rouge-l-performance/">&lt;h3 id=&quot;the-experiment&quot;&gt;The Experiment&lt;&#x2F;h3&gt;
&lt;p&gt;I was curious about the performance differences between Java and Rust for a specific workload. So I built identical ROUGE-L implementations in both languages and ran some benchmarks. The code was AI-generated, the algorithm was the same, and the results were mathematically equivalent. But the performance? That&#x27;s where it gets interesting.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-rouge-l&quot;&gt;What is ROUGE-L?&lt;&#x2F;h3&gt;
&lt;p&gt;ROUGE-L (Recall-Oriented Understudy for Gisting Evaluation - Longest Common Subsequence) is a metric used to evaluate text summarization quality. It&#x27;s part of the &lt;a href=&quot;https:&#x2F;&#x2F;aclanthology.org&#x2F;W04-1013&#x2F;&quot;&gt;ROUGE evaluation suite&lt;&#x2F;a&gt; developed by Chin-Yew Lin in 2004, which has become a standard in natural language processing for assessing how well generated summaries match reference summaries.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;How it works:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ROUGE-L measures similarity based on the Longest Common Subsequence (LCS) between sequences of words. It calculates three metrics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Precision&lt;&#x2F;strong&gt;: LCS &#x2F; (number of words in candidate summary)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Recall&lt;&#x2F;strong&gt;: LCS &#x2F; (number of words in reference summary)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;F-Measure&lt;&#x2F;strong&gt;: 2 × (Precision × Recall) &#x2F; (Precision + Recall)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The algorithm uses dynamic programming with O(m × n) time complexity, where m and n are the lengths of the two sequences. It&#x27;s a straightforward implementation, which makes it a good candidate for comparing language performance.&lt;&#x2F;p&gt;
&lt;p&gt;ROUGE-L is particularly useful because it doesn&#x27;t require exact word matches—it finds the longest sequence of words that appear in the same order in both texts, making it more flexible than n-gram overlap methods like ROUGE-1 or ROUGE-2.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-setup&quot;&gt;The Setup&lt;&#x2F;h3&gt;
&lt;p&gt;Both implementations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use the same dynamic programming algorithm for LCS calculation&lt;&#x2F;li&gt;
&lt;li&gt;Tokenize text the same way (lowercase, whitespace-based splitting)&lt;&#x2F;li&gt;
&lt;li&gt;Handle the same 16 test examples across 6 complexity levels&lt;&#x2F;li&gt;
&lt;li&gt;Produce identical mathematical results&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The only difference is the language they&#x27;re written in.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Test scenarios include:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Basic text comparisons&lt;&#x2F;li&gt;
&lt;li&gt;Structured data (JSON, HTML)&lt;&#x2F;li&gt;
&lt;li&gt;Mixed content with embedded structures&lt;&#x2F;li&gt;
&lt;li&gt;Real-world technical documentation&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can find the full comparison project on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;rouge-l-comparison&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-results&quot;&gt;The Results&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Accuracy:&lt;&#x2F;strong&gt; Both implementations produced &lt;strong&gt;100% identical results&lt;&#x2F;strong&gt; across all 16 test cases. Every F-Measure, Precision, and Recall value matched perfectly. This confirms the algorithms are mathematically equivalent.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Performance:&lt;&#x2F;strong&gt; That&#x27;s where things get interesting.&lt;&#x2F;p&gt;
&lt;p&gt;After multiple benchmark runs with 10 iterations each:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Java:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Average: 52-62ms per run&lt;&#x2F;li&gt;
&lt;li&gt;Median: 51-61ms&lt;&#x2F;li&gt;
&lt;li&gt;Standard deviation: 1.5-13ms (relatively consistent)&lt;&#x2F;li&gt;
&lt;li&gt;Includes JVM startup time in every run&lt;&#x2F;li&gt;
&lt;li&gt;Performance stabilizes quickly after first iteration&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Rust:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Average: 21-40ms (heavily skewed by first cold start)&lt;&#x2F;li&gt;
&lt;li&gt;Median: 2.5-4ms (after warmup)&lt;&#x2F;li&gt;
&lt;li&gt;First iteration: 190-360ms (cold start overhead including compilation)&lt;&#x2F;li&gt;
&lt;li&gt;Warm average: 2.4-4.4ms (excluding first run)&lt;&#x2F;li&gt;
&lt;li&gt;Standard deviation: 60-115ms (largely due to cold start variability)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The speedup:&lt;&#x2F;strong&gt; After warmup, Rust was consistently &lt;strong&gt;12-25x faster&lt;&#x2F;strong&gt; than Java for this workload. For example, in one run: Java median 56.66ms vs Rust warm average 2.50ms = &lt;strong&gt;24.76x speedup&lt;&#x2F;strong&gt;. The median Rust time (2.5-3ms) versus Java&#x27;s median (52-56ms) tells the story.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-this-means&quot;&gt;What This Means&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;For Java:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;JVM startup adds significant overhead (~50-60ms)&lt;&#x2F;li&gt;
&lt;li&gt;Once running, performance is consistent&lt;&#x2F;li&gt;
&lt;li&gt;The runtime environment is predictable but has a fixed cost&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;For Rust:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cold start includes compilation&#x2F;optimization overhead (~200-350ms)&lt;&#x2F;li&gt;
&lt;li&gt;Once warmed up, execution is extremely fast (~2.5-3ms)&lt;&#x2F;li&gt;
&lt;li&gt;Compiled binary runs without runtime interpretation overhead&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The reality:&lt;&#x2F;strong&gt; In production, both would run with warmup periods. Java would maintain its ~55ms average. Rust would settle into its ~2.5-3ms sweet spot. The performance difference would still be substantial.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;observations&quot;&gt;Observations&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Startup time:&lt;&#x2F;strong&gt; Java has consistent startup overhead. Rust has a larger initial cold start but negligible warm overhead.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistency:&lt;&#x2F;strong&gt; Java&#x27;s performance is more predictable from run to run. Rust&#x27;s cold start variability makes early measurements misleading.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Warm performance:&lt;&#x2F;strong&gt; Once both are warmed up, Rust&#x27;s compiled nature provides significant advantages for CPU-bound work.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ecosystem:&lt;&#x2F;strong&gt; Java has mature NLP libraries. Rust has growing ecosystem support. For this isolated algorithm, both worked well.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;The &quot;just for fun&quot; part:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was an experiment. I wanted to see what would happen if you took the same algorithm, implemented it identically in two languages, and compared performance. The answer: same results, dramatically different performance characteristics.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;&#x2F;h3&gt;
&lt;p&gt;For production systems evaluating summarization quality:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Throughput matters:&lt;&#x2F;strong&gt; Processing thousands of summaries per second benefits from Rust&#x27;s speed&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Latency matters:&lt;&#x2F;strong&gt; If this runs in a request path, 2.5ms vs 55ms is significant&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure matters:&lt;&#x2F;strong&gt; Java&#x27;s JVM ecosystem vs Rust&#x27;s compiled binary have different deployment considerations&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Neither is &quot;better&quot; in absolute terms. They have different tradeoffs. Understanding those tradeoffs is what matters.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;aclanthology.org&#x2F;W04-1013&#x2F;&quot;&gt;Lin, C.-Y. (2004). ROUGE: A Package for Automatic Evaluation of Summaries&lt;&#x2F;a&gt; - The original paper introducing ROUGE metrics&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ROUGE_(metric)&quot;&gt;ROUGE Metrics Documentation&lt;&#x2F;a&gt; - Wikipedia overview of ROUGE evaluation metrics&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;rouge-l-comparison&quot;&gt;Comparison Project on GitHub&lt;&#x2F;a&gt; - Full source code and benchmarks&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;The code is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&#x2F;rouge-l-comparison&quot;&gt;available on GitHub&lt;&#x2F;a&gt; if you want to run your own benchmarks. Same algorithm, same results, different performance characteristics. Sometimes the fun experiments teach you the most.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What Happens When Prototypes Try to Go to Production</title>
        <published>2025-11-02T00:00:00+00:00</published>
        <updated>2025-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/when-prototypes-become-production/"/>
        <id>https://noos.blog/posts/when-prototypes-become-production/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/when-prototypes-become-production/">&lt;h3 id=&quot;the-conversation&quot;&gt;The Conversation&lt;&#x2F;h3&gt;
&lt;p&gt;You&#x27;ve built a production-ready AI system with proper architecture, traceability, and acceptable latency. Then someone suggests &quot;why not just use a prompt?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t need to explain why that won&#x27;t work. The industry has already documented what happens when prototypes become production.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-not-just-give-an-opinion&quot;&gt;Why Not Just Give an Opinion?&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a temptation to push back when surface-level decisions ignore architecture, scalability, edge cases, and maintainability. But opinions based on current trends or individual preferences don&#x27;t help. They just add noise.&lt;&#x2F;p&gt;
&lt;p&gt;The current landscape is filled with quick takes and hot takes. Everyone has an opinion, often shaped more by what&#x27;s trending than by systematic thinking. Narcissistic individualism—the idea that one person&#x27;s perspective matters more than collective experience—creates an environment where strong opinions win even when they&#x27;re wrong.&lt;&#x2F;p&gt;
&lt;p&gt;The problem isn&#x27;t trends themselves. It&#x27;s choosing tools or approaches because they&#x27;re trending, rather than because they solve real problems. The distinction matters.&lt;&#x2F;p&gt;
&lt;p&gt;Research and data exist. Patterns repeat. The industry has documented what works and what doesn&#x27;t. Why add another opinion when we can point to what&#x27;s already been proven?&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why this isn&#x27;t about pushing back with opinions. It&#x27;s about pointing to what the industry already knows: prototypes that skip architecture rarely become production systems. The data is public. The patterns are clear.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-statistics&quot;&gt;The Statistics&lt;&#x2F;h3&gt;
&lt;p&gt;According to recent research, &lt;strong&gt;87% of machine learning models never make it past the prototype stage&lt;&#x2F;strong&gt;. Reasons include technical debt, edge cases that break everything, scale issues, and maintainability collapses.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-just-a-prompt-rarely-works&quot;&gt;Why &quot;Just a Prompt&quot; Rarely Works&lt;&#x2F;h3&gt;
&lt;p&gt;When you build a direct LLM solution, you&#x27;re coding logic into prompts. This creates brittleness—small changes require rewriting entire prompts. It&#x27;s impossible to debug—you can&#x27;t see intermediate steps. And there&#x27;s no observability—production needs monitoring, logging, and metrics that a single prompt can&#x27;t provide.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-actually-ships&quot;&gt;What Actually Ships&lt;&#x2F;h3&gt;
&lt;p&gt;AI systems that reach production are rarely &quot;just a prompt.&quot; They have structured workflows, error handling, observability, modularity, and scalability. These frameworks and architectures exist because production systems need structure—not because they&#x27;re trendy, but because they solve real problems that prototypes ignore.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-numbers-don-t-lie&quot;&gt;The Numbers Don&#x27;t Lie&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.gartner.com&#x2F;en&#x2F;articles&#x2F;how-to-make-your-ai-projects-production-ready&quot;&gt;Gartner&lt;&#x2F;a&gt;: Over 80% of organizations will struggle to operationalize AI systems by 2026&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mitsloan.mit.edu&#x2F;ideas-made-to-matter&#x2F;how-companies-are-putting-ai-work&quot;&gt;MIT Sloan&lt;&#x2F;a&gt;: 85% experiment with AI, but only 37% deploy at scale&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;hbr.org&#x2F;2023&#x2F;04&#x2F;how-to-avoid-the-ai-maturity-problem&quot;&gt;Harvard Business Review&lt;&#x2F;a&gt;: &quot;Failure to move beyond proof of concept&quot; is the primary cause of AI failures&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Architecture matters.&lt;&#x2F;strong&gt; Prototypes skip it. Production requires it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-hard-truth&quot;&gt;The Hard Truth&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a mismatch between demos and production. Prototypes optimize for speed. Production optimizes for reliability, scalability, and maintainability.&lt;&#x2F;p&gt;
&lt;p&gt;The question isn&#x27;t whether your prototype works. It&#x27;s whether it works when you have 100x more users, need unanticipated features, or an edge case breaks everything.&lt;&#x2F;p&gt;
&lt;p&gt;Prototypes optimize for the first week. Production systems need to survive years.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Sometimes the most important lessons are the ones you learn before you make the mistake.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Zola About Page Puzzle: When Root Sections Bite Back</title>
        <published>2025-11-02T00:00:00+00:00</published>
        <updated>2025-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/zola-about-page-puzzle/"/>
        <id>https://noos.blog/posts/zola-about-page-puzzle/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/zola-about-page-puzzle/">&lt;h3 id=&quot;the-goal&quot;&gt;The Goal&lt;&#x2F;h3&gt;
&lt;p&gt;I wanted to add an About page to my blog. Simple enough, right? Create &lt;code&gt;content&#x2F;about.md&lt;&#x2F;code&gt;, add it to the navigation menu, and done.&lt;&#x2F;p&gt;
&lt;p&gt;Wrong.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h3&gt;
&lt;p&gt;I created the About page, updated the navigation, and ran &lt;code&gt;zola build&lt;&#x2F;code&gt;. Instead of a clean build, I got this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Warning: 1 page(s) ignored (missing date or weight in a sorted section):
&lt;&#x2F;span&gt;&lt;span&gt;Warning: - &#x2F;Users&#x2F;oshadhagunawardena&#x2F;Projects&#x2F;personal&#x2F;personal-blog&#x2F;content&#x2F;about.md
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The page wasn&#x27;t being built at all. But wait—why does an About page need a date? It&#x27;s not a blog post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-investigation&quot;&gt;The Investigation&lt;&#x2F;h3&gt;
&lt;p&gt;The issue was in my &lt;code&gt;content&#x2F;_index.md&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;title &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Home&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginate_by &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sort_by &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;transparent &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;template &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;index.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;See that &lt;code&gt;sort_by = &quot;date&quot;&lt;&#x2F;code&gt;? That was the culprit. According to &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;section&#x2F;&quot;&gt;Zola&#x27;s documentation&lt;&#x2F;a&gt;, when you set &lt;code&gt;sort_by = &quot;date&quot;&lt;&#x2F;code&gt; in a section&#x27;s front matter, &lt;strong&gt;all pages in that section&lt;&#x2F;strong&gt; must have a date field. Pages without the required field are ignored during rendering and a warning is displayed.&lt;&#x2F;p&gt;
&lt;p&gt;This is documented behavior, but it&#x27;s easy to miss when you&#x27;re just trying to add a simple About page.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h3&gt;
&lt;p&gt;The fix had two parts:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Step 1&lt;&#x2F;strong&gt;: Add a &lt;code&gt;date&lt;&#x2F;code&gt; field to the About page. Since the root section uses &lt;code&gt;sort_by = &quot;date&quot;&lt;&#x2F;code&gt;, all pages need a date—even non-post pages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Step 2&lt;&#x2F;strong&gt;: Hide the date from displaying on the About page using &lt;code&gt;show_date = false&lt;&#x2F;code&gt; in the front matter.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;title &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Learn about Oshadha G...&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;template &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;page.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;weight &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;[extra]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hide_from_list &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;comments &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;After:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;title &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Learn about Oshadha G...&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;template &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;page.html&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;date &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2025-01-01  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Required because root section uses sort_by = &amp;quot;date&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;weight &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;[extra]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hide_from_list &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;comments &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;show_date &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Hide the awkward date from displaying
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;+++&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also had to create a custom &lt;code&gt;templates&#x2F;macros&#x2F;post_macros.html&lt;&#x2F;code&gt; to override the theme&#x27;s default meta macro and respect the &lt;code&gt;show_date&lt;&#x2F;code&gt; flag. But the front matter solution is the important part.&lt;&#x2F;p&gt;
&lt;p&gt;Now the About page builds correctly, appears in the correct order for pagination, but doesn&#x27;t display the awkward &quot;Published: 2025-01-01&quot; meta.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-lesson&quot;&gt;The Lesson&lt;&#x2F;h3&gt;
&lt;p&gt;Root-level sections in Zola behave differently than you might think. If you set &lt;code&gt;sort_by = &quot;date&quot;&lt;&#x2F;code&gt; in a section, &lt;strong&gt;all&lt;&#x2F;strong&gt; pages in that section must have a date field—even non-post pages like About pages.&lt;&#x2F;p&gt;
&lt;p&gt;While Zola&#x27;s docs mention this behavior, it&#x27;s the kind of detail that&#x27;s easy to gloss over when you&#x27;re focused on building features. The solution is straightforward:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Give every page a date if your section sorts by date (I use &lt;code&gt;2025-01-01&lt;&#x2F;code&gt; for static pages)&lt;&#x2F;li&gt;
&lt;li&gt;Use a custom macro override to conditionally hide dates where they don&#x27;t make sense&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For static pages like About, seeing &quot;Published: 2025-01-01&quot; is awkward, so the &lt;code&gt;show_date = false&lt;&#x2F;code&gt; flag was essential.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;quick-check&quot;&gt;Quick Check&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;re having similar issues with Zola pages not building:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Check your section&#x27;s &lt;code&gt;_index.md&lt;&#x2F;code&gt; for &lt;code&gt;sort_by&lt;&#x2F;code&gt; directives&lt;&#x2F;li&gt;
&lt;li&gt;Make sure all pages in that section have the required fields&lt;&#x2F;li&gt;
&lt;li&gt;Use &lt;code&gt;show_date = false&lt;&#x2F;code&gt; in front matter if you want to hide dates on static pages&lt;&#x2F;li&gt;
&lt;li&gt;Consider moving sorted content to a dedicated subsection&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Sometimes the simplest fixes are the most unexpected. And sometimes you need two fixes instead of one.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rust Macros vs Functions: What Java and Python Developers Should Know</title>
        <published>2025-11-01T00:00:00+00:00</published>
        <updated>2025-11-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/rust-macros-vs-functions/"/>
        <id>https://noos.blog/posts/rust-macros-vs-functions/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/rust-macros-vs-functions/">&lt;h3 id=&quot;the-question&quot;&gt;The Question&lt;&#x2F;h3&gt;
&lt;p&gt;When you first start with Rust, one thing that throws you off is the difference between macros and functions. Coming from Java or Python, this seems odd—why would you need both?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-s-the-difference&quot;&gt;What&#x27;s the Difference?&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s the simplest explanation: &lt;strong&gt;functions work on data, macros work on code&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In Python, you write:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;a + b
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Java, you write:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public static int &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; a, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; b) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; a + b;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both run at &lt;strong&gt;runtime&lt;&#x2F;strong&gt;—when your program executes.&lt;&#x2F;p&gt;
&lt;p&gt;But in Rust, macros run at &lt;strong&gt;compile time&lt;&#x2F;strong&gt;—before your code is even compiled. They&#x27;re code that writes code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-simple-example&quot;&gt;A Simple Example&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Function in Rust:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    x + y
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Macro in Rust:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;macro_rules! &lt;&#x2F;span&gt;&lt;span&gt;add {
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$y&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$x &lt;&#x2F;span&gt;&lt;span&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$y
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When you call the macro &lt;code&gt;add!(1, 2)&lt;&#x2F;code&gt;, the compiler literally rewrites your code to &lt;code&gt;1 + 2&lt;&#x2F;code&gt; before compiling. It&#x27;s like a smart find-and-replace.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-not-just-use-functions&quot;&gt;Why Not Just Use Functions?&lt;&#x2F;h3&gt;
&lt;p&gt;Macros solve problems that functions can&#x27;t:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Variable Arguments&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ever wonder how &lt;code&gt;println!&lt;&#x2F;code&gt; can take any number of arguments?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, name);
&lt;&#x2F;span&gt;&lt;span&gt;println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; and &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, a, b);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s because it&#x27;s a macro. Functions need fixed signatures; macros can accept variable patterns.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Code Generation&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Python has decorators like &lt;code&gt;@dataclass&lt;&#x2F;code&gt; that generate code for you:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dataclass
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Person&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    name: str
&lt;&#x2F;span&gt;&lt;span&gt;    age: int
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Java has annotations like &lt;code&gt;@Override&lt;&#x2F;code&gt; or Lombok&#x27;s &lt;code&gt;@Builder&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In Rust, this is done with macros:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug, Clone)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Person {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;age&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;derive&lt;&#x2F;code&gt; macro generates &lt;code&gt;Debug&lt;&#x2F;code&gt; and &lt;code&gt;Clone&lt;&#x2F;code&gt; implementations automatically.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Domain-Specific Languages (DSL)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A DSL is a miniature language tailored to a specific problem domain. Think of SQL—it&#x27;s not a general-purpose language, but a specialized one for databases.&lt;&#x2F;p&gt;
&lt;p&gt;You can create custom syntax with macros:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;html! {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;div class=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;container&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;p&amp;gt;Hello world&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;p&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This isn&#x27;t valid Rust code—it&#x27;s an HTML-like DSL that the macro converts into valid Rust. It lets you write HTML templates directly in your Rust code, which the macro transforms into function calls and data structures at compile time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mental-model-for-java-python-developers&quot;&gt;Mental Model for Java&#x2F;Python Developers&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Concept&lt;&#x2F;th&gt;&lt;th&gt;Python&#x2F;Java&lt;&#x2F;th&gt;&lt;th&gt;Rust&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Function&lt;&#x2F;td&gt;&lt;td&gt;Normal code that runs&lt;&#x2F;td&gt;&lt;td&gt;Same—runs at runtime&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Annotation&#x2F;Decorator&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;@Override&lt;&#x2F;code&gt;, &lt;code&gt;@dataclass&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;#[derive(...)]&lt;&#x2F;code&gt; macro&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Macro&lt;&#x2F;td&gt;&lt;td&gt;Doesn&#x27;t really exist&lt;&#x2F;td&gt;&lt;td&gt;Generates code at compile time&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Think of macros as &lt;strong&gt;super-powered annotations&lt;&#x2F;strong&gt;. In Java, annotations can generate some code (like Lombok does), but macros can generate &lt;em&gt;any&lt;&#x2F;em&gt; code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-to-use-what&quot;&gt;When to Use What?&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use functions&lt;&#x2F;strong&gt; for normal logic and calculations.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Use macros&lt;&#x2F;strong&gt; when you want to avoid boilerplate, create custom syntax, or generate code automatically.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;bottom-line&quot;&gt;Bottom Line&lt;&#x2F;h3&gt;
&lt;p&gt;Macros are Rust&#x27;s way of reducing repetition and enabling metaprogramming. They run before compilation and rewrite your code. If you&#x27;re coming from Java or Python, think of them as annotations or decorators on steroids—but they can do much more.&lt;&#x2F;p&gt;
&lt;p&gt;For most beginners, you don&#x27;t need to write macros. The standard library macros like &lt;code&gt;println!&lt;&#x2F;code&gt;, &lt;code&gt;vec!&lt;&#x2F;code&gt;, and &lt;code&gt;format!&lt;&#x2F;code&gt; will carry you far. But understanding &lt;em&gt;what&lt;&#x2F;em&gt; they are helps when you read Rust code and wonder why you see &lt;code&gt;!&lt;&#x2F;code&gt; everywhere.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;veykril.github.io&#x2F;tlborm&#x2F;&quot;&gt;The Little Book of Rust Macros&lt;&#x2F;a&gt; — A practical guide to writing macros in Rust.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch19-06-macros.html&quot;&gt;The Rust Programming Language: Macros&lt;&#x2F;a&gt; — Official Rust book chapter on macros.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rust-by-example&#x2F;macros.html&quot;&gt;Rust by Example: Macros&lt;&#x2F;a&gt; — Interactive examples to learn macros.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Domain-specific_language&quot;&gt;Domain-Specific Language (DSL)&lt;&#x2F;a&gt; — Wikipedia article explaining the concept.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Covers on Autopilot — Why I Let AI Paint the Edges</title>
        <published>2025-10-30T00:00:00+00:00</published>
        <updated>2025-10-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/ai-covers-on-autopilot/"/>
        <id>https://noos.blog/posts/ai-covers-on-autopilot/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/ai-covers-on-autopilot/">&lt;p&gt;There are jobs you can do by hand for years and never notice the drag. For me it was cover images. Each new post: open a design tool, stare at a blank canvas, export a PNG, wire it to the post. Not hard, just enough friction to make publishing feel heavier than it should.&lt;&#x2F;p&gt;
&lt;p&gt;This is a short note about shaving that yak with a tiny toolchain: a context‑aware prompt, an image model, and some guardrails in CI. No heroics—just removing a repeated decision so the writing flows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-automate-covers&quot;&gt;Why automate covers&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Consistency: 1200×630 every time. No more odd crops in link unfurls.&lt;&#x2F;li&gt;
&lt;li&gt;Momentum: publishing shouldn’t wait for “design time”.&lt;&#x2F;li&gt;
&lt;li&gt;Style: light background, a single accent, abstract shapes. Enough personality, zero fuss.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Over time the manual step was turning into a speed bump. That was the whole motivation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bumps-along-the-road-the-important-part&quot;&gt;The bumps along the road (the important part)&lt;&#x2F;h2&gt;
&lt;p&gt;I didn’t get it right on the first try. Here are the lessons that actually mattered.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Model and limits&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;At first I used Stable Horde for SDXL. It’s great and free, but CI doesn’t love queues. I kept seeing 400&#x2F;429s in the logs (invalid payload or rate‑limited). Switched to Replicate with an API token; still got rate limits sometimes, but at least the errors were predictable.&lt;&#x2F;p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Exact sizing&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;SDXL likes multiples of 64. I request 1024×576, then downscale to 1200×630 with Sharp. Crisp edges, correct aspect for social cards.&lt;&#x2F;p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Slugs vs. filenames&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;My fallback logic assumed &lt;code&gt;page.slug&lt;&#x2F;code&gt; exactly matched the filename. It didn’t—some posts derive the slug from the permalink. I fixed the template to compute &lt;code&gt;slug-from-permalink&lt;&#x2F;code&gt; and look for &lt;code&gt;&#x2F;images&#x2F;covers&#x2F;{slug}.png&lt;&#x2F;code&gt;. Simple, but that bug cost the most time.&lt;&#x2F;p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;“Why is it regenerating?”&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;CI starts from a clean checkout. If covers aren’t in Git, they disappear each run. I added a cache step that restores &lt;code&gt;static&#x2F;images&#x2F;covers&lt;&#x2F;code&gt; keyed to &lt;code&gt;content&#x2F;posts&#x2F;**&#x2F;*.md&lt;&#x2F;code&gt;. Now the generator truly skips when a PNG already exists.&lt;&#x2F;p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Context in prompts&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Pure tags produced vague images. I added a little context: title + ~240 chars of summary (description if present, otherwise a clean slice of the body). Still abstract, just more grounded.&lt;&#x2F;p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;One‑time resets&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Sometimes you do want a full refresh. There’s a manual “Run workflow” input (&lt;code&gt;force_regenerate=true&lt;&#x2F;code&gt;) that bypasses the “file exists” check once. Good for style changes or a model upgrade.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-we-decided-on-ai-at-all&quot;&gt;How we decided on AI at all&lt;&#x2F;h2&gt;
&lt;p&gt;I tried three options in order:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Hand‑made banners (historically): high quality, low velocity.&lt;&#x2F;li&gt;
&lt;li&gt;Programmatic shapes: reliable, but too repetitive.&lt;&#x2F;li&gt;
&lt;li&gt;AI with guardrails: abstract, brand‑aware, hands‑off once it’s set.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The third option hit the balance. The prompt constrains style; the model supplies variation. It’s not “art direction”, but it serves the post—and that’s the only job here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-pipeline-nuts-and-bolts&quot;&gt;The pipeline (nuts and bolts)&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Script: &lt;code&gt;scripts&#x2F;generate-ai-covers.mjs&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Scans &lt;code&gt;content&#x2F;posts&#x2F;*.md&lt;&#x2F;code&gt;, pulls &lt;code&gt;tags&lt;&#x2F;code&gt;, &lt;code&gt;title&lt;&#x2F;code&gt;, and a short summary.&lt;&#x2F;li&gt;
&lt;li&gt;Prompt (simplified):
&lt;blockquote&gt;
&lt;p&gt;Abstract, minimal illustration. Tags: {tags}. Title: {title}. Context: {summary}. Vector‑like, clean geometric shapes, high contrast, brand accent #d64a48 on #f6f7f4. No text.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Calls Replicate SDXL (model version from a secret), requests 1024×576, saves 1200×630 as &lt;code&gt;static&#x2F;images&#x2F;covers&#x2F;{slug}.png&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Skips generation if the PNG already exists (unless &lt;code&gt;FORCE_REGENERATE_COVERS=1&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Template: &lt;code&gt;themes&#x2F;radion&#x2F;templates&#x2F;page.html&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If a post doesn’t specify a cover, it tries &lt;code&gt;&#x2F;images&#x2F;covers&#x2F;{slug-from-permalink}.png&lt;&#x2F;code&gt; and hides the figure if missing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;CI: &lt;code&gt;.github&#x2F;workflows&#x2F;deploy.yml&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Restores a covers cache before generation; saves it after.&lt;&#x2F;li&gt;
&lt;li&gt;Optional manual input &lt;code&gt;force_regenerate&lt;&#x2F;code&gt; for a one‑time refresh.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-github-actions-brings&quot;&gt;What GitHub Actions brings&lt;&#x2F;h2&gt;
&lt;p&gt;The boring kind of power: reliability.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Every run restores the previous covers, generates only what’s missing, and embeds without a front‑matter tweak.&lt;&#x2F;li&gt;
&lt;li&gt;When rate‑limited, the post still publishes—missing images pick up on the next run.&lt;&#x2F;li&gt;
&lt;li&gt;A small verification stage lists the covers in &lt;code&gt;public&#x2F;images&#x2F;covers&lt;&#x2F;code&gt; and checks that posts actually reference them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;closing-the-loop&quot;&gt;Closing the loop&lt;&#x2F;h2&gt;
&lt;p&gt;This wasn’t about making the site “AI‑powered”. It was about removing a tiny friction point that kept breaking the flow. The rule of thumb I keep coming back to: automate anything that steals attention from writing. Covers were stealing attention. Now they aren’t.&lt;&#x2F;p&gt;
&lt;p&gt;And if the art ever needs a new feel? I flip the model or tweak the prompt, hit “Run workflow”, and let the pipeline repaint the edges while I get back to words.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Redis, TLS, and Deadpool: When Dependency Versions Collide</title>
        <published>2025-10-30T00:00:00+00:00</published>
        <updated>2025-10-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/redis-tls-deadpool-compatibility/"/>
        <id>https://noos.blog/posts/redis-tls-deadpool-compatibility/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/redis-tls-deadpool-compatibility/">&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h3&gt;
&lt;p&gt;I was working on a Rust project that uses Redis with TLS connections and a connection pool via &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt;. Everything was working fine until I ran &lt;code&gt;cargo check&lt;&#x2F;code&gt; and saw this warning:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;warning: the following packages contain code that will be rejected by a future version of Rust: redis v0.25.4
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s not something you want to see. It suggests your code will break in future Rust versions. So naturally, I tried to upgrade the &lt;code&gt;redis&lt;&#x2F;code&gt; crate to a newer version.&lt;&#x2F;p&gt;
&lt;p&gt;But here&#x27;s where things got interesting—every single attempt to upgrade past &lt;code&gt;v0.25.4&lt;&#x2F;code&gt; broke the build. Not just small errors, but fundamental incompatibilities between the &lt;code&gt;redis&lt;&#x2F;code&gt; crate and &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-i-tried&quot;&gt;What I Tried&lt;&#x2F;h3&gt;
&lt;p&gt;I went through the versions systematically, testing each major release:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v0.26.x&lt;&#x2F;strong&gt;: Broke immediately. Missing &lt;code&gt;TcpTls&lt;&#x2F;code&gt; variant in the &lt;code&gt;Tokio&lt;&#x2F;code&gt; enum, internal TLS types incomplete.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;v0.27.x&lt;&#x2F;strong&gt;: Same issues as v0.26.x. TLS support partially broken.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;v0.28.x&lt;&#x2F;strong&gt;: TLS types like &lt;code&gt;TlsConnParams&lt;&#x2F;code&gt; and &lt;code&gt;TcpTls&lt;&#x2F;code&gt; were still broken internally.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;v0.29.x&lt;&#x2F;strong&gt;: Now it requires a &lt;code&gt;connect_tcp_tls&lt;&#x2F;code&gt; trait that &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; doesn&#x27;t implement.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;v0.30.x, v0.31.x, v0.32.x&lt;&#x2F;strong&gt;: All require the same &lt;code&gt;connect_tcp_tls&lt;&#x2F;code&gt; trait.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The pattern became clear: somewhere between v0.25.4 and v0.29.0, the &lt;code&gt;redis&lt;&#x2F;code&gt; crate changed how it handles TLS connections, and &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; hasn&#x27;t caught up yet.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-root-cause&quot;&gt;The Root Cause&lt;&#x2F;h3&gt;
&lt;p&gt;The issue is that &lt;code&gt;redis&lt;&#x2F;code&gt; v0.25.4 is the last version before &lt;code&gt;connect_tcp_tls&lt;&#x2F;code&gt; was introduced. This new trait-based approach is cleaner architecturally, but it requires connection pool libraries to implement this trait. &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; v0.14.0 was built against the older API and doesn&#x27;t have this implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Versions between v0.25.4 and v0.29.0 are in a transitional state—they have some TLS support but it&#x27;s incomplete or broken.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;current-status&quot;&gt;Current Status&lt;&#x2F;h3&gt;
&lt;p&gt;After testing all these versions, I ended up staying with &lt;code&gt;redis v0.25.4&lt;&#x2F;code&gt;. Here&#x27;s the working configuration:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Cargo.toml:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;redis &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;=0.25.4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tokio-comp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tls-rustls&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;connection-manager&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;] }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;deadpool-redis &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;0.14.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;full&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;] }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tokio-rustls &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;0.23.4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rustls &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;0.20.8&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;rust-toolchain.toml:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[toolchain]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;channel &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1.74.1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;=&lt;&#x2F;code&gt; prefix on the redis version pins it exactly to v0.25.4, preventing accidental upgrades. And pinning Rust to &lt;code&gt;1.74.1&lt;&#x2F;code&gt; helps avoid future compatibility warnings.&lt;&#x2F;p&gt;
&lt;p&gt;This setup works reliably:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;TLS over &lt;code&gt;rediss:&#x2F;&#x2F;&lt;&#x2F;code&gt; connections work with &lt;code&gt;tokio-rustls&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Connection pooling via &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; functions correctly&lt;&#x2F;li&gt;
&lt;li&gt;No compilation warnings or errors&lt;&#x2F;li&gt;
&lt;li&gt;Stable and predictable behavior&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;what-to-avoid&quot;&gt;What to Avoid&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;re in a similar situation, avoid these version combinations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;redis v0.26.x–v0.28.x&lt;&#x2F;strong&gt;: TLS support is broken or incomplete. You&#x27;ll get compilation errors related to missing enum variants or broken TLS types.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;redis &amp;gt;= v0.29.0 with deadpool-redis&lt;&#x2F;strong&gt;: Won&#x27;t compile because &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; doesn&#x27;t implement the required &lt;code&gt;connect_tcp_tls&lt;&#x2F;code&gt; trait.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;deadpool-redis v0.15+&lt;&#x2F;strong&gt;: Currently not compatible with stable &lt;code&gt;redis&lt;&#x2F;code&gt; versions due to the same trait mismatch.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;alternative-approach&quot;&gt;Alternative Approach&lt;&#x2F;h3&gt;
&lt;p&gt;If you really need a newer &lt;code&gt;redis&lt;&#x2F;code&gt; version, you have one option: drop &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; and use &lt;code&gt;redis&lt;&#x2F;code&gt;&#x27;s built-in &lt;code&gt;ConnectionManager&lt;&#x2F;code&gt; or switch to a different pooling library like &lt;code&gt;bb8&lt;&#x2F;code&gt;. But this means rewriting your connection pool code, which might not be worth it for most projects.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-future&quot;&gt;The Future&lt;&#x2F;h3&gt;
&lt;p&gt;The situation is temporary, but it&#x27;s unclear when it will be resolved. The &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt; maintainers need to either:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Implement the &lt;code&gt;connect_tcp_tls&lt;&#x2F;code&gt; trait for newer &lt;code&gt;redis&lt;&#x2F;code&gt; versions, or&lt;&#x2F;li&gt;
&lt;li&gt;Update their API to work with the new connection model&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Until then, staying with &lt;code&gt;redis v0.25.4&lt;&#x2F;code&gt; and &lt;code&gt;deadpool-redis v0.14.0&lt;&#x2F;code&gt; is the most stable path forward. Yes, you&#x27;ll see that deprecation warning, but it&#x27;s better than having broken code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bottom-line&quot;&gt;Bottom Line&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes upgrading dependencies isn&#x27;t the right move. In this case, the ecosystem has a compatibility gap between &lt;code&gt;redis&lt;&#x2F;code&gt; and &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt;, and staying on the older version is the pragmatic choice. The warning is annoying, but it&#x27;s not breaking anything right now—and when the ecosystem catches up, you can upgrade then.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re building a new project and need Redis with TLS and pooling, consider whether you can use &lt;code&gt;redis&lt;&#x2F;code&gt;&#x27;s built-in &lt;code&gt;ConnectionManager&lt;&#x2F;code&gt; instead of &lt;code&gt;deadpool-redis&lt;&#x2F;code&gt;. If you&#x27;re maintaining an existing project, stick with what works: &lt;code&gt;redis v0.25.4&lt;&#x2F;code&gt; and &lt;code&gt;deadpool-redis v0.14.0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>When Rust Expects a String But Gets a Map</title>
        <published>2025-10-29T00:00:00+00:00</published>
        <updated>2025-10-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/rust-serde-datetime-deserialization-error/"/>
        <id>https://noos.blog/posts/rust-serde-datetime-deserialization-error/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/rust-serde-datetime-deserialization-error/">&lt;p&gt;I recently hit this error while working with a Rust application that reads from a database:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Database(&amp;quot;Kind: invalid type: map, expected an RFC 3339 formatted date and time string, labels: {}&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At first glance, it&#x27;s a bit cryptic. But the error message actually tells you exactly what&#x27;s wrong.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;When you have a Rust struct with a &lt;code&gt;DateTime&amp;lt;Utc&amp;gt;&lt;&#x2F;code&gt; field, Serde expects the JSON to contain an RFC 3339 formatted string like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2025-09-15T01:36:19Z&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But instead, your database (or JSON source) is storing it as a map&#x2F;object:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sec&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1694733379&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;nsec&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;199610000
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Serde can&#x27;t automatically convert a map into a &lt;code&gt;DateTime&lt;&#x2F;code&gt;—it needs a string it can parse.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-happens&quot;&gt;Why This Happens&lt;&#x2F;h2&gt;
&lt;p&gt;Different systems store timestamps differently:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Some databases store timestamps as Unix time objects with separate seconds and nanoseconds&lt;&#x2F;li&gt;
&lt;li&gt;Some JSON APIs return timestamps as nested objects&lt;&#x2F;li&gt;
&lt;li&gt;Other sources might use different date formats&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But Rust&#x27;s &lt;code&gt;DateTime&amp;lt;Utc&amp;gt;&lt;&#x2F;code&gt; with Serde defaults expects the standard RFC 3339 string format.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-fix&quot;&gt;The Fix&lt;&#x2F;h2&gt;
&lt;p&gt;You have two options:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Option 1: Fix the data source&lt;&#x2F;strong&gt; (if you control it)
Make sure your database or JSON source stores timestamps as RFC 3339 strings:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2025-09-15T01:36:19Z&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Option 2: Use a custom deserializer&lt;&#x2F;strong&gt; (if you can&#x27;t change the source)
Write a custom Serde deserializer to handle the map format:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;chrono::{DateTime, Utc};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;serde::{Deserialize, Deserializer};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;from_timestamp_map&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;&amp;#39;de&lt;&#x2F;span&gt;&lt;span&gt;, D&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;deserializer&lt;&#x2F;span&gt;&lt;span&gt;: D) -&amp;gt; Result&amp;lt;DateTime&amp;lt;Utc&amp;gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;D::&lt;&#x2F;span&gt;&lt;span&gt;Error&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;    D: Deserializer&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;&amp;#39;de&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Deserialize)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;Timestamp {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sec&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i64&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nsec&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; ts = Timestamp::deserialize(deserializer)?;
&lt;&#x2F;span&gt;&lt;span&gt;    DateTime::from_timestamp(ts.sec, ts.nsec)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;ok_or_else&lt;&#x2F;span&gt;&lt;span&gt;(|| serde::de::Error::custom(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Invalid timestamp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;ModelSelection {
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serde&lt;&#x2F;span&gt;&lt;span&gt;(deserialize_with = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;from_timestamp_map&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;created_at&lt;&#x2F;span&gt;&lt;span&gt;: DateTime&amp;lt;Utc&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The custom deserializer reads the &lt;code&gt;sec&lt;&#x2F;code&gt; and &lt;code&gt;nsec&lt;&#x2F;code&gt; fields from the map and constructs a &lt;code&gt;DateTime&amp;lt;Utc&amp;gt;&lt;&#x2F;code&gt; from them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bottom-line&quot;&gt;Bottom Line&lt;&#x2F;h2&gt;
&lt;p&gt;When Serde complains about expecting a string but getting a map for a &lt;code&gt;DateTime&lt;&#x2F;code&gt; field, your data source is storing timestamps in a format Serde doesn&#x27;t recognize. Either standardize on RFC 3339 strings, or write a deserializer to handle your specific format.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Confusing World of Cursor Payments</title>
        <published>2025-10-25T00:00:00+00:00</published>
        <updated>2025-10-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/cursor-payments-confusion/"/>
        <id>https://noos.blog/posts/cursor-payments-confusion/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/cursor-payments-confusion/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cursor.sh&#x2F;&quot;&gt;Cursor&lt;&#x2F;a&gt; is a powerful AI-powered code editor that many developers are excited about. However, one aspect that consistently causes confusion is its payment and pricing model. This post attempts to document some of these confusions based on recent experiences.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-confusion&quot;&gt;The Confusion&lt;&#x2F;h2&gt;
&lt;p&gt;The payment structure for Cursor can be unclear in several ways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Free tier limits&lt;&#x2F;strong&gt;: What exactly counts toward your monthly quota? Is it AI completions, chat requests, or something else?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Billing cycles&lt;&#x2F;strong&gt;: When does your quota reset? Is it calendar month, or from when you first started using the service?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Payment methods&lt;&#x2F;strong&gt;: What payment options are available, and how are they processed?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Usage tracking&lt;&#x2F;strong&gt;: How can you monitor your usage to avoid unexpected charges or hitting limits?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These ambiguities can lead to surprises—either unexpected charges or finding yourself blocked when you thought you had quota remaining.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;&#x2F;h2&gt;
&lt;p&gt;For developers evaluating whether to adopt Cursor, understanding the cost and payment structure is important. Unclear pricing creates friction and can be a barrier to adoption, especially for individual developers or small teams on tight budgets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-to-do&quot;&gt;What to Do&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re considering Cursor:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Check the official pricing page&lt;&#x2F;strong&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;cursor.sh&#x2F;pricing&quot;&gt;cursor.sh&#x2F;pricing&lt;&#x2F;a&gt;) for current information&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Monitor your usage&lt;&#x2F;strong&gt; if there&#x27;s a dashboard or settings page that shows quota&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reach out to support&lt;&#x2F;strong&gt; if you&#x27;re confused about billing or limits&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Document what you learn&lt;&#x2F;strong&gt; since this information may not be clearly documented&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;important-disclaimer&quot;&gt;Important Disclaimer&lt;&#x2F;h2&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Please note&lt;&#x2F;strong&gt;: Pricing, payment structures, and feature availability for Cursor are subject to change. The information in this post reflects experiences at the time of writing and may be outdated by the time you read this. Always refer to Cursor&#x27;s official documentation and pricing pages for the most current information.&lt;&#x2F;p&gt;
&lt;p&gt;The goal of this post is to highlight that these aspects can be confusing, not to provide definitive answers that may become incorrect over time.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you&#x27;ve experienced similar confusion with Cursor&#x27;s pricing or payment model, you&#x27;re not alone. Hopefully, this clarity improves as the platform matures.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How Embracing Rust Sharpens the Mind — and Elevates Teams</title>
        <published>2025-10-22T00:00:00+00:00</published>
        <updated>2025-10-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/how-embracing-rust-sharpens-the-mind/"/>
        <id>https://noos.blog/posts/how-embracing-rust-sharpens-the-mind/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/how-embracing-rust-sharpens-the-mind/">&lt;p&gt;When you decide to work in Rust, something subtle begins to shift. It’s not just about learning a new syntax or mastering memory safety. It’s about engaging a mode of thinking that demands attention, clarity, and responsibility. In doing so, you develop stronger focus, sharpened critical thinking, and what might best be called responsible individualism. And when one person grows in that way, the ripple effects can touch an entire team or organization.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I’ll explore three interlocking themes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Focus&lt;&#x2F;strong&gt; — how Rust trains sustained, precise attention&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Critical Thinking&lt;&#x2F;strong&gt; — how Rust forces richer reasoning and choice&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Individualism &amp;amp; Contribution&lt;&#x2F;strong&gt; — how mastering Rust empowers an individual who then strengthens the whole&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-focus-cultivating-attention-in-the-code&quot;&gt;1. Focus: Cultivating Attention in the Code&lt;&#x2F;h2&gt;
&lt;p&gt;In many languages, you type, you compile, you run. Mistakes show up in logs or in production. With Rust, the compiler intervenes early and often. You’ll catch ownership issues, borrowing conflicts, lifetime mismatches—not after deployment, but at compile time.&lt;&#x2F;p&gt;
&lt;p&gt;Consider these moments:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You borrow a value; you pause and ask: Who currently has access?&lt;&#x2F;li&gt;
&lt;li&gt;You annotate a function; you reflect: What lifetimes are involved, and why must this reference stay valid here?&lt;&#x2F;li&gt;
&lt;li&gt;You handle a &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;&#x2F;code&gt;; you evaluate: What happens when this fails?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are micro‑acts of attention. Over time, they build a habit of precision and presence. Rust trains you not to write just-sufficient code—but code you understand and own.&lt;&#x2F;p&gt;
&lt;p&gt;When a developer learns this mode of working, the whole team benefits. Code reviews become richer. Discussions shift from “does it compile?” to “why did this compile, and is this the best way?” The developer’s focus becomes a team asset.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-critical-thinking-building-sound-reasoning&quot;&gt;2. Critical Thinking: Building Sound Reasoning&lt;&#x2F;h2&gt;
&lt;p&gt;Beyond focus, Rust forces you into deeper reasoning. Ownership, borrowing, lifetimes, concurrency—they’re not mere mechanics, they’re invitations to think about how your code works, why it is safe, and what assumptions you’re making.&lt;&#x2F;p&gt;
&lt;p&gt;In philosophical terms, this resembles the concept of technē—the idea of craft or making, where knowing how to do something is inseparable from knowing why you do it. In programming, Rust becomes a modern technē: you not only write code, you reason about resources, validity, and correctness.&lt;&#x2F;p&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You ask: “If I pass this borrow here, could someone else modify the data concurrently?”&lt;&#x2F;li&gt;
&lt;li&gt;You reason: “If this value moves and the original is used afterward, I invite a compile error. Why does Rust forbid this? What risk is it preventing?”&lt;&#x2F;li&gt;
&lt;li&gt;You model: “When threads share &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, what invariants do I preserve? What could go wrong if I slip up?”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These questions steer you away from simple feature‑delivery and toward thoughtful system design. And when more developers engage in this kind of thinking, the codebase becomes more robust, maintainable, and predictable.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-individualism-contribution-mastery-that-scales&quot;&gt;3. Individualism &amp;amp; Contribution: Mastery That Scales&lt;&#x2F;h2&gt;
&lt;p&gt;“Individualism” often carries negative connotations—but here I mean a positive kind: an individual taking responsibility for their craft, developing depth of understanding, and then choosing to bring that to the group. Rust supports exactly that.&lt;&#x2F;p&gt;
&lt;p&gt;The philosophy of software craftsmanship frames development as more than meeting a deadline—it’s about mastery, continuous improvement, and professional pride. When someone invests the time to master Rust’s rule set, they gain both confidence and clarity. They can mentor others, raise standards, and uplift the team.&lt;&#x2F;p&gt;
&lt;p&gt;In turn, the team and organization benefit:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Decreased bugs and runtime surprises&lt;&#x2F;li&gt;
&lt;li&gt;Clearer design boundaries and documentation&lt;&#x2F;li&gt;
&lt;li&gt;A culture where thinking matters and craftsmanship is respected&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In this way, the individual’s growth becomes a catalyst for collective growth. One Rust‑savvy engineer can shift a team’s mindset from “just ship” to “ship well.”&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;philosophical-reflection-craft-virtue-and-the-common-good&quot;&gt;Philosophical Reflection: Craft, Virtue, and the Common Good&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s step into the philosophical background. In the virtue‑ethics tradition, the focus isn’t only on what you do, but who you become by doing it. When you practise a craft with discipline, you internalize values: care, integrity, attention to detail.&lt;&#x2F;p&gt;
&lt;p&gt;Rust—as a programming language—is more than a tool; it’s a training ground. The rules around ownership and safety aren’t arbitrary; they coax you into a mindset of accountability. That mindset echoes technē: making with purpose, not just automatism.&lt;&#x2F;p&gt;
&lt;p&gt;At the same time, contributing to a team, a codebase, or an open ecosystem aligns with the idea of the common good. Technology can be shaped for more than profit—it can serve that which is good, beautiful, and durable.&lt;&#x2F;p&gt;
&lt;p&gt;Putting those threads together: writing Rust becomes a practice in character as much as a skill in code. You become someone who thinks deeply about structure, consequences, and shared responsibility. And that transformation benefits every person you code with.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Working with Rust gives you more than faster performance or fewer runtime bugs. It gives you a path to sharpen your focus, refine your thinking, and develop a personal mastery that contributes to something larger than yourself.&lt;br &#x2F;&gt;
If you’re a developer seeking to grow not just your output but your way of working, Rust offers a compelling challenge.&lt;br &#x2F;&gt;
If you’re a leader or a team building culture, supporting your engineers in learning Rust can become a signal of valuing craft, clarity, and long‑term thinking.&lt;&#x2F;p&gt;
&lt;p&gt;In the end, the code we build reflects the minds we train. With Rust, the training is real—and the benefits run deep.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Software_craftsmanship&quot;&gt;Software Craftsmanship&lt;&#x2F;a&gt; – the movement emphasizing craftsmanship in programming.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Techne&quot;&gt;Techne&lt;&#x2F;a&gt; – the Greek philosophical concept of craft and skill.&lt;&#x2F;li&gt;
&lt;li&gt;Jonsson, M.; Tholander, C. &lt;a href=&quot;https:&#x2F;&#x2F;www.diva-portal.org&#x2F;smash&#x2F;get&#x2F;diva2%3A1902016&#x2F;FULLTEXT02.pdf&quot;&gt;&lt;em&gt;Aiming for Virtue in Programming with Generative AI&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; (2023) – On craft, judgement and programming.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>GitHub Actions: When Automation Meets Reality</title>
        <published>2025-10-19T00:00:00+00:00</published>
        <updated>2025-10-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/github-actions-when-automation-meets-reality/"/>
        <id>https://noos.blog/posts/github-actions-when-automation-meets-reality/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/github-actions-when-automation-meets-reality/">&lt;p&gt;When you first set up GitHub Actions for a project, it feels like magic. Push your code, and the CI&#x2F;CD pipeline takes care of everything—building, testing, deploying. But sometimes, the magic breaks. And when it does, you learn things you never expected to learn.&lt;&#x2F;p&gt;
&lt;p&gt;I recently went through a journey with GitHub Actions that taught me a lot about the gap between &quot;this should work&quot; and &quot;this actually works.&quot; Here&#x27;s what I learned, without exposing too many project details.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;background-building-with-zola&quot;&gt;Background: Building with Zola&lt;&#x2F;h2&gt;
&lt;p&gt;For context, this blog is built using &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;, a static site generator written in Rust. Zola takes Markdown files and templates, processes them, and outputs a static HTML site. It&#x27;s fast, simple, and doesn&#x27;t require a runtime—just compile your site and serve the static files.&lt;&#x2F;p&gt;
&lt;p&gt;The deployment process is straightforward:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Write content in Markdown&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;zola build&lt;&#x2F;code&gt; to generate static HTML&lt;&#x2F;li&gt;
&lt;li&gt;Deploy the generated files to a hosting service&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This makes Zola an ideal candidate for GitHub Pages: you build once, deploy the static files, and GitHub Pages serves them. The challenge comes when you want to automate this process with GitHub Actions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-promise-of-automation&quot;&gt;The Promise of Automation&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub Actions promises a lot:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic builds on every push&lt;&#x2F;li&gt;
&lt;li&gt;Consistent deployment environments&lt;&#x2F;li&gt;
&lt;li&gt;Less manual work, fewer human errors&lt;&#x2F;li&gt;
&lt;li&gt;Integration with GitHub Pages for static sites&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The setup looks straightforward:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.github&#x2F;workflows&#x2F;deploy.yml&lt;&#x2F;code&gt; file&lt;&#x2F;li&gt;
&lt;li&gt;Define your build steps&lt;&#x2F;li&gt;
&lt;li&gt;Push and watch it work&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;But reality has a way of complicating things.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;issue-1-the-submodule-problem&quot;&gt;Issue #1: The Submodule Problem&lt;&#x2F;h2&gt;
&lt;p&gt;One of the first issues I encountered was related to git submodules. If your project includes external dependencies managed as submodules, GitHub Actions needs explicit configuration to handle them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Error:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Error: fatal: No url found for submodule path &amp;#39;themes&#x2F;radion&amp;#39; in .gitmodules
&lt;&#x2F;span&gt;&lt;span&gt;Error: The process &amp;#39;&#x2F;usr&#x2F;bin&#x2F;git&amp;#39; failed with exit code 128
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;The Root Cause:&lt;&#x2F;strong&gt;
GitHub Actions tries to fetch submodules by default, but if your &lt;code&gt;.gitmodules&lt;&#x2F;code&gt; file is missing or incomplete, or if the submodule path exists but isn&#x27;t properly configured, the build fails.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Solution:&lt;&#x2F;strong&gt;
If you&#x27;re not actually using submodules (maybe you copied files directly), you have two options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Remove the submodule completely:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --cached&lt;&#x2F;span&gt;&lt;span&gt; themes&#x2F;radion
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rm -rf&lt;&#x2F;span&gt;&lt;span&gt; themes&#x2F;radion&#x2F;.git
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; add themes&#x2F;radion
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; commit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -m &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Convert submodule to regular directory&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure GitHub Actions to skip submodules:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;actions&#x2F;checkout@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;submodules&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I went with option 1 because I wasn&#x27;t actually using submodules—I had copied the theme files directly into the repository. The submodule reference was leftover from an earlier setup.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;issue-2-branch-confusion&quot;&gt;Issue #2: Branch Confusion&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub Pages supports two deployment methods:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions:&lt;&#x2F;strong&gt; Build your site using a workflow&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Branch-based:&lt;&#x2F;strong&gt; Serve files directly from a branch (like &lt;code&gt;gh-pages&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I started with GitHub Actions, but ran into issues. The build kept failing, and debugging CI&#x2F;CD pipelines can be frustrating—you push, wait, check logs, repeat.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Revelation:&lt;&#x2F;strong&gt;
Sometimes, manual deployment to a &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch is simpler and more reliable. You get:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Full control over when and how you deploy&lt;&#x2F;li&gt;
&lt;li&gt;Ability to test locally before deploying&lt;&#x2F;li&gt;
&lt;li&gt;No hidden automation surprises&lt;&#x2F;li&gt;
&lt;li&gt;Easier debugging (you can inspect the built files directly)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The Manual Approach:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Build locally
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;zola&lt;&#x2F;span&gt;&lt;span&gt; build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --output-dir&lt;&#x2F;span&gt;&lt;span&gt; public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --force
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Deploy to gh-pages branch
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -B&lt;&#x2F;span&gt;&lt;span&gt; gh-pages
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rsync -av --delete --exclude&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.git&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; public&#x2F; .
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; add&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -A
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; commit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -m &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Publish: &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; push&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; origin gh-pages
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; checkout main
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Is this less &quot;modern&quot;? Maybe. But it&#x27;s transparent, predictable, and gives you control when you need it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;update-automation-wins-after-all&quot;&gt;Update: Automation Wins After All&lt;&#x2F;h2&gt;
&lt;p&gt;After writing the initial version of this post, I realized something: maintaining two branches manually was becoming a burden. The workflow was reliable, but it required:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Remembering to switch branches&lt;&#x2F;li&gt;
&lt;li&gt;Running build commands&lt;&#x2F;li&gt;
&lt;li&gt;Manually syncing files&lt;&#x2F;li&gt;
&lt;li&gt;Switching back to main&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The Solution:&lt;&#x2F;strong&gt;
I revisited GitHub Actions, but this time with a clear understanding of the issues:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Configured submodules properly (&lt;code&gt;submodules: false&lt;&#x2F;code&gt; since we don&#x27;t use them)&lt;&#x2F;li&gt;
&lt;li&gt;Used a reliable deployment action (&lt;code&gt;peaceiris&#x2F;actions-gh-pages&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Set up the workflow to trigger on push to &lt;code&gt;main&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;The Result:&lt;&#x2F;strong&gt;
Now I just push to &lt;code&gt;main&lt;&#x2F;code&gt;, and GitHub Actions handles everything:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Builds the site automatically&lt;&#x2F;li&gt;
&lt;li&gt;Deploys to &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch&lt;&#x2F;li&gt;
&lt;li&gt;Updates GitHub Pages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;What Changed:&lt;&#x2F;strong&gt;
The key was understanding the submodule issue first. Once that was resolved, the automation became reliable. The workflow is simple and transparent—I can see exactly what&#x27;s happening in the Actions tab.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Lesson:&lt;&#x2F;strong&gt;
Sometimes automation &lt;em&gt;does&lt;&#x2F;em&gt; make sense. The difference between my first attempt and my second attempt was:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Understanding the root causes (submodules, branch setup)&lt;&#x2F;li&gt;
&lt;li&gt;Using proven, maintained actions&lt;&#x2F;li&gt;
&lt;li&gt;Starting simple and adding complexity only when needed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The manual approach taught me what was happening under the hood. That knowledge made the automation reliable.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;issue-3-missing-scripts-and-assets&quot;&gt;Issue #3: Missing Scripts and Assets&lt;&#x2F;h2&gt;
&lt;p&gt;When deploying to GitHub Pages, you need to ensure all your assets are included. This might seem obvious, but it&#x27;s easy to miss.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;&#x2F;strong&gt;
Your site builds locally, but when deployed, certain features don&#x27;t work—like a theme toggle button that does nothing, or search functionality that&#x27;s broken.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Cause:&lt;&#x2F;strong&gt;
JavaScript files weren&#x27;t being included in the HTML. The build process generated the files, but the HTML templates weren&#x27;t referencing them correctly, or the script tags were missing entirely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;&#x2F;strong&gt;
Manually verify that all necessary scripts are loaded:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check your HTML templates&lt;&#x2F;li&gt;
&lt;li&gt;Ensure script tags are in the correct order&lt;&#x2F;li&gt;
&lt;li&gt;Verify paths are correct (especially for GitHub Pages subdirectory paths)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Sometimes, the simplest solution is to open the generated HTML and check what&#x27;s actually there.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;issue-4-the-abstraction-trap&quot;&gt;Issue #4: The Abstraction Trap&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the thing about automation: when it works, it&#x27;s great. When it doesn&#x27;t, you&#x27;re debugging an abstraction layer you may not fully understand.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Pattern:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Something breaks&lt;&#x2F;li&gt;
&lt;li&gt;You check GitHub Actions logs&lt;&#x2F;li&gt;
&lt;li&gt;You see an error message&lt;&#x2F;li&gt;
&lt;li&gt;You make a change&lt;&#x2F;li&gt;
&lt;li&gt;You push and wait&lt;&#x2F;li&gt;
&lt;li&gt;Repeat&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This cycle can be slow, especially if your builds take a few minutes each time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Alternative:&lt;&#x2F;strong&gt;
With manual deployment:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Build locally (instant feedback)&lt;&#x2F;li&gt;
&lt;li&gt;Test locally&lt;&#x2F;li&gt;
&lt;li&gt;Deploy when ready&lt;&#x2F;li&gt;
&lt;li&gt;Inspect the deployed files directly&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The feedback loop is faster, and you understand every step.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;issue-5-github-pages-source-branch-mismatch&quot;&gt;Issue #5: GitHub Pages Source Branch Mismatch&lt;&#x2F;h2&gt;
&lt;p&gt;Even after getting the deployment pipeline working, some issues only surfaced when viewing the site in production. These weren&#x27;t build failures—they were configuration problems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-github-pages-source-branch-mismatch&quot;&gt;The GitHub Pages Source Branch Mismatch&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;The Symptom:&lt;&#x2F;strong&gt;
After deploying fixes, the blog post updates weren&#x27;t appearing on the live site. The build succeeded, GitHub Actions showed successful deployment, but visiting the site showed old content.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Root Cause:&lt;&#x2F;strong&gt;
GitHub Pages has two deployment methods:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;&#x2F;strong&gt; - Pages builds from Actions workflow output&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Branch-based&lt;&#x2F;strong&gt; - Pages serves files directly from a branch (like &lt;code&gt;gh-pages&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The problem was a configuration mismatch: GitHub Pages was set to use &quot;GitHub Actions&quot; as the source, but our workflow was deploying to the &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch. GitHub Pages was trying to serve from the Actions output, not the branch we were deploying to.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;&#x2F;strong&gt;
Changed GitHub Pages source back to &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch in repository settings:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Go to repository Settings → Pages&lt;&#x2F;li&gt;
&lt;li&gt;Under &quot;Source&quot;, select &quot;Deploy from a branch&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Choose &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch and &lt;code&gt;&#x2F; (root)&lt;&#x2F;code&gt; folder&lt;&#x2F;li&gt;
&lt;li&gt;Save&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Why This Happened:&lt;&#x2F;strong&gt;
When you first set up GitHub Actions, GitHub Pages might automatically switch to &quot;GitHub Actions&quot; mode. If your workflow deploys to &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch (which is common), you need to ensure the Pages source matches where you&#x27;re deploying.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Lesson:&lt;&#x2F;strong&gt;
Always verify that your GitHub Pages source matches your deployment target. If your Actions workflow deploys to &lt;code&gt;gh-pages&lt;&#x2F;code&gt;, make sure Pages is configured to serve from that branch. Configuration mismatches can cause silent failures where everything appears to work but content doesn&#x27;t update.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-across-browsers&quot;&gt;Testing Across Browsers&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;&#x2F;strong&gt;
Different browsers cache differently. Chrome might show updated content while Firefox shows old content, or vice versa. However, in this case, the issue wasn&#x27;t browser caching—it was a configuration mismatch.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The Solution:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Always verify GitHub Pages source matches your deployment method&lt;&#x2F;li&gt;
&lt;li&gt;Check GitHub Actions logs to confirm deployment succeeded&lt;&#x2F;li&gt;
&lt;li&gt;Wait 2-5 minutes for GitHub Pages to rebuild after deployment&lt;&#x2F;li&gt;
&lt;li&gt;Use hard refresh (Cmd+Shift+R &#x2F; Ctrl+Shift+R) when testing&lt;&#x2F;li&gt;
&lt;li&gt;Test in multiple browsers to rule out caching issues&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;What I Learned:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Pages configuration must match your deployment method&lt;&#x2F;li&gt;
&lt;li&gt;Always verify the deployment target matches the Pages source&lt;&#x2F;li&gt;
&lt;li&gt;Testing locally doesn&#x27;t catch configuration mismatches&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;when-to-use-github-actions&quot;&gt;When to Use GitHub Actions&lt;&#x2F;h2&gt;
&lt;p&gt;Don&#x27;t get me wrong—GitHub Actions is powerful and useful. Use it when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You need automated testing on every commit&lt;&#x2F;li&gt;
&lt;li&gt;You want consistent build environments&lt;&#x2F;li&gt;
&lt;li&gt;Multiple people are deploying&lt;&#x2F;li&gt;
&lt;li&gt;You need to run expensive operations (like building large projects)&lt;&#x2F;li&gt;
&lt;li&gt;You want to enforce code quality checks&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;But consider manual deployment when:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Your build process is simple&lt;&#x2F;li&gt;
&lt;li&gt;You want full control&lt;&#x2F;li&gt;
&lt;li&gt;You&#x27;re deploying infrequently&lt;&#x2F;li&gt;
&lt;li&gt;You want to understand every step&lt;&#x2F;li&gt;
&lt;li&gt;Debugging automation is slowing you down&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Understand your dependencies:&lt;&#x2F;strong&gt; Know whether you&#x27;re using submodules, npm packages, or other external resources. This affects your CI&#x2F;CD setup.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test locally first:&lt;&#x2F;strong&gt; Always build and test your site locally before relying on automation. Automation catches mistakes, but you should catch them first.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep it simple:&lt;&#x2F;strong&gt; If automation adds complexity without clear benefits, consider if manual processes might be better for your use case.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inspect the output:&lt;&#x2F;strong&gt; When something doesn&#x27;t work, look at the actual generated files. HTML errors, missing assets, and broken links are often visible in the source.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify GitHub Pages configuration matches deployment:&lt;&#x2F;strong&gt; If your Actions workflow deploys to &lt;code&gt;gh-pages&lt;&#x2F;code&gt; branch, ensure GitHub Pages source is set to that branch. Configuration mismatches cause silent failures.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test across browsers:&lt;&#x2F;strong&gt; Different browsers cache differently. Always test in multiple browsers after deployment, and use hard refresh when debugging.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document your process:&lt;&#x2F;strong&gt; Whether you use automation or manual deployment, document it. Future you (and your team) will thank you.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub Actions is a powerful tool, but it&#x27;s not always the right tool—at least not immediately. My journey started with frustration, moved to manual control, and ended with successful automation.&lt;&#x2F;p&gt;
&lt;p&gt;The manual approach wasn&#x27;t wasted effort. It taught me:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What was happening under the hood&lt;&#x2F;li&gt;
&lt;li&gt;How the build process worked&lt;&#x2F;li&gt;
&lt;li&gt;What could go wrong and why&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That knowledge made the automation reliable. When I returned to GitHub Actions, I understood the root causes and could configure it correctly.&lt;&#x2F;p&gt;
&lt;p&gt;The key is to choose the approach that fits your project, your team, and your workflow. Sometimes that means starting manually to understand the process, then automating once you know what you&#x27;re automating.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of the day, the goal is to deploy reliably and efficiently. Whether that&#x27;s through GitHub Actions, manual commands, or a hybrid approach, what matters is that it works for you—and that you understand why it works.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&quot;&gt;GitHub Actions Documentation&lt;&#x2F;a&gt; – Official documentation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;pages&quot;&gt;GitHub Pages Deployment&lt;&#x2F;a&gt; – Deployment strategies&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Tools-Submodules&quot;&gt;Managing Git Submodules&lt;&#x2F;a&gt; – Git submodule basics&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why Rust Makes You a Better Engineer</title>
        <published>2025-10-16T00:00:00+00:00</published>
        <updated>2025-10-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/why-rust-makes-you-better-engineer/"/>
        <id>https://noos.blog/posts/why-rust-makes-you-better-engineer/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/why-rust-makes-you-better-engineer/">&lt;p&gt;If you&#x27;ve ever tried learning Rust, chances are the compiler pushed back at you—hard. It may feel relentless at times, especially compared to more permissive languages like Python, JavaScript, or even Java. But here&#x27;s the key: that friction is the point. Rust doesn&#x27;t just run your code safely—it teaches you to &lt;strong&gt;think differently&lt;&#x2F;strong&gt; about correctness, safety, and performance.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, we&#x27;ll break down the key concepts that make Rust unique and walk through what the compiler enforces—and why that&#x27;s a good thing in the long run.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-strict-type-system&quot;&gt;1. Strict Type System&lt;&#x2F;h2&gt;
&lt;p&gt;Rust&#x27;s type system is precise, and it expects you to be explicit about what types you&#x27;re working with. There&#x27;s no silent coercion between mismatched types. This can feel rigid at first, but it eliminates entire classes of bugs you&#x27;d otherwise only see at runtime.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-type-mismatch&quot;&gt;Example: Type mismatch&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    a + b
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; y: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u32 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; sum = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(x, y); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; error: mismatched types
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust won&#x27;t implicitly convert between &lt;code&gt;u32&lt;&#x2F;code&gt; and &lt;code&gt;i32&lt;&#x2F;code&gt;. You&#x27;ll need to cast it explicitly:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; sum = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(x, y as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This forces you to make conversion decisions intentionally and not by accident.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-error-handling-no-exceptions&quot;&gt;2. Error Handling: No Exceptions&lt;&#x2F;h2&gt;
&lt;p&gt;Rust does not have exceptions. Instead, it uses the &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; types for fallible operations. You must explicitly handle errors or propagate them. Ignoring them is not allowed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-required-handling-of-result&quot;&gt;Example: Required handling of &lt;code&gt;Result&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::fs::File;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; file = File::open(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;config.txt&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; warning&#x2F;error: unused `Result`
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To resolve this, you can either handle the error:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span&gt;File::open(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;config.txt&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) {
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(f) =&amp;gt; println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;File opened: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, f),
&lt;&#x2F;span&gt;&lt;span&gt;    Err(e) =&amp;gt; eprintln!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Failed to open file: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, e),
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or propagate it using the &lt;code&gt;?&lt;&#x2F;code&gt; operator in a function that returns a &lt;code&gt;Result&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;open_file&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; std::io::Result&amp;lt;()&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; _file = File::open(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;config.txt&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)?;
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This enforces a programming style where failure is expected and dealt with.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-immutability-by-default&quot;&gt;3. Immutability by Default&lt;&#x2F;h2&gt;
&lt;p&gt;Variables are immutable unless explicitly marked otherwise. This encourages predictability and thread-safety, even in single-threaded contexts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-immutable-variable&quot;&gt;Example: Immutable variable&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; name = String::from(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Alice&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push_str&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; Smith&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; error: cannot borrow as mutable
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The correct way:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; name = String::from(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Alice&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push_str&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; Smith&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This simple rule helps reduce bugs from unintended mutation, especially in shared state.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-ownership-and-move-semantics&quot;&gt;4. Ownership and Move Semantics&lt;&#x2F;h2&gt;
&lt;p&gt;Rust enforces a unique ownership model. Every value in Rust has a single owner, and when that owner goes out of scope, the value is dropped. You can move ownership, borrow it temporarily, or clone the data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-moved-value-error&quot;&gt;Example: Moved value error&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;takes_ownership&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;: String) {
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, s);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; s1 = String::from(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;takes_ownership&lt;&#x2F;span&gt;&lt;span&gt;(s1);
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, s1); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; error: value was moved
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once ownership is transferred, you can no longer use the original. This prevents bugs like double-free or use-after-free that are common in C&#x2F;C++.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-borrowing-rules&quot;&gt;5. Borrowing Rules&lt;&#x2F;h2&gt;
&lt;p&gt;Instead of transferring ownership, you can borrow references. Borrowing can be either immutable (&lt;code&gt;&amp;amp;T&lt;&#x2F;code&gt;) or mutable (&lt;code&gt;&amp;amp;mut T&lt;&#x2F;code&gt;), but Rust enforces strict rules:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You can have &lt;strong&gt;multiple immutable borrows&lt;&#x2F;strong&gt; at the same time.&lt;&#x2F;li&gt;
&lt;li&gt;Or &lt;strong&gt;one mutable borrow&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;But not both.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is enforced at compile time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;invalid-example-two-mutable-borrows&quot;&gt;Invalid example: Two mutable borrows&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; s = String::from(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; r1 = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; s;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; r2 = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; s; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; error: cannot borrow `s` twice mutably
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler ensures that you never access memory from multiple places in ways that could cause race conditions or undefined behavior.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-lifetimes&quot;&gt;6. Lifetimes&lt;&#x2F;h2&gt;
&lt;p&gt;Lifetimes are Rust&#x27;s way of tracking how long references are valid. The compiler uses lifetimes to prevent dangling references.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-dangling-reference-compile-error&quot;&gt;Example: Dangling reference (compile error)&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result;
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; s = String::from(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;temporary&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;        result = &amp;amp;s; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; s does not live long enough
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, result); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; would be a dangling reference
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust will reject this code, saving you from accessing freed memory. Often, you&#x27;ll write explicit lifetime annotations in function signatures when dealing with multiple references.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-concurrency-without-data-races&quot;&gt;7. Concurrency Without Data Races&lt;&#x2F;h2&gt;
&lt;p&gt;Rust prevents data races at compile time using its ownership and borrowing rules. When you need to share mutable state between threads, you must use thread-safe abstractions like &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-thread-safe-counter&quot;&gt;Example: Thread-safe counter&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::sync::{Arc, Mutex};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::thread;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; counter = Arc::new(Mutex::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; handles = vec![];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;_ in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; counter = Arc::clone(&amp;amp;counter);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; handle = thread::spawn(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;move &lt;&#x2F;span&gt;&lt;span&gt;|| {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; num = counter.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            *num += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;        handles.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(handle);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; handle in handles {
&lt;&#x2F;span&gt;&lt;span&gt;        handle.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Result: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, *counter.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In many other languages, this would compile fine but possibly fail at runtime. In Rust, the compiler ensures that shared state is accessed safely.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Rust asks a lot from the developer upfront. You&#x27;ll be forced to think about ownership, lifetimes, mutability, and error paths very early in the process. But once your code compiles, it&#x27;s often rock-solid. You&#x27;ll find fewer runtime crashes, undefined behaviors, or memory leaks.&lt;&#x2F;p&gt;
&lt;p&gt;More importantly, Rust helps you develop habits that transfer to any language: understanding lifecycles, reducing shared mutable state, and always considering failure cases.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;&quot;&gt;The Rust Book&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;rust-by-example&#x2F;&quot;&gt;Rust by Example&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rustlings&quot;&gt;Rustlings: Interactive CLI Exercises&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cheats.rs&#x2F;&quot;&gt;cheats.rs: Rust best practices&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How my terminal looks</title>
        <published>2025-10-13T00:00:00+00:00</published>
        <updated>2025-10-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/how-my-terminal-looks/"/>
        <id>https://noos.blog/posts/how-my-terminal-looks/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/how-my-terminal-looks/">&lt;h3 id=&quot;background&quot;&gt;Background&lt;&#x2F;h3&gt;
&lt;p&gt;I think everyone of you geeks would like if your terminal looks cool and productive. And yes, I&#x27;m talking here about terminals in POSIX-compliant OS&#x27;s, more specifically terminal in a Linux environment.&lt;&#x2F;p&gt;
&lt;p&gt;Anyhow I will share you the details of my customized terminal configurations so you could get an idea of what it is really capable of doing and perhaps you would start applying the changes to your own terminal.&lt;&#x2F;p&gt;
&lt;p&gt;So to make it all happen I had to give up on &lt;code&gt;bash&lt;&#x2F;code&gt; and start using &lt;code&gt;zsh&lt;&#x2F;code&gt;. Because &lt;code&gt;zsh&lt;&#x2F;code&gt; provides some extensive support to terminal users and it is certainly feature rich, customizable  and cooler than &lt;code&gt;bash&lt;&#x2F;code&gt;. So all the customizations I made are based on &lt;code&gt;zsh&lt;&#x2F;code&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&quot;&gt;oh-my-zsh&lt;&#x2F;a&gt; (a really cool community driven project for managing &lt;code&gt;zsh&lt;&#x2F;code&gt; configurations)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;looks&quot;&gt;Looks&lt;&#x2F;h3&gt;
&lt;p&gt;If looking at theming &lt;code&gt;oh-my-zsh&lt;&#x2F;code&gt; provides some cool and nice looking themes out of the box. But the theme that I&#x27;m using is an external theme and needs little bit more configurations before using it.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s call &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caiogondim&#x2F;bullet-train-oh-my-zsh-theme&quot;&gt;bullet-train&lt;&#x2F;a&gt; and to make it look right I had to install some fonts from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;powerline&#x2F;fonts&quot;&gt;Vim-Powerline&lt;&#x2F;a&gt;. I also had to change the color scheme of my terminal to use &lt;strong&gt;Solarized (dark)&lt;&#x2F;strong&gt;. Here I&#x27;m using xfce4-terminal that comes with Xubuntu, so I can easily change the color scheme from the preferences menu. I also make it a bit transparent to make it even more cooler.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;productivity&quot;&gt;Productivity&lt;&#x2F;h3&gt;
&lt;p&gt;Enough of the looks. Lets see how is it when it comes the productivity. Here again &lt;code&gt;oh-my-zsh&lt;&#x2F;code&gt; provides some nice &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&quot;&gt;plugins&lt;&#x2F;a&gt; that you can use to increase your productivity. I&#x27;ll list down the plugins that I&#x27;m using&lt;&#x2F;p&gt;
&lt;h4 id=&quot;plugins&quot;&gt;Plugins&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;wiki&#x2F;Plugin:git&quot;&gt;git&lt;&#x2F;a&gt; - A very comprehensive plugin for &lt;code&gt;git&lt;&#x2F;code&gt; with lots of aliases for easy usage&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;history-substring-search&quot;&gt;history-substring-search&lt;&#x2F;a&gt; - Very useful plugin that will get you through your &lt;code&gt;zsh&lt;&#x2F;code&gt; history backwards-forwards via up and down arrow keys&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zsh-users&#x2F;zsh-syntax-highlighting&quot;&gt;zsh-syntax-highlighting&lt;&#x2F;a&gt; - this will highlight the commands we use inside the terminal&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;command-not-found&quot;&gt;command-not-found&lt;&#x2F;a&gt; - will suggest alternative&#x2F;correct commands instead of just command not found error&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;colorize&quot;&gt;colorize&lt;&#x2F;a&gt; - this will colorize the syntaxes for almost every popular sciprting&#x2F;programing language&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;colored-man-pages&quot;&gt;colored-man-pages&lt;&#x2F;a&gt; - syntax highlight in man pages for easy reading&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;python&quot;&gt;python&lt;&#x2F;a&gt; - provides lots of useful aliases to Python&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;sudo&quot;&gt;sudo&lt;&#x2F;a&gt; - hit ESC twice and boom! &lt;code&gt;sudo&lt;&#x2F;code&gt; will added to your command&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;web-search&quot;&gt;web-search&lt;&#x2F;a&gt; - simply opens up your browser with the search query of yours in desired search engine i.e &lt;code&gt;google whats the weather like today&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;chucknorris&quot;&gt;chucknorris&lt;&#x2F;a&gt; - this plugin isn&#x27;t particularly meant for productivity but it can get really humorous.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;robbyrussell&#x2F;oh-my-zsh&#x2F;tree&#x2F;master&#x2F;plugins&#x2F;ubuntu&quot;&gt;ubuntu&lt;&#x2F;a&gt; - this particular plugin provides lot of Ubuntu specific aliases to ease up the day-to-day work&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once put these all together this is how it looks&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;how_my_t_looks_01.gif&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;how_my_t_looks_02.gif&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>About</title>
        <published>2025-01-01T00:00:00+00:00</published>
        <updated>2025-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/about/"/>
        <id>https://noos.blog/about/</id>
        
        <content type="html" xml:base="https://noos.blog/about/">&lt;h2 id=&quot;who-i-am&quot;&gt;Who I Am&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m &lt;strong&gt;Oshadha&lt;&#x2F;strong&gt;, a senior engineer passionate about building reliable, performant systems. My journey started in Java—Spring, Spring Boot, microservices, you name it. I cut my teeth on enterprise Java applications and learned the art of distributed systems the hard way.&lt;&#x2F;p&gt;
&lt;p&gt;But recently, I&#x27;ve been fascinated by &lt;strong&gt;Rust&lt;&#x2F;strong&gt;—its speed, memory safety, and the compiler&#x27;s refreshingly rude honesty. I&#x27;m now diving deep into Rust with personal projects, exploring systems programming from a new angle. But I know my roots, and those Java lessons still shape how I think about architecture.&lt;&#x2F;p&gt;
&lt;p&gt;In my day job, I&#x27;m mostly into efforts—overseeing in AI&#x2F;ML, building, and planning AI&#x2F;ML pipelines for targeted clients. It&#x27;s about battling the unknowns behind this new frontier and shaping features that grab users&#x27; attention. The journey from Java to AI to Rust has taught me that the best engineers aren&#x27;t defined by their language, but by their ability to solve problems.&lt;&#x2F;p&gt;
&lt;p&gt;This blog, &lt;strong&gt;Noos&lt;&#x2F;strong&gt;, is where I share my thoughts on technology, engineering practices, and the occasional hard-won lesson from the trenches.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-write-about&quot;&gt;What I Write About&lt;&#x2F;h2&gt;
&lt;p&gt;My content spans:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java &amp;amp; Spring&lt;&#x2F;strong&gt;: Enterprise patterns, microservices architecture, and lessons from building large-scale systems&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Rust&lt;&#x2F;strong&gt;: Exploring systems programming, memory safety, and the Rust ecosystem as I learn&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;AI&#x2F;ML Engineering&lt;&#x2F;strong&gt;: Building robust ML pipelines, tackling the unknowns, and shipping production ML systems&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Linux &amp;amp; Open Source&lt;&#x2F;strong&gt;: Terminal workflows, system configuration, and the tools I use daily&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DevOps&lt;&#x2F;strong&gt;: CI&#x2F;CD, infrastructure, automation, and the realities of deployment&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I write from experience, not theory. You&#x27;ll find real problems I&#x27;ve encountered, actual solutions that worked (and some that didn&#x27;t), and honest reflections on what I wish I&#x27;d known sooner.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what I work with:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Languages&lt;&#x2F;strong&gt;: Java, Python, Rust, JavaScript&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Frameworks&lt;&#x2F;strong&gt;: Spring Boot, microservices architecture, Rust ecosystem&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;ML&#x2F;AI&lt;&#x2F;strong&gt;: Building and deploying ML pipelines, model serving, production ML systems&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure&lt;&#x2F;strong&gt;: Linux, Docker, GitHub Actions, cloud platforms&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;&#x2F;strong&gt;: Terminal-first workflows, Vim, tmux&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Interests&lt;&#x2F;strong&gt;: Systems programming, distributed systems, performance optimization, ML engineering&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;contact&quot;&gt;Contact&lt;&#x2F;h2&gt;
&lt;p&gt;You can find me on:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LinkedIn&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;oshadhagunawardena&#x2F;&quot;&gt;Oshadha Gunawardena&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;GitHub&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nooscraft&quot;&gt;nooscraft&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Blog&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;noos.blog&quot;&gt;noos.blog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;This site is built with &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;, a blazingly fast static site generator written in Rust. The theme is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aaranxu&#x2F;radion&quot;&gt;Radion&lt;&#x2F;a&gt;, customized for a clean, minimal aesthetic.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A simple word definition script</title>
        <published>2015-08-25T00:00:00+00:00</published>
        <updated>2015-08-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/a-simple-word-definition-script/"/>
        <id>https://noos.blog/posts/a-simple-word-definition-script/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/a-simple-word-definition-script/">&lt;p&gt;Have you ever come across a difficulty to find a definition of a word that you are looking for. Well I find it bit difficult for me.&lt;&#x2F;p&gt;
&lt;p&gt;I normally use Google search to find definitions of words. For example to get the definition of the word &quot;replenish&quot; I use &lt;a href=&quot;https:&#x2F;&#x2F;www.google.lk&#x2F;webhp?sourceid=chrome-instant&amp;amp;ion=1&amp;amp;espv=2&amp;amp;ie=UTF-8#q=definition%20replenish&quot;&gt;&lt;em&gt;definition replenish&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; on search bar&lt;&#x2F;p&gt;
&lt;p&gt;But this approach is still bit clumsy though, since you need a browser and if you are already opened multiple tabs then it is sometimes get messy around to switch around tabs.&lt;&#x2F;p&gt;
&lt;p&gt;So I found out this simple script from the book &lt;a href=&quot;https:&#x2F;&#x2F;www.packtpub.com&#x2F;application-development&#x2F;linux-shell-scripting-cookbook-second-edition&quot;&gt;&lt;em&gt;Linux Shell Scripting Cookbook, 2nd Edition&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; that will query your word and get the definition of it.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how to get it to work (I have altered the original script from the book to make is more convenient)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First you need an account in &lt;em&gt;dictionaryapi.com&lt;&#x2F;em&gt; - &lt;a href=&quot;http:&#x2F;&#x2F;www.dictionaryapi.com&#x2F;register&#x2F;index.htm&quot;&gt;http:&#x2F;&#x2F;www.dictionaryapi.com&#x2F;register&#x2F;index.htm&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;API key for the &lt;em&gt;learners&lt;&#x2F;em&gt; API would be enough for the time being&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Get the script from &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;nooscraft&#x2F;1fe12ee85074e361836a&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Assign your API key to the variable &lt;strong&gt;apikey&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Make it executable &lt;code&gt;chmod +x define&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Optional: if you want to make it available system wide add the script to &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Finally you can run it as &lt;code&gt;.&#x2F;define replenish&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The outcome would be:-
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;define_script.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>JDK 9 and JShell</title>
        <published>2015-06-12T00:00:00+00:00</published>
        <updated>2015-06-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/jdk-9-and-jshell/"/>
        <id>https://noos.blog/posts/jdk-9-and-jshell/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/jdk-9-and-jshell/">&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h3&gt;
&lt;p&gt;I recently got to know about this official Java REPL (Read-Eval-Print-Loop) or JShell project. It is named as Kulla and you can visit &lt;a href=&quot;http:&#x2F;&#x2F;openjdk.java.net&#x2F;projects&#x2F;kulla&#x2F;&quot;&gt;here&lt;&#x2F;a&gt; to see the project&#x27;s home. This is pretty much same like the Python&#x27;s IDLE (If you have used it before) and a great way to exercise your code in real time. Also the good thing is that this project will be available as a part of JDK9 among with some other cool features.&lt;&#x2F;p&gt;
&lt;p&gt;Anyhow I managed to get it run on own and have tried few exercises too. Here, take a look&lt;&#x2F;p&gt;
&lt;script type=&quot;text&#x2F;javascript&quot; src=&quot;https:&#x2F;&#x2F;asciinema.org&#x2F;a&#x2F;eddp51uxxwidh8vlpy91ufyj2.js&quot; id=&quot;asciicast-eddp51uxxwidh8vlpy91ufyj2&quot; async&gt;&lt;&#x2F;script&gt;
&lt;h3 id=&quot;how-do-i-get-it-to-run&quot;&gt;How do I get it to run&lt;&#x2F;h3&gt;
&lt;p&gt;I haven&#x27;t tried this on Windows, &lt;strong&gt;only on POSIX based systems (Linux)&lt;&#x2F;strong&gt;.  But I believe the precompiled jar will work on Windows. You can give it a go and see.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;easy-way&quot;&gt;Easy way&lt;&#x2F;h3&gt;
&lt;p&gt;If you want to try out REPL right away there&#x27;s this precompiled Jar that you can use . What you&#x27;ll need is&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Link for the Jar:Kulla.jar&lt;&#x2F;li&gt;
&lt;li&gt;Java 9 early access JDK&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once these are in place you just need to set the JAVA_HOME to your &#x2F; path &#x2F; to &#x2F; JDK 9. Then execute the following -jar command:-&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ java -jar kulla.jar&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You will be entered in to the JShell.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hard-way&quot;&gt;Hard way&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;NOTE: The whole build process can take up to 20-30 minutes or more, so brace yourself.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure you have set the JAVA_HOME&lt;&#x2F;li&gt;
&lt;li&gt;You also need Mercurial. If you are on Ubuntu just give &lt;code&gt;sudo apt-get install mercurial&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Then the follow these commands to get kulla-dev branch built
&lt;ul&gt;
&lt;li&gt;hg clone http:&#x2F;&#x2F;hg.openjdk.java.net&#x2F;kulla&#x2F;dev kulla-dev&lt;&#x2F;li&gt;
&lt;li&gt;cd kulla-dev&lt;&#x2F;li&gt;
&lt;li&gt;sh get_sources.sh&lt;&#x2F;li&gt;
&lt;li&gt;bash configure –with-boot-jdk=&#x2F;path&#x2F;to&#x2F;jdk1.9&lt;&#x2F;li&gt;
&lt;li&gt;make clean images&lt;&#x2F;li&gt;
&lt;li&gt;make install  (optional)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;OK, kulla-dev branch is now built, hopefully without any errors. Now lets see how we can build and run the REPL. I&#x27;m extracting these information from official README under Kulla dev branch.&lt;&#x2F;p&gt;
&lt;p&gt;Download JLINE2 from Maven, and set the environment variable JLINE2LIB to point to the downloaded jar file.&lt;&#x2F;p&gt;
&lt;p&gt;Building REPL:-&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;cd langtools&#x2F;repl &lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;bash .&#x2F;scripts&#x2F;compile.sh&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Running:-&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;bash .&#x2F;scripts&#x2F;run.sh&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If everything goes fine you&#x27;ll be entered to the JShell without any issues.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;&#x2F;h3&gt;
&lt;p&gt;I will add a summary of features that you&#x27;ll find useful when using the REPL.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;REPL has networking support. Yes you can work with java.net&lt;&#x2F;li&gt;
&lt;li&gt;Semicolone is optional giving you a flexibility like most of REPL&#x27;s out there&lt;&#x2F;li&gt;
&lt;li&gt;It has some useful help commands that you can use to improve your productivity. &#x2F;help list those commands&lt;&#x2F;li&gt;
&lt;li&gt;Checked exceptions are not valid here. Like in normal Java environment you will not be forced to handle the checked exceptions. REPL will be handling it in the background&lt;&#x2F;li&gt;
&lt;li&gt;Expressions will also work out of the box here. Arithmetic, String manipulations, method calls .etc&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;java.net&#x2F;downloads&#x2F;adoptopenjdk&#x2F;REPL_Tutorial.pdf&quot;&gt;Here&lt;&#x2F;a&gt; I found a good tutorial that might be useful. It has some basic to intermediate exercises that you can follow go get familiar with the JShell&#x2F;REPL&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Linux | Fancy little &quot;dialog&quot; utility</title>
        <published>2013-12-23T00:00:00+00:00</published>
        <updated>2013-12-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/linux-fancy-little-dialog-utility/"/>
        <id>https://noos.blog/posts/linux-fancy-little-dialog-utility/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/linux-fancy-little-dialog-utility/">&lt;p&gt;Using Linux&#x27;s sophisticated &quot;dialog&quot; utility to display CPU core temperature&lt;&#x2F;p&gt;
&lt;p&gt;Installation&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sudo apt-get install dialog&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sudo apt-get install lm-sensors&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here the lm-sensors for detecting temperature (see here for configuration details: http:&#x2F;&#x2F;lm-sensors.org&#x2F;wiki&#x2F;iwizard&#x2F;Detection)&lt;&#x2F;p&gt;
&lt;p&gt;Finally the script goes as&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;#!&#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;temp1=$(sensors | grep &amp;quot;Core 0:&amp;quot; | cut -c1-24)
&lt;&#x2F;span&gt;&lt;span&gt;temp2=$(sensors | grep &amp;quot;Core 1:&amp;quot; | cut -c1-24)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;dialog –title &amp;quot;System temp info&amp;quot; –msgbox &amp;quot;$temp1 $temp2″ 10 22
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;dialog –clear
&lt;&#x2F;span&gt;&lt;span&gt;exit 0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using minimal advantage of lm-sensors, it&#x27;s just displaying main core temperatures.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;screenshot-11232013-105234-pm1.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>openSUSE 13.1 – almost here [updated]</title>
        <published>2013-10-07T00:00:00+00:00</published>
        <updated>2013-10-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/opensuse-13-1-almost-here/"/>
        <id>https://noos.blog/posts/opensuse-13-1-almost-here/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/opensuse-13-1-almost-here/">&lt;p&gt;It&#x27;s been a while since the &lt;a href=&quot;http:&#x2F;&#x2F;news.opensuse.org&#x2F;2013&#x2F;09&#x2F;19&#x2F;opensuse-13-1-beta-is-out&#x2F;&quot;&gt;openSUSE 13.1 beta&lt;&#x2F;a&gt; is released. And now it&#x27;s just a matter of time until the official release. [RC1 is coming on Thursday]
Anyhow I managed to get it run on own and have tried few exercises too. Here, take a look&lt;&#x2F;p&gt;
&lt;p&gt;So as a SUSE user I&#x27;m putting my blog&#x27;s background picture to a nice art work made by the community. You can see the gecko face in between the posts, well it didn&#x27;t work as I expected but still it look pretty sleek.&lt;&#x2F;p&gt;
&lt;p&gt;You can check out other &lt;a href=&quot;https:&#x2F;&#x2F;news.opensuse.org&#x2F;2013&#x2F;10&#x2F;07&#x2F;help-promote-opensuse-13-1&#x2F;&quot;&gt;promotion&lt;&#x2F;a&gt; fun sparks and suggested &lt;a href=&quot;https:&#x2F;&#x2F;en.opensuse.org&#x2F;openSUSE:Goals_13.1&quot;&gt;goals&lt;&#x2F;a&gt; for openSUSE 13.1&lt;&#x2F;p&gt;
&lt;p&gt;Have fun..!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; It&#x27;s out..! But I didn&#x27;t have much time on checking it out. Hoping to do a run through, let&#x27;s see how it goes this time.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Amazon WS EC2 – connect via SSH RSA</title>
        <published>2013-09-15T00:00:00+00:00</published>
        <updated>2013-09-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/amazon-ws-ec2-connect-via-ssh-rsa/"/>
        <id>https://noos.blog/posts/amazon-ws-ec2-connect-via-ssh-rsa/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/amazon-ws-ec2-connect-via-ssh-rsa/">&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h3&gt;
&lt;p&gt;So you own an up and running Amazon EC2 instance. And you want it to access via hassle free way. This guided post will describe on how you can do it with few steps.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; Just for the convenience I&#x27;m referring some steps from this blog &lt;a href=&quot;http:&#x2F;&#x2F;thekeesh.com&#x2F;2011&#x2F;05&#x2F;setting-up-user-accounts-password-authentication-and-ssh-keys-on-a-new-ec2-instance&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;OK before I start assume you have an up and running EC2 instance. If so grab the public DNS from your AWS management console&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;ec2-management-console-2013-09-15-15-54-28.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;It will be probably something like&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ec2-#############.compute-1.amazonaws.com&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once that is acquired I again assume you have already made your Key-pair and saved it while you are in the process of creating a new instance&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;ec2-management-console-2013-09-15-16-01-07.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;Once that is also in place go to your terminal and CD to the location you saved your Key [your-key-pair.pem] and try to do &lt;strong&gt;SSH&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ ssh -i your-key-pair.pem ubuntu@ec2-#############.compute-1.amazonaws.com&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[the default user will be ubuntu for &lt;a href=&quot;http:&#x2F;&#x2F;memoverkill.com&#x2F;2013&#x2F;09&#x2F;15&#x2F;Amazon%20Machine%20Images%20(AMIs)&quot;&gt;Amazon Machine Images (AMIs)&lt;&#x2F;a&gt;]&lt;&#x2F;p&gt;
&lt;p&gt;Now you should be inside the AMS terminal&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;aws-terminal1.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;Ok now you have to do is add your self up as a new user and give the root privileges (sudoers). Simply follow execute the following commands on to the AWS terminal&lt;&#x2F;p&gt;
&lt;p&gt;Adding yourself as a user:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ adduser yourself&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;granting privileges&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo visudo&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;find the line root  &lt;code&gt;ALL=(ALL:ALL) ALL&lt;&#x2F;code&gt; and the line yourself &lt;code&gt;ALL=(ALL) ALL&lt;&#x2F;code&gt; under it.&lt;&#x2F;p&gt;
&lt;p&gt;Then enable password authentication via (I used the nano editor)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo nano &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;PasswordAuthentication no to PasswordAuthentication yes&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Afterward reload the ssh configuration&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo &#x2F;etc&#x2F;init.d&#x2F;ssh reload&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;OK now logout from the current session and log back as yourself&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ ssh yourself@ec2-#############.compute-1.amazonaws.com&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And to make sure everything is working just fine, execute following&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo -v&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You will be promted to enter the password you have provided while creating the user(yourself). Enter that and if everything went well you will get no output on terminal.&lt;&#x2F;p&gt;
&lt;p&gt;Now lets remove this troublesome password authentication replacing SSH RSA public key authentication&lt;&#x2F;p&gt;
&lt;p&gt;To do that first you need to create a SSH RSA public key&lt;&#x2F;p&gt;
&lt;p&gt;So logout from the AWS terminal and from your local terminal execute the following command (Just press return for all the steps)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;local-host$ ssh-keygen -t rsa&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And you will be promted as below&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; file in which to save the key (&#x2F;home&#x2F;yourself&#x2F;.ssh&#x2F;id_rsa)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Created&lt;&#x2F;span&gt;&lt;span&gt; directory &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;home&#x2F;yourself&#x2F;.ssh&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; passphrase (empty for no passphrase)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; same passphrase again:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; identification has been saved in &#x2F;home&#x2F;yourself&#x2F;.ssh&#x2F;id_rsa.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; public key has been saved in &#x2F;home&#x2F;yourself&#x2F;.ssh&#x2F;id_rsa.pub.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;The&lt;&#x2F;span&gt;&lt;span&gt; key fingerprint is:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;58:3a:80:a5:df:17:b0:af:4f:90:07:c5:3c:01:50:c2&lt;&#x2F;span&gt;&lt;span&gt; yourself@inux-cc6a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span&gt; public key will be stored in &#x2F;home&#x2F;yourself&#x2F;.ssh&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now what you have to do is add that key to AWS in order to identify yourself as authorize user.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;local-host$ scp ~&#x2F;.ssh&#x2F;id_rsa.pub yourself@ec2-#############..compute-1.amazonaws.com:&#x2F;home&#x2F;yourself&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then again login to the AWS&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;local-host$ ssh yourself@ec2-#############..compute-1.amazonaws.com:&#x2F;home&#x2F;yourself&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And place the key file in right place&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;$ mkdir .ssh
&lt;&#x2F;span&gt;&lt;span&gt;$ mv id_rsa.pub .ssh&#x2F;authorized_keys
&lt;&#x2F;span&gt;&lt;span&gt;$ chmod 700 .ssh
&lt;&#x2F;span&gt;&lt;span&gt;$ chmod 600 .ssh&#x2F;authorized_keys
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you should be able to login without using a password&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;local-host$ ssh yourself@ec2-#############.compute-1.amazonaws.com&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finally remove the password authentication and root user access&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo vim &#x2F;etc&#x2F;ssh&#x2F;sshd_config&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Find the line &lt;code&gt;PasswordAuthentication yes&lt;&#x2F;code&gt; and change it to &lt;code&gt;PasswordAuthentication no&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Also &lt;code&gt;PermitRootLogin yes&lt;&#x2F;code&gt; to &lt;code&gt;PermitRootLogin no&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finally reload the SSH configurations again&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo &#x2F;etc&#x2F;init.d&#x2F;ssh reload&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s all you need for crating a new user account and allowing authentication via SSH RSA.&lt;&#x2F;p&gt;
&lt;p&gt;If you are still lazy enough to type that long public DNS you can simply assign it to an alias and place it in the &lt;code&gt;~&#x2F;.bash_proflle&lt;&#x2F;code&gt;  or &lt;code&gt;~&#x2F;.bashrc&lt;&#x2F;code&gt; to make it permanent.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;amazon cloud ssh
&lt;&#x2F;span&gt;&lt;span&gt;alias connect-amazon=&amp;#39;ssh yourself@ec2-#############..compute-1.amazonaws.com&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;$ connect-amazon&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Connecting Dots</title>
        <published>2013-09-15T00:00:00+00:00</published>
        <updated>2013-09-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/connecting-dots/"/>
        <id>https://noos.blog/posts/connecting-dots/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/connecting-dots/">&lt;p&gt;While I was fooling around the Play store I found this recently released nice, simple and very addictive game. It&#x27;s called the Dots. The funny thing about this game is that once you get started playing it you won&#x27;t feel bored even it just has to do the same thing over and over again.&lt;&#x2F;p&gt;
&lt;p&gt;The experience that I felt was somewhat different from other games I have played before (but this could be only me). I think the main psychological factor that keeps me in the game is its simplicity and the options you get to boost your score and the opportunity you get to use those options. So there should be also a bit of a planning involved.&lt;&#x2F;p&gt;
&lt;p&gt;Talking about the game basics there are two game mods &quot;Time mode&quot; and &quot;Moves mode&quot;. In time mode there&#x27;s a timer which runs for 60 seconds for a single round, so all your connecting skills should be used in this time frame. And in moves mode you&#x27;ll get 30 moves to scream at in a round. I usually go with the Time mode because it is fun and challenging.&lt;&#x2F;p&gt;
&lt;p&gt;Once you filled your bucket with enough earned dots you can go ahead and acquire one or more above mentioned options. They call these options as Power Ups.&lt;&#x2F;p&gt;
&lt;p&gt;Here I will list those Power Ups&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Time Stops: With this Power Up you can hold off your timer for 5 seconds and it requires 1000 dots for 5 tryouts. It&#x27;s a pretty neat Power Up by the way. (In moves mode you&#x27;ll get extra five moves)&lt;&#x2F;li&gt;
&lt;li&gt;Shrinkers: This Power Up just shrinks any dot from the board. You just have to double tap on a dot. It requires 500 dots for five tryouts and can be used only once in a round.&lt;&#x2F;li&gt;
&lt;li&gt;Expanders: This Power Up I haven&#x27;t used yet. As described it removes all the same color dots. It requires 5000 dots for five tryouts. This Power Up will definitely come in handy.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That&#x27;s pretty much it about this beautiful game. If you are bored or trying to eat up some free time this game will certainly do the job for you.&lt;&#x2F;p&gt;
&lt;p&gt;So here&#x27;s the Play store &lt;a href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=com.nerdyoctopus.gamedots&quot;&gt;URL&lt;&#x2F;a&gt; give it a try&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I noticed something new in the game&#x27;s About section. Actually it was already there but I wasn&#x27;t so curious to check it out.
There are few sections, most of them has quotes of wisdom by an admired person.  But one of them has this funny little dots of all colors and you can tap on them, which will eventually makes a unique sound for each dot.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;dots_screenshot_2013-09-15-22-32-05.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;dots_screenshot_2013-09-15-22-32-19.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>JAXB + Maven – Xml to Java</title>
        <published>2013-09-02T00:00:00+00:00</published>
        <updated>2013-09-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/jaxb-maven-xml-to-java/"/>
        <id>https://noos.blog/posts/jaxb-maven-xml-to-java/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/jaxb-maven-xml-to-java/">&lt;h3 id=&quot;situation&quot;&gt;Situation&lt;&#x2F;h3&gt;
&lt;p&gt;Assume there&#x27;s a situation where you want to generate Java sources from a xml schema definition (xsd).  And generate it constantly whenever you want it if the xsd&#x27;s got updated.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;solution&quot;&gt;Solution&lt;&#x2F;h3&gt;
&lt;p&gt;There are quiet a few out tools&#x2F;libs out there but I found this particulate set (Maven and JAXB2) works well for me, just because it&#x27;s pretty straight forward. But you may prefer a different approach.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;in-to-the-action&quot;&gt;In to the action&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s create a very basic maven project (assume you have already set up Maven and Java)&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;mvn archetype:generate -DgroupId={com.jaxb.hello} -DartifactId={HelloJAXB2}
&lt;&#x2F;span&gt;&lt;span&gt; -DarchetypeArtifactId=maven-archetype-quickstart  -DinteractiveMode=false
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the project is created open the pom.xml file in edit mode and add following two plugins&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;mojo.codehaus.org&#x2F;jaxb2-maven-plugin&#x2F;&quot;&gt;JAXB-2 Maven Plugin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;plugins&#x2F;maven-source-plugin&#x2F;&quot;&gt;Maven Source Plugin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;[Since this is a sample project you may have to define the maven &lt;code&gt;&amp;lt;plugins &#x2F;&amp;gt;&lt;&#x2F;code&gt; sections]&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;plugins-section.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;Carefully notice the sections&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;generatePackage&amp;gt;&lt;&#x2F;code&gt; defines the place where you want to put the generated sources for the xsd&#x27;s&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;schemaIncludes&amp;gt;&lt;&#x2F;code&gt; defines the place where you place your .xsd files.&lt;&#x2F;p&gt;
&lt;p&gt;Now keep all that in mine lets see the project structure&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;project-structure.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;As you can see I just added a single .xsd to the location helloschema&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Once all these are in place you just have run the Maven target.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;mvn package&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;final-build.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;[Here I have skipped the tests for the convenience]&lt;&#x2F;p&gt;
&lt;p&gt;If everything goes smoothly you&#x27;ll see two .jar files in the target folder&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;maven-build.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
&lt;p&gt;Sources are bundled in the &lt;code&gt;HelloJAXB2-1.1-SNAPSHOT-sources.jar&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and you can find it also in &lt;code&gt;generated-sources&lt;&#x2F;code&gt; folder as well&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;public&#x2F;images&#x2F;generated-sources.png&quot; alt=&quot;img&quot; class=&quot;inline&quot;&#x2F;&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Google Go on OpenSUSE 12.1</title>
        <published>2012-01-29T00:00:00+00:00</published>
        <updated>2012-01-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://noos.blog/posts/google-go-on-opensuse-12-1/"/>
        <id>https://noos.blog/posts/google-go-on-opensuse-12-1/</id>
        
        <content type="html" xml:base="https://noos.blog/posts/google-go-on-opensuse-12-1/">&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h3&gt;
&lt;p&gt;As you may have already know the latest distribution release of &lt;a href=&quot;http:&#x2F;&#x2F;en.opensuse.org&#x2F;Portal:12.1&quot;&gt;OpenSUSE 12.1&lt;&#x2F;a&gt; ships with Google Go language. &lt;a href=&quot;http:&#x2F;&#x2F;golang.org&#x2F;&quot;&gt;Go language&lt;&#x2F;a&gt; was founded by Google as their very own programming language. It works like an interpreter language yet it has to be compiled in order to execute. This small introduction will show you how to set up the environment and run your first Go language program.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;installing-go&quot;&gt;Installing Go&lt;&#x2F;h3&gt;
&lt;p&gt;Actually Go language is a part of the SUSE 12.1 distribution so you will not get it out of the box that is you have to install it first in order to use it. So lets begin installing Go&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sudo zypper install go&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here I have issued the zypper command to install the Go (usual SUSE way of installing packages from repos). It will probably install two packages. Ok after the successful installation lets check if it&#x27;s available&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;whereis go&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;outputs&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;go: &#x2F;usr&#x2F;lib&#x2F;go &#x2F;usr&#x2F;share&#x2F;go&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Voila.! so we got installed Go&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-the-environment&quot;&gt;Setting up the environment&lt;&#x2F;h3&gt;
&lt;p&gt;As in the Go guided &lt;a href=&quot;http:&#x2F;&#x2F;golang.org&#x2F;doc&#x2F;install.html#introduction&quot;&gt;tutorial&lt;&#x2F;a&gt; mentioned we need to set up three environment variables in order to successfully compile and execute a Go program. So lets open up the &lt;code&gt;&#x2F;.bashrc&lt;&#x2F;code&gt; file where you usually place your environment variables (because &lt;code&gt;&#x2F;.bashrc&lt;&#x2F;code&gt; file executes with every new session)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sudo nano ~&#x2F;.bashrc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;place three environment variables on the bottom of the file:-&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;export GOROOT=&#x2F;usr&#x2F;lib&#x2F;go
&lt;&#x2F;span&gt;&lt;span&gt;export GOOS=linux
&lt;&#x2F;span&gt;&lt;span&gt;export GOARCH=386
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;[ctrl + x to save the file]&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;export GOROOT&lt;&#x2F;code&gt; – location of your Go source installation (&#x2F;usr&#x2F;lib&#x2F;go) use whereis command to see that as used before.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;export GOOS&lt;&#x2F;code&gt; – Your OS type which is Linux&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;export GOARCH&lt;&#x2F;code&gt; – Your OS architecture, in my case x86-32 (32-bit) so it goes as 386 [see here for more details]. Issue lscpu to check your sys-architecture.&lt;&#x2F;p&gt;
&lt;p&gt;Ok now everything is setup up. All you have to do now is open a new session, simply logout and re-login. or simply do a &lt;code&gt;source ~&#x2F;.bashrc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;first-program&quot;&gt;First program&lt;&#x2F;h3&gt;
&lt;p&gt;As this is an introduction I will grab the same &quot;Hello&quot; program listed on &lt;a href=&quot;http:&#x2F;&#x2F;golang.org&#x2F;doc&#x2F;install.html#writing&quot;&gt;http:&#x2F;&#x2F;golang.org&#x2F;doc&#x2F;install.html#writing&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;nano hello.go&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello, world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;[ctrl + x to save the file]&lt;&#x2F;p&gt;
&lt;p&gt;As I did mentioned earlier Go is a compile and run language like C or C++. So you&#x27;ll have to to take the necessary steps before you make your Go code to be executed.&lt;&#x2F;p&gt;
&lt;p&gt;Compiling, linking and executing&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;8g hello.go
&lt;&#x2F;span&gt;&lt;span&gt;8l hello.8
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;8.out
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Will gives you&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;hello, world&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: here I have used 8g, 8l because my arc-type is 32-bit (386).
You will notice a different formation on the guide.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now you have successfully completed your first Google Go language program. Pretty straight forward hah? To me it seems like that it follows a familiar executing process as in assembly language. So this is just a little heads up you can find more tutorials and code sample on &lt;a href=&quot;http:&#x2F;&#x2F;golang.org&#x2F;doc&#x2F;&quot;&gt;http:&#x2F;&#x2F;golang.org&#x2F;doc&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
