<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>DevEx on Grant Lucas</title>
    <link>https://grantlucas.com/tags/devex/</link>
    <description>Recent content in DevEx on Grant Lucas</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Wed, 29 Jul 2026 19:00:00 -0400</lastBuildDate>
    <atom:link href="https://grantlucas.com/tags/devex/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>For Agents, --help Has to Be Enough</title>
      <link>https://grantlucas.com/posts/2026/07/help-has-to-be-enough/</link>
      <pubDate>Wed, 29 Jul 2026 19:00:00 -0400</pubDate>
      <guid>https://grantlucas.com/posts/2026/07/help-has-to-be-enough/</guid>
      <description>&lt;p&gt;&lt;em&gt;Designing an AI-native CLI: usable by humans, and especially usable by coding agents&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/grantlucas/Logger-TXT&#34;&gt;Logger-TXT&lt;/a&gt; started life as a shell&#xA;script over a decade ago. It does one small thing: append a timestamped line to&#xA;a text file so I can remember what I did on that Tuesday two weeks ago. I&#xA;rewrote it in Go recently, and the surface reason was capabilities. Date ranges,&#xA;type and project filters, reverse scanning so a decade-deep log file stays fast.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s not really why I did it, though. The rewrite was an excuse to practise&#xA;designing an AI-native command-line tool: one a coding agent can pick up and&#xA;drive correctly, with no help from me, on the first try.&lt;/p&gt;&#xA;&lt;p&gt;Every loop in the process that follows (the constraint, the testing, the&#xA;rewrites of my own help text) was for the agent. Humans get a better tool out of&#xA;it, and I&amp;rsquo;ll get to why that happens, but that&amp;rsquo;s the dividend rather than the&#xA;motivation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-ai-native-means-here&#34;&gt;What &amp;ldquo;AI-Native&amp;rdquo; Means Here&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-ai-native-means-here&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Worth defining, since the phrase gets used for something else entirely.&lt;/p&gt;&#xA;&lt;p&gt;An AI-native CLI isn&amp;rsquo;t a CLI with AI inside it. There&amp;rsquo;s no model in Logger-TXT,&#xA;no natural-language parsing, no &lt;code&gt;logger-txt ask &amp;quot;what did I do Tuesday&amp;quot;&lt;/code&gt;. &lt;strong&gt;It&amp;rsquo;s&#xA;a tool that treats a coding agent as a first-class consumer of its interface,&#xA;alongside the human at the terminal.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;A human explores. They run the command, squint at the output, try a flag, read&#xA;the error, and converge. They carry context between attempts, and they can go&#xA;ask someone or read the source when they&amp;rsquo;re stuck.&lt;/p&gt;&#xA;&lt;p&gt;An agent commits. It reads the interface once, picks the command that best&#xA;matches its intent, and runs it. An error it will read and route around, so a&#xA;command that fails outright is no problem. A plausible-looking result it simply&#xA;accepts, because it has no independent sense that the number should have been 40&#xA;instead of 10.&lt;/p&gt;&#xA;&lt;p&gt;So an agent is functionally a user with no prior context, no accumulated&#xA;intuition about your tool, and no way to ask a clarifying question, who&#xA;nonetheless has to get it right immediately.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-constraint---help-has-to-be-enough&#34;&gt;The Constraint: &lt;code&gt;--help&lt;/code&gt; Has to Be Enough&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-constraint---help-has-to-be-enough&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the rule I gave myself: &lt;strong&gt;an agent handed nothing but this binary should&#xA;reach the right command on the first try.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;No instruction file to load. No skill to install. No MCP server wrapping the&#xA;CLI. No prose in a repo telling the agent which flag to prefer. Everything it&#xA;needs to use the tool correctly ships inside the tool.&lt;/p&gt;&#xA;&lt;h3 id=&#34;why-not-a-claudemd-a-skill-or-an-mcp-server&#34;&gt;Why Not a CLAUDE.md, a Skill, or an MCP Server&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#why-not-a-claudemd-a-skill-or-an-mcp-server&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Those are the normal answers to &amp;ldquo;make this tool legible to an agent,&amp;rdquo; and each&#xA;one puts the knowledge in the wrong place:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; section&lt;/strong&gt; lives in &lt;em&gt;your&lt;/em&gt; repo, not mine. It drifts out of&#xA;date the moment I ship a flag, and it only helps agents that happen to read&#xA;that file, in that repo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;An MCP server&lt;/strong&gt; is a second artifact to build, version, release, and keep in&#xA;sync with the binary. Now there are two things that can disagree about how the&#xA;tool behaves. It does buy something help text can&amp;rsquo;t, in fairness: a typed&#xA;schema constrains arguments structurally, where help text only asks nicely. I&#xA;went the other way regardless, for the reason below.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A skill&lt;/strong&gt; has to be installed before it can help, so the tool doesn&amp;rsquo;t work&#xA;until someone has already done setup on the agent&amp;rsquo;s side.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Help text ships with the binary, versions with the binary, and is already the&#xA;first place both consumers look. It&amp;rsquo;s the only documentation surface that can&amp;rsquo;t&#xA;drift from the thing it documents. That matters most in the case the tool&#xA;actually gets used in: someone runs &lt;code&gt;brew install grantlucas/tap/logger-txt&lt;/code&gt; and&#xA;there is no repo, no README, and no source to fall back on. There&amp;rsquo;s a binary and&#xA;its help output, and nothing else.&lt;/p&gt;&#xA;&lt;p&gt;Though the better reason I picked it is that it&amp;rsquo;s a forcing function. If&#xA;&lt;code&gt;--help&lt;/code&gt; is the only channel I&amp;rsquo;m allowed to use, every ambiguity in the tool has&#xA;to get sorted out in the help text or in the design itself. I can&amp;rsquo;t paper over a&#xA;confusing interface with a paragraph somewhere else.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-design-loop&#34;&gt;The Design Loop&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-design-loop&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The constraint makes the workflow concrete, and it ends up looking like&#xA;usability testing with an agent as the test subject:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Hand an agent the binary and nothing but &lt;code&gt;--help&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Watch for the spot where it picks a plausible-but-wrong command&lt;/li&gt;&#xA;&lt;li&gt;Fix the help text, or the design, until a cold read leads somewhere better&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Step 2 is the whole game, because &lt;strong&gt;plausible-but-wrong is the failure mode&#xA;agents produce and humans mostly don&amp;rsquo;t.&lt;/strong&gt; An agent that picks a command which&#xA;errors out is fine, since it reads the error, adjusts, and gets there. An agent&#xA;that picks a command which succeeds and returns believable-but-wrong results has&#xA;no signal that anything went wrong, and neither do I until I notice the numbers&#xA;look off weeks later.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-trap-i-built-myself&#34;&gt;The Trap I Built Myself&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-trap-i-built-myself&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;The best example came from a change that looked purely additive.&lt;/p&gt;&#xA;&lt;p&gt;I added &lt;code&gt;-t&lt;/code&gt; (type) and &lt;code&gt;-p&lt;/code&gt; (project) filters to both &lt;code&gt;show&lt;/code&gt; and &lt;code&gt;search&lt;/code&gt;. Nice&#xA;and consistent: same flags, both commands. Except that quietly created a trap.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger-txt search WORK   &lt;span style=&#34;color:#6272a4&#34;&gt;# matches &amp;#34;WORK&amp;#34; anywhere in the line&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger-txt show -t WORK  &lt;span style=&#34;color:#6272a4&#34;&gt;# matches only entries tagged WORK&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;These now &lt;em&gt;look&lt;/em&gt; interchangeable, but &lt;code&gt;search&lt;/code&gt; matches the term anywhere in the&#xA;full log line, including the message body, so an entry like&#xA;&lt;code&gt;31/01/26 13:30 -0600 - PERSONAL - Followed up on WORK email&lt;/code&gt; shows up in the&#xA;first command and not the second.&lt;/p&gt;&#xA;&lt;p&gt;Asked to find my work entries, the agent reasonably picked &lt;code&gt;search&lt;/code&gt; and got&#xA;plausible results back. Nothing in the help text would have told it otherwise. A&#xA;human who wrote the tool would never make this mistake, which is exactly why I&#xA;couldn&amp;rsquo;t see it.&lt;/p&gt;&#xA;&lt;p&gt;The fix went into the help string, not into a document telling the agent what to&#xA;do:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-3&#34;&gt;3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-4&#34;&gt;4&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Note: &amp;#34;search TYPENAME&amp;#34; is not equivalent to &amp;#34;show -t TYPENAME&amp;#34;. The search&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;term matches anywhere in the full log line — including message text — so&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;entries that mention the type name in their message body will appear as false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;positives. Use &amp;#34;show -t&amp;#34; to filter strictly by the type tag.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s deliberately blunt, and it names the wrong choice explicitly, because a&#xA;disambiguation that only describes the correct option is useless to a reader who&#xA;already believes they&amp;rsquo;ve found it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-designing-for-the-agent-produced&#34;&gt;What Designing for the Agent Produced&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-designing-for-the-agent-produced&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;A handful of patterns fell out of running that loop. Each one is here because of&#xA;a specific way an agent breaks without it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;help-text-that-teaches-rather-than-lists&#34;&gt;Help Text That Teaches Rather Than Lists&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#help-text-that-teaches-rather-than-lists&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Without this, an agent works out the data format by running commands and&#xA;inferring from whatever comes back, which burns turns and produces a wrong&#xA;inference when the sample isn&amp;rsquo;t representative.&lt;/p&gt;&#xA;&lt;p&gt;So the root &lt;code&gt;--help&lt;/code&gt; embeds the log entry format and the full file-resolution&#xA;order. One read establishes where the data lives and what it looks like:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-1&#34;&gt; 1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-2&#34;&gt; 2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-3&#34;&gt; 3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-4&#34;&gt; 4&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-5&#34;&gt; 5&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-6&#34;&gt; 6&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-7&#34;&gt; 7&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-8&#34;&gt; 8&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-9&#34;&gt; 9&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-10&#34;&gt;10&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-11&#34;&gt;11&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Log entry format:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DD/MM/YY HH:MM -0700 - Message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DD/MM/YY HH:MM -0700 - TYPE - Message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DD/MM/YY HH:MM -0700 - (PROJECT) - Message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DD/MM/YY HH:MM -0700 - TYPE (PROJECT) - Message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;File resolution order:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  1. --file flag value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  2. ./log.txt if it exists in the current directory&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  3. LOGGERTXT_PATH environment variable&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  4. ./log.txt (default)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Subcommand examples show their expected output inline:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-3-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-3-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-3-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-3-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-3-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-3-3&#34;&gt;3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # With both type and project&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  logger-txt add -t dev -p acme Fixed login bug&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # =&amp;gt; 04/03/26 14:30 -0500 - DEV (ACME) - Fixed login bug&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;# =&amp;gt;&lt;/code&gt; line costs nothing and removes a round trip. It also makes the&#xA;uppercasing visible, since &lt;code&gt;-t dev&lt;/code&gt; comes back as &lt;code&gt;DEV&lt;/code&gt; and &lt;code&gt;-p acme&lt;/code&gt; as&#xA;&lt;code&gt;(ACME)&lt;/code&gt;, instead of leaving it as a surprise to be discovered later.&lt;/p&gt;&#xA;&lt;h3 id=&#34;documented-defaults-not-implied-ones&#34;&gt;Documented Defaults, Not Implied Ones&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#documented-defaults-not-implied-ones&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;An undocumented default gets guessed at, and a guess that happens to return rows&#xA;is indistinguishable from a correct answer. So the ones that could bite are&#xA;spelled out:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A date-only &lt;code&gt;--start&lt;/code&gt; begins at &lt;code&gt;00:00&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;A date-only &lt;code&gt;--end&lt;/code&gt; ends at &lt;code&gt;23:59&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;An active date range &lt;strong&gt;overrides&lt;/strong&gt; the default count of 10&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That last one especially. &lt;code&gt;show --start 01/03/26 --end 14/03/26&lt;/code&gt; returning&#xA;everything in the range rather than the last 10 is the behaviour I want, and it&#xA;flatly contradicts the count default documented a few lines above it in the same&#xA;help output. That contradiction is exactly why it has to be stated: left&#xA;implicit, an agent summarising two weeks of entries would confidently report on&#xA;ten of them.&lt;/p&gt;&#xA;&lt;h3 id=&#34;no-quoting-ceremony&#34;&gt;No Quoting Ceremony&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#no-quoting-ceremony&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Quoting is one of the most reliable ways to make a generated command fail:&#xA;nested quotes, shell escaping, an apostrophe in the message. So there&amp;rsquo;s nothing&#xA;to quote.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-4-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-4-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger-txt add Had coffee with the team&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Arguments are joined into the message, which removes the whole class of failure&#xA;and is pretty nice to type besides.&lt;/p&gt;&#xA;&lt;h3 id=&#34;a-non-interactive-path-for-everything&#34;&gt;A Non-Interactive Path for Everything&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#a-non-interactive-path-for-everything&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;A blocking prompt is a hung agent. It sits there waiting on stdin that will&#xA;never come, and the turn dies on a timeout with no useful error. So &lt;code&gt;delete&lt;/code&gt;&#xA;asks for confirmation and &lt;code&gt;delete -y&lt;/code&gt; skips it. This is the oldest advice on the&#xA;list and the easiest to forget when you write the interactive path first.&lt;/p&gt;&#xA;&lt;h3 id=&#34;composable-greppable-output&#34;&gt;Composable, Greppable Output&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#composable-greppable-output&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;One entry per line on stdout, plain text on disk:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-5-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-5-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-5-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-5-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger-txt show | grep MEETING&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger-txt show -c &lt;span style=&#34;color:#bd93f9&#34;&gt;50&lt;/span&gt; | wc -l&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The property I care about here is the escape hatch. When my CLI is the wrong&#xA;tool for the job, the agent can ignore it entirely and read the log file&#xA;directly. That was always the point of the plain-text format, though it pays off&#xA;differently with a consumer that&amp;rsquo;s perfectly happy to &lt;code&gt;grep&lt;/code&gt; a file when that&amp;rsquo;s&#xA;the shorter path.&lt;/p&gt;&#xA;&lt;h3 id=&#34;loud-failure-on-ambiguity&#34;&gt;Loud Failure on Ambiguity&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#loud-failure-on-ambiguity&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Passing &lt;code&gt;--start&lt;/code&gt; without &lt;code&gt;--end&lt;/code&gt; is an error with a clear message, not a&#xA;guessed range. The temptation is to be helpful: assume &amp;ldquo;until now,&amp;rdquo; assume &amp;ldquo;for&#xA;one day.&amp;rdquo; But a guessed range produces results that look correct, which puts us&#xA;straight back into plausible-but-wrong.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Erroring out converts a silent wrong answer into an immediate, self-correcting&#xA;retry.&lt;/strong&gt; For an agent, a good error message beats a permissive default, since&#xA;the error is information and the default is a coin flip.&lt;/p&gt;&#xA;&lt;h2 id=&#34;humans-get-it-for-free&#34;&gt;Humans Get It for Free&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#humans-get-it-for-free&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the part I didn&amp;rsquo;t expect: I never once had to trade one consumer against&#xA;the other. Every change on that list made the tool better for me at the terminal&#xA;too.&lt;/p&gt;&#xA;&lt;p&gt;I think the reason is structural, and it&amp;rsquo;s the most useful thing I&amp;rsquo;ve taken from&#xA;the exercise. &lt;strong&gt;An agent is the worst-case human.&lt;/strong&gt; No context, no accumulated&#xA;intuition about the tool, no memory of the last time it hit this flag, no way to&#xA;ask. Every affordance a human silently supplies from experience, the agent needs&#xA;supplied by the interface.&lt;/p&gt;&#xA;&lt;p&gt;Which means designing for the agent isn&amp;rsquo;t a parallel track to designing for&#xA;humans. It&amp;rsquo;s designing for the strictest possible human, the one on their first&#xA;day, at 2am, with no colleague to ask. Satisfy that reader and the experienced&#xA;one is covered by construction.&lt;/p&gt;&#xA;&lt;p&gt;So there&amp;rsquo;s no &amp;ldquo;agent version&amp;rdquo; of the interface to maintain, and I&amp;rsquo;d push back on&#xA;the idea that this is extra work. It&amp;rsquo;s one interface, held to a standard that&#xA;was always the right one, now with something that reliably tells me when I&amp;rsquo;ve&#xA;fallen short of it. I &lt;em&gt;knew&lt;/em&gt; &lt;code&gt;search WORK&lt;/code&gt; and &lt;code&gt;show -t WORK&lt;/code&gt; differed, since I wrote&#xA;both. My tool&amp;rsquo;s ambiguities were invisible to me precisely because I held all&#xA;the context that resolved them. The agent holds none of it, and it doesn&amp;rsquo;t have&#xA;the good grace to quietly work around my gaps. It takes exactly the wrong turn&#xA;my help text permits, immediately and repeatably, where I can watch it happen.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-this-is-worth-practising-now&#34;&gt;Why This Is Worth Practising Now&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#why-this-is-worth-practising-now&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Logger-TXT is a personal tool. The stakes are that I mis-tally my hours one&#xA;week, which makes it a pretty good place to practise something that gets&#xA;expensive elsewhere.&lt;/p&gt;&#xA;&lt;p&gt;The ratio is shifting, though. More and more of the tools I build, at work and&#xA;otherwise, are going to spend most of their invocations being driven by&#xA;something that can&amp;rsquo;t ask a clarifying question in Slack, can&amp;rsquo;t absorb the tribal&#xA;knowledge in someone&amp;rsquo;s head, and can&amp;rsquo;t infer intent from a hallway&#xA;conversation. The interface is all it gets, and if the interface is ambiguous&#xA;then the tool is wrong in a way that looks right, at whatever scale the agent is&#xA;operating at.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s a design discipline, and it&amp;rsquo;s worth practising deliberately before it&#xA;matters. Which flags are confusable. Which defaults are guessable. Which prompts&#xA;hang. Which errors teach. None of it is hard, it&amp;rsquo;s just invisible until you put&#xA;something in front of the tool that can&amp;rsquo;t paper over your omissions with&#xA;intuition.&lt;/p&gt;&#xA;&lt;p&gt;If you want to poke at the result, it&amp;rsquo;s on&#xA;&lt;a href=&#34;https://github.com/grantlucas/Logger-TXT&#34;&gt;GitHub&lt;/a&gt;, or&#xA;&lt;code&gt;brew install grantlucas/tap/logger-txt&lt;/code&gt; and run &lt;code&gt;--help&lt;/code&gt;. If you try it with an&#xA;agent and it takes a wrong turn I haven&amp;rsquo;t caught yet, let me know. Those are the&#xA;gaps I can&amp;rsquo;t find on my own, since I already know what my own tool means.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>I Built an App That&#39;s Only Useful Three Weeks a Year</title>
      <link>https://grantlucas.com/posts/2026/02/i-built-an-app-thats-only-useful-three-weeks-a-year/</link>
      <pubDate>Wed, 18 Feb 2026 20:00:00 -0500</pubDate>
      <guid>https://grantlucas.com/posts/2026/02/i-built-an-app-thats-only-useful-three-weeks-a-year/</guid>
      <description>&lt;p&gt;&lt;em&gt;How building from my phone exposed every point of friction in my workflow, and how I can make it better for all devices&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Let me be upfront: the app I&amp;rsquo;m about to describe is useful for &lt;em&gt;maybe&lt;/em&gt; three&#xA;weeks out of the year. &lt;a href=&#34;https://sapcast.ca/&#34;&gt;It helps you figure out when to tap a maple tree for&#xA;sap.&lt;/a&gt; That&amp;rsquo;s it. The rest of the year it just sits there,&#xA;waiting for February to roll around again. I can&amp;rsquo;t think of much else targeting&#xA;such a specific, and time-limited, niche&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;A year ago, I wouldn&amp;rsquo;t have built this. Not because it&amp;rsquo;s hard, but because the&#xA;return on investment is laughable. A few weeks of utility for evenings of&#xA;development time, if I finish it at &lt;em&gt;all&lt;/em&gt;, is not worth it. I&amp;rsquo;d just check the&#xA;weather forecast myself, squint at the overnight lows, and make a guess like I&#xA;did last year. The motivation simply wouldn&amp;rsquo;t survive contact with the reality&#xA;of actually sitting down to build it.&lt;/p&gt;&#xA;&lt;p&gt;At the end of the day or on the weekend, at this point in my life, I &lt;em&gt;just don&amp;rsquo;t&#xA;have the time or remaining brain cycles,&lt;/em&gt; to work through software side projects&#xA;when life&amp;rsquo;s already busy or I spent all day in meetings. But the cost of&#xA;producing code has changed. And that changes which ideas I can tackle in the&#xA;limited time I have available.&lt;/p&gt;&#xA;&lt;p&gt;With &lt;em&gt;many&lt;/em&gt; experiments, learnings, and changes to my flows over the past 6-8&#xA;months, I&amp;rsquo;ve produced more code than in the past 6 years since getting into&#xA;management. Finally a lot of the ideas I&amp;rsquo;ve had can see the light&#xA;of day as I can instead focus on the product/feature, secure deployments, smooth&#xA;DevEx, and architecture of the thing I&amp;rsquo;d like to see. And each time through this&#xA;process, whether at my computer, &lt;em&gt;or now at my phone,&lt;/em&gt; &lt;strong&gt;I find points of&#xA;friction I can smooth out so that &lt;em&gt;next time&lt;/em&gt; it&amp;rsquo;s just a bit easier.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-spark&#34;&gt;The Spark&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-spark&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;d been having ongoing conversations with a family member about the impact of&#xA;AI on software engineering: what&amp;rsquo;s possible now, what&amp;rsquo;s overhyped, where it&amp;rsquo;s&#xA;actually headed. The kind of back-and-forth where you&amp;rsquo;re theorizing about the&#xA;future but don&amp;rsquo;t have a great concrete example to point at. I&amp;rsquo;d been thinking&#xA;about what simple, real app I could build to just &lt;em&gt;show&lt;/em&gt; what&amp;rsquo;s possible&#xA;instead of talking about it abstractly.&lt;/p&gt;&#xA;&lt;p&gt;Then, sitting at dinner, talking about tapping maple trees (because it&amp;rsquo;s that&#xA;time of year), the idea clicked. Last year I&amp;rsquo;d manually checked the forecast&#xA;every day, trying to figure out when the freeze-thaw cycle was right for sap&#xA;to flow. Lots of guessing, lots of doubt. Why not build something that does&#xA;that automatically?&lt;/p&gt;&#xA;&lt;p&gt;I pulled out my phone, opened the Claude iOS app, and started describing what&#xA;I wanted using &lt;a href=&#34;https://www.wispr.com/&#34;&gt;Wispr Flow&lt;/a&gt; for voice dictation. I&#xA;gave it the problem I was trying to solve, a high-level overview of my&#xA;understanding of how maple sap tapping works, and told it to do research so it&#xA;could cite its sources. Then I let it work.&lt;/p&gt;&#xA;&lt;p&gt;By the time I got home, maybe 20 minutes later, I ran &lt;code&gt;npm run dev&lt;/code&gt; and had&#xA;a working application. It detected my location, pulled a 7-day temperature&#xA;forecast, and graded each day as poor, fair, good, or excellent based on the&#xA;freeze-thaw cycle. It had information explaining how the cycle works, how the&#xA;ratings are calculated, and a full section of sources and further reading based&#xA;on the research it had done. Not a skeleton. A real app with real content.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;That was pretty eye opening. Was it perfect? Definitely not. Did I trust it&#xA;yet? Nope. Did it &amp;ldquo;work&amp;rdquo; and could I build from this? Oh yeah&amp;hellip; Let&amp;rsquo;s iterate.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-note callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Note&lt;/h5&gt;This wasn&amp;rsquo;t my first attempt at building from my phone. I&amp;rsquo;d hit some friction on&#xA;a prior project that taught me a fair amount about the workflow, like making&#xA;sure your repo has a &lt;code&gt;main&lt;/code&gt; branch before Claude tries to open PRs. I wrote up&#xA;those learnings separately in &lt;a href=&#34;https://grantlucas.com/posts/2026/02/tips-for-starting-new-projects-with-claude-code-on-ios/&#34;&gt;Tips for Starting New Projects With Claude Code&#xA;on iOS&lt;/a&gt;.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;getting-to-production&#34;&gt;Getting to Production&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#getting-to-production&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Once I was home with the app running locally, the next step was getting it&#xA;deployed. I connected a Cloudflare Worker to my GitHub repo so that every&#xA;merge to &lt;code&gt;main&lt;/code&gt; triggers a production deploy, with preview URLs enabled for&#xA;pull requests. There were a couple of build issues to work through, and I had&#xA;Claude move the project from vanilla JavaScript to TypeScript, which is what&#xA;I would have preferred from the start. I just hadn&amp;rsquo;t caught it during that&#xA;initial phone session because everything was happening so quickly.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-note callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Sidenote&lt;/h5&gt;That&amp;rsquo;s one of the trade-offs of the autonomous mode that Claude Code on iOS runs&#xA;in. It&amp;rsquo;s not asking for permission as frequently as it does on desktop because&#xA;it&amp;rsquo;s working in a sandboxed environment. The upside is speed: it moves fast and&#xA;gets a lot done. The downside is that it might make a bunch of decisions and&#xA;execute on them before you have a chance to review and steer it in a different&#xA;direction. Though once you &lt;strong&gt;do&lt;/strong&gt; steer it, it shifts pretty easily, so I&amp;rsquo;m not&#xA;sure it&amp;rsquo;s actually a drawback so much as something to be aware of. 🤔&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;I was watching the output, bouncing between the Claude app and GitHub on my&#xA;phone to see what commits were getting pushed. That back-and-forth between apps&#xA;is the closest thing to a &amp;ldquo;review loop&amp;rdquo; you get on the phone.&lt;/p&gt;&#xA;&lt;p&gt;One thing I made sure to do was have Claude create unit tests for the rating&#xA;logic. The app was &lt;em&gt;functionally&lt;/em&gt; showing me different ratings for different&#xA;days, but I wasn&amp;rsquo;t confident it was correct until I could read through test&#xA;cases. Once I could see &amp;ldquo;given this overnight low and that daytime high, the&#xA;rating should be X,&amp;rdquo; I had a lot more confidence that it would work in&#xA;reality.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Like a tale as old as time, good, clear unit tests, help enable an Agent to&#xA;prove its work and help us humans build confidence in the underlying code.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;the-iteration-loop-a-few-days-later&#34;&gt;The Iteration Loop, a Few Days Later&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-iteration-loop-a-few-days-later&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;A few days passed. I had more ideas. Season timing estimates based on&#xA;latitude, so if you haven&amp;rsquo;t had a good tapping window, you&amp;rsquo;d know when to&#xA;just tap anyway before you miss the season. A map showing your location.&#xA;Better error&#xA;handling if location services weren&amp;rsquo;t working. Analytics and a feedback widget&#xA;through &lt;a href=&#34;https://posthog.com/&#34;&gt;PostHog&lt;/a&gt; so I could get basic analytics and&#xA;collect feedback.&lt;/p&gt;&#xA;&lt;p&gt;By this point, the development flow was established. Here&amp;rsquo;s what the loop&#xA;looked like:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Open Claude on my phone&lt;/li&gt;&#xA;&lt;li&gt;Describe the change I want&lt;/li&gt;&#xA;&lt;li&gt;Claude writes the code, pushes a PR to GitHub&lt;/li&gt;&#xA;&lt;li&gt;Cloudflare builds a preview URL within seconds&lt;/li&gt;&#xA;&lt;li&gt;I review the changes in the preview on my phone&lt;/li&gt;&#xA;&lt;li&gt;If something&amp;rsquo;s off, I go back to Claude, request changes, and it pushes to&#xA;the same PR, and Cloudflare picks up the update automatically&lt;/li&gt;&#xA;&lt;li&gt;Merge the PR, Cloudflare deploys to production&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Rinse and repeat. &lt;strong&gt;The whole thing, from idea to production, could happen in&#xA;minutes.&lt;/strong&gt; And because each change was isolated in its own PR with a preview&#xA;deployment, I could spin up multiple Claude sessions for independent changes and&#xA;review them in parallel: one adding Gitleaks scanning, another adding help docs&#xA;for location service issues, a third integrating PostHog.&lt;/p&gt;&#xA;&lt;p&gt;In this round of iteration, the &lt;em&gt;only&lt;/em&gt; thing I needed my laptop for was&#xA;setting a new secret variable in Cloudflare for the PostHog API key.&#xA;Everything else was done entirely through the feedback loop between Claude on&#xA;iOS, GitHub, and Cloudflare.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-went-well&#34;&gt;What Went Well&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-went-well&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Voice-to-code is surprisingly effective for greenfield work.&lt;/strong&gt; Describing a&#xA;new application out loud, conversationally, maps well to how you&amp;rsquo;d explain a&#xA;feature to a colleague. I didn&amp;rsquo;t think about file structure or naming&#xA;conventions. I described the problem and the desired outcome and let Claude&#xA;sort out the implementation.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The research was genuinely good.&lt;/strong&gt; I told Claude to research maple sap&#xA;tapping and cite its sources. The result was an app backed by real references:&#xA;academic papers, extension programs, established guides. For something I&amp;rsquo;m&#xA;still learning about myself, having that research done &lt;em&gt;and&lt;/em&gt; integrated into&#xA;the app was valuable.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The phone-to-production cycle time was genuinely fast.&lt;/strong&gt; Once Cloudflare was&#xA;wired up, making a change and seeing it live took minutes. That&amp;rsquo;s competitive&#xA;with, or better than, a lot of traditional workflows where you&amp;rsquo;d context&#xA;switch to a terminal, run a build, push, wait for CI, and deploy. &lt;em&gt;This was&#xA;somewhat aided by Claude&amp;rsquo;s sandboxed, more autonomous mode.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;PR-based workflow with preview URLs gave me confidence.&lt;/strong&gt; Every change went&#xA;through a PR. Every PR got a preview deployment behind Cloudflare Access. I&#xA;could see exactly what the change looked like in a real environment before&#xA;merging, and share preview URLs for feedback without exposing half-finished&#xA;work.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Unit tests removed the doubt.&lt;/strong&gt; Asking Claude to write tests for the rating&#xA;logic helped the project go from &amp;ldquo;this &lt;em&gt;looks&lt;/em&gt; right&amp;rdquo; to &amp;ldquo;I&amp;rsquo;m&#xA;&lt;em&gt;pretty sure&lt;/em&gt; this &lt;em&gt;is&lt;/em&gt; right.&amp;rdquo; Being able to read through concrete test cases&#xA;grounded the whole thing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-there-was-friction&#34;&gt;Where There Was Friction&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#where-there-was-friction&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Greenfield setup from a phone has gotchas.&lt;/strong&gt; Repo creation, default&#xA;branches, missing config files. I covered these in detail in the &lt;a href=&#34;https://grantlucas.com/posts/2026/02/tips-for-starting-new-projects-with-claude-code-on-ios/&#34;&gt;companion&#xA;tips&#xA;post&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The autonomous mode is a double-edged sword.&lt;/strong&gt; Claude on iOS moves fast and&#xA;doesn&amp;rsquo;t ask for permission as often. That&amp;rsquo;s great for momentum but means it&#xA;can make a batch of decisions before you&amp;rsquo;ve reviewed the first one. Bouncing&#xA;between the Claude app and GitHub to watch commits is the workaround, but it&amp;rsquo;s&#xA;not the same as the more interactive desktop experience. This is definitely a&#xA;pro &lt;em&gt;and&lt;/em&gt; a con at the same time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Secrets management breaks the flow.&lt;/strong&gt; Every time I needed to add a new API&#xA;key or environment variable in Cloudflare, I had to go to my laptop. This&#xA;happened for &lt;a href=&#34;https://pirateweather.net/&#34;&gt;Pirate Weather&lt;/a&gt; (a Dark Sky compatible&#xA;weather API) and again for PostHog. It&amp;rsquo;s a small interruption, but it breaks the&#xA;autonomy of the phone-based loop.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Infrastructure setup is still a laptop task.&lt;/strong&gt; Configuring the Cloudflare&#xA;Worker, setting up the custom domain, wiring up DNS, configuring Cloudflare&#xA;Access for preview environments. All of that was done on the laptop the first&#xA;night. It&amp;rsquo;s not a Claude Code limitation; it&amp;rsquo;s a &amp;ldquo;managing cloud&#xA;infrastructure from a phone browser&amp;rdquo; limitation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;an-app-that-doesnt-really-need-to-exist-but-can&#34;&gt;An App That Doesn&amp;rsquo;t Really Need to Exist (But Can)&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#an-app-that-doesnt-really-need-to-exist-but-can&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The result is &lt;a href=&#34;https://sapcast.ca/&#34;&gt;Sapcast&lt;/a&gt;. It takes your location, pulls&#xA;real forecast data, and tells you when conditions are right to tap your maple&#xA;trees. It rates each day, highlights the best tapping window, estimates season&#xA;timing based on your latitude, and includes educational content with cited&#xA;sources. All in a beginner-friendly way.&lt;/p&gt;&#xA;&lt;p&gt;Last year I was guessing. This year I have an app backed by research that I&#xA;can check in seconds. I&amp;rsquo;m also sharing it with other people who are into&#xA;tapping to get feedback. I&amp;rsquo;m not 100% confident in my own understanding of all&#xA;this, so getting real-world feedback matters.&lt;/p&gt;&#xA;&lt;p&gt;Does this app &lt;em&gt;need&lt;/em&gt; to exist? Honestly, no. People have been tapping maple&#xA;trees forever without issue. But a year ago, the effort-to-payoff ratio would&#xA;have killed this idea before I finished dinner. Now, the idea survived because&#xA;the cost of going from &amp;ldquo;that would be neat&amp;rdquo; to &amp;ldquo;it&amp;rsquo;s live at a URL&amp;rdquo; has&#xA;dropped to the point where three weeks of utility is &lt;em&gt;enough.&lt;/em&gt; I didn&amp;rsquo;t have&#xA;to carve out a weekend. I didn&amp;rsquo;t have to weigh it against other projects&#xA;competing for my limited free time. I described it out loud at dinner and had&#xA;something real by the time I got home.&lt;/p&gt;&#xA;&lt;p&gt;That shift, from &amp;ldquo;not worth building&amp;rdquo; to &amp;ldquo;why not,&amp;rdquo; is the thing I keep&#xA;coming back to. It&amp;rsquo;s not about this app specifically. It&amp;rsquo;s about all the&#xA;small, niche, only-useful-to-a-few-people ideas that now clear the bar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-phone-is-just-a-litmus-test&#34;&gt;The Phone is Just a Litmus Test&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-phone-is-just-a-litmus-test&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I don&amp;rsquo;t want to do all my development from my phone. That&amp;rsquo;s not the point.&lt;/p&gt;&#xA;&lt;p&gt;Building from a phone is an &lt;em&gt;extreme&lt;/em&gt; scenario, and &lt;strong&gt;extreme scenarios are&#xA;useful because they amplify every point of friction.&lt;/strong&gt; On a desktop, you can&#xA;work around a snag quickly enough that you don&amp;rsquo;t even register it as friction.&#xA;On a phone, there&amp;rsquo;s nowhere to hide. Every rough edge stops you cold. Each of&#xA;the friction points I listed above is a small inconvenience on a desktop but a&#xA;real blocker on a phone.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s what makes it a useful experiment. If I can smooth out friction in an&#xA;environment where &lt;em&gt;any&lt;/em&gt; friction is hostile to the development flow, removing&#xA;that same friction in a more amenable environment becomes trivial.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Template repos with sensible defaults&lt;/li&gt;&#xA;&lt;li&gt;Automated preview deployments&lt;/li&gt;&#xA;&lt;li&gt;PR-based workflows with visual review&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;All of that makes the desktop experience better too. The learnings transfer&#xA;directly.&lt;/p&gt;&#xA;&lt;p&gt;For me, this is a learning process I can apply back to my career. I&amp;rsquo;ve been&#xA;thinking a lot about developer experience and reducing the distance between&#xA;having an idea and seeing it running. The phone is just a litmus test. If&#xA;the workflow holds up there, it&amp;rsquo;ll hold up anywhere.&lt;/p&gt;&#xA;&lt;p&gt;The side benefit is that I can finally build some of the side projects I&amp;rsquo;ve&#xA;been sitting on. The cost of going from idea to working software has dropped&#xA;enough that even something useful for three weeks a year is worth doing.&#xA;And the next time someone asks me what&amp;rsquo;s &lt;em&gt;actually&lt;/em&gt; possible with AI and&#xA;software engineering, I won&amp;rsquo;t have to theorize. I&amp;rsquo;ll just show them.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Tips for Starting New Projects With Claude Code on iOS</title>
      <link>https://grantlucas.com/posts/2026/02/tips-for-starting-new-projects-with-claude-code-on-ios/</link>
      <pubDate>Wed, 18 Feb 2026 19:00:00 -0500</pubDate>
      <guid>https://grantlucas.com/posts/2026/02/tips-for-starting-new-projects-with-claude-code-on-ios/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been experimenting with using Claude Code on iOS to build projects from&#xA;my phone. It&amp;rsquo;s been a great learning experience, and I wrote about the&#xA;broader experiment in &lt;a href=&#34;https://grantlucas.com/posts/2026/02/i-built-an-app-thats-only-useful-three-weeks-a-year/&#34;&gt;I Built an App That&amp;rsquo;s Only Useful Three Weeks a&#xA;Year&lt;/a&gt;. But&#xA;before that project went smoothly, I hit some friction that&amp;rsquo;s worth sharing&#xA;on its own.&lt;/p&gt;&#xA;&lt;p&gt;Most of these tips are specific to starting &lt;em&gt;greenfield&lt;/em&gt; projects from your&#xA;phone. If you&amp;rsquo;re making changes to an existing repo that already has the&#xA;right files in place, the experience is &lt;strong&gt;significantly smoother&lt;/strong&gt; from the&#xA;start.&lt;/p&gt;&#xA;&lt;h2 id=&#34;create-your-repo-with-a-default-readme&#34;&gt;Create Your Repo With a Default README&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#create-your-repo-with-a-default-readme&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;When you start a brand new project from your phone, you need a GitHub repo.&#xA;The GitHub iOS app doesn&amp;rsquo;t let you &lt;em&gt;create&lt;/em&gt; repos, so you&amp;rsquo;ll have to&#xA;authenticate into GitHub in the mobile browser to do it.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the important part: &lt;strong&gt;have GitHub create a default README when you set&#xA;up the repo.&lt;/strong&gt; Not for the README content, but just to get a &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;&#xA;&lt;p&gt;Claude Code on iOS works in cloud-prefixed branches (starting with&#xA;&lt;code&gt;claude/&lt;/code&gt;). For reasonable security, it &lt;em&gt;can&amp;rsquo;t&lt;/em&gt; create or edit the &lt;code&gt;main&lt;/code&gt;&#xA;branch or any other non-&lt;code&gt;claude/&lt;/code&gt; branch. If there&amp;rsquo;s no &lt;code&gt;main&lt;/code&gt; branch when&#xA;Claude pushes its working branch, that branch &lt;em&gt;becomes&lt;/em&gt; the default branch&#xA;of your repo. There&amp;rsquo;s no easy way to create a &lt;code&gt;main&lt;/code&gt; branch from your phone&#xA;after the fact. Not from the GitHub iOS app, not from the mobile web. It&amp;rsquo;s a&#xA;mess to sort out, and Claude can&amp;rsquo;t help you fix it because of those same&#xA;branch restrictions.&lt;/p&gt;&#xA;&lt;h2 id=&#34;set-up-a-claudemd-early&#34;&gt;Set Up a CLAUDE.md Early&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#set-up-a-claudemd-early&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;A brand new repo won&amp;rsquo;t have a &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; file. On desktop,&#xA;Claude Code has access to your global configuration file with your&#xA;preferences for things like commit message conventions. On iOS, it doesn&amp;rsquo;t.&#xA;So a new project has no context for how you like things done: commit style,&#xA;language preferences, TDD, commit frequency, any of it.&lt;/p&gt;&#xA;&lt;p&gt;On my first project from a phone, Claude defaulted to vanilla JavaScript when I&#xA;would have preferred TypeScript. I just didn&amp;rsquo;t catch it because everything was&#xA;moving so quickly in the autonomous mode. That&amp;rsquo;s the kind of thing a &lt;code&gt;CLAUDE.md&lt;/code&gt;&#xA;would have prevented.&lt;/p&gt;&#xA;&lt;h2 id=&#34;consider-a-template-repo&#34;&gt;Consider a Template Repo&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#consider-a-template-repo&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;m pretty confident now that the right move is to set up a &lt;strong&gt;template repo&lt;/strong&gt;&#xA;with sensible defaults for phone-first development:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; with your preferences (language, commit conventions, testing&#xA;approach)&lt;/li&gt;&#xA;&lt;li&gt;Basic &lt;a href=&#34;https://gitleaks.io/&#34;&gt;Gitleaks&lt;/a&gt; workflows for secret scanning&lt;/li&gt;&#xA;&lt;li&gt;Whatever project scaffolding makes sense for the kind of things you build&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I haven&amp;rsquo;t built this yet, but it&amp;rsquo;s next on my list. The goal is to remove as&#xA;much of the initial setup friction as possible so that starting a new project&#xA;from my phone is as smooth as working on an existing one.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Override Git&#39;s User Email by Folder</title>
      <link>https://grantlucas.com/posts/2024/03/override-gits-user-email-by-folder/</link>
      <pubDate>Sun, 03 Mar 2024 15:19:19 -0500</pubDate>
      <guid>https://grantlucas.com/posts/2024/03/override-gits-user-email-by-folder/</guid>
      <description>&lt;p&gt;&lt;em&gt;Never forget to set your work email after cloning a work Git repository again!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;When using a single computer for multiple categories of development (personal,&#xA;work, etc), there is often a need to set a specific &lt;code&gt;user.email&lt;/code&gt; value for each&#xA;repository cloned depending on the use. For example, it&amp;rsquo;d make sense to use your&#xA;work email for repositories cloned for work while you&amp;rsquo;d use your personal email&#xA;for you own projects.&lt;/p&gt;&#xA;&lt;p&gt;Without the following setup, it&amp;rsquo;s &lt;strong&gt;very easy&lt;/strong&gt; to forget to override your&#xA;&lt;code&gt;user.email&lt;/code&gt; at a repository level and end up committing to one or the other&#xA;with the wrong email attached. Though not the end of the world, I can imagine&#xA;your company, coworkers, and security department would prefer to see your work&#xA;email attached to all commits instead of a random Gmail account.&lt;/p&gt;&#xA;&lt;p&gt;With the following in place, &lt;strong&gt;you&amp;rsquo;ll never forget to have the correct&#xA;&lt;code&gt;user.email&lt;/code&gt; set again!&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;splitbox&#34;&gt;&lt;div class=&#34;left&#34;&gt;&#xA;&lt;div class=&#34;alert alert-note callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Note&lt;/h5&gt;The following only works if you &lt;strong&gt;split up your repository clones within&#xA;dedicated folders&lt;/strong&gt;&amp;hellip;which is probably better than mixing professional and&#xA;personal repository together anyways.&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;div class=&#34;right&#34;&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── personal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── repo1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── repo2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── work&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── repo1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── repo2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;div style=&#34;clear:both&#34;&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;In your main &lt;code&gt;~/.gitconfig&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-1&#34;&gt; 1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-2&#34;&gt; 2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-3&#34;&gt; 3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-4&#34;&gt; 4&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-5&#34;&gt; 5&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-6&#34;&gt; 6&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-7&#34;&gt; 7&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-8&#34;&gt; 8&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-9&#34;&gt; 9&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-10&#34;&gt;10&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;; Set your desired default name and email&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[user]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name = Bob Loblaw&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = bob@home.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;; Override your user info, and anything else, depending on the folder you&amp;#39;re&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;; currently in&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;; It&amp;#39;s important this comes after anything you&amp;#39;re overriding&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[includeIf &amp;#34;gitdir:~/Development/work/**&amp;#34;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path = ~/.gitconfig_work.inc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Now in &lt;code&gt;~/.gitconfig_work.inc&lt;/code&gt; you can set your work specific information&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-3&#34;&gt;3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[user]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name = Bob Loblaw&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = bob@work.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;With those configs, now &lt;em&gt;any&lt;/em&gt; Git interaction within &lt;code&gt;~/Development/work/**&lt;/code&gt;&#xA;will be done with &lt;code&gt;user.email&lt;/code&gt; set to &lt;code&gt;bob@work.com&lt;/code&gt;. No need for any local Git&#xA;config overrides ever again to set common settings for work vs personal&#xA;repositories.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>A Git Alias for Keeping a Tidy Development Environment</title>
      <link>https://grantlucas.com/posts/2024/02/a-git-alias-for-keeping-a-tidy-development-environment/</link>
      <pubDate>Sun, 25 Feb 2024 22:07:36 -0500</pubDate>
      <guid>https://grantlucas.com/posts/2024/02/a-git-alias-for-keeping-a-tidy-development-environment/</guid>
      <description>&lt;p&gt;Over time, whether working alone or in a team, your Git environment can become&#xA;cluttered and messy with branches of the past, both local and remote. Though&#xA;this isn&amp;rsquo;t an issue in the short term, if left alone, it can lead to time&#xA;wasting mistakes, out of date code that doesn&amp;rsquo;t work once pushed to origin, and&#xA;clutter in any Git tools or GUIs you may use leading to lower productivity.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;How many local and remote branches do &lt;strong&gt;you&lt;/strong&gt; see in one of your projects when&#xA;you run &lt;code&gt;git branch -a&lt;/code&gt;?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The following alias in your &lt;code&gt;~/.gitconfig&lt;/code&gt; will not only become second nature to&#xA;use when updating your base branch, but it&amp;rsquo;ll keep your environment tidy at the&#xA;same time.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-3&#34;&gt;3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-4&#34;&gt;4&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-0-5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-0-5&#34;&gt;5&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[alias]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    update-main = !git checkout main \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git pull \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git remote prune origin \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git branch --merged | grep -v &amp;#34;\\\\*&amp;#34; | xargs -n 1 git branch -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Now any time you want to begin work on a new branch, first run &lt;code&gt;git update-main&lt;/code&gt;&#xA;to not only update your &lt;code&gt;main&lt;/code&gt; branch but also tidy your development&#xA;environment. Then checkout your new branch and get to work.&lt;/p&gt;&#xA;&lt;p&gt;It doesn&amp;rsquo;t really add any time to the process, has a lot of benefits by negating&#xA;the problems below, and eventually becomes second nature to use any time you&#xA;move over to a base branch.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-note callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Note&lt;/h5&gt;&lt;p&gt;If your &lt;strong&gt;base branch&lt;/strong&gt; is different, such as &lt;code&gt;master&lt;/code&gt; or &lt;code&gt;develop&lt;/code&gt;, add another&#xA;instance of this alias with the proper replacements. Personally, I have 3&#xA;instances covering &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;master&lt;/code&gt;, and &lt;code&gt;staging&lt;/code&gt; which are often used in teams&#xA;I&amp;rsquo;ve been on.&lt;/p&gt;&#xA;&lt;p&gt;Once setup, they rarely change or need to be thought of again. I&amp;rsquo;ve been using&#xA;these since around 2013 and I&amp;rsquo;ve rarely given them a second thought other than&#xA;to share them with others.&lt;/p&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;common-problems-with-a-messy-environment&#34;&gt;Common Problems with a Messy Environment&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#common-problems-with-a-messy-environment&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;out-of-date-base-branch&#34;&gt;Out of Date Base Branch&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#out-of-date-base-branch&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;How often have you completed work on a new feature and pushed your code to Github&#xA;only to find out your PR is &lt;em&gt;already out of date?&lt;/em&gt; How?! It&amp;rsquo;s a brand new PR!&lt;/p&gt;&#xA;&lt;p&gt;Well, it turns out locally &lt;em&gt;you forgot to update your base branch (&lt;code&gt;main&lt;/code&gt;)&#xA;before checking out a new branch to start work on your new feature.&lt;/em&gt; There&amp;rsquo;s a&#xA;high chance of this happening if you&amp;rsquo;re working within a team and commits are&#xA;landing in the base branch frequently.&lt;/p&gt;&#xA;&lt;p&gt;Though this is relatively inconsequential &lt;em&gt;most of the time,&lt;/em&gt; the worst case&#xA;would be that your code doesn&amp;rsquo;t work with the latest of the base branch at all!&#xA;Though this would hopefully be &lt;em&gt;very rare,&lt;/em&gt; it&amp;rsquo;s also easily avoided by keeping&#xA;your environment tidy.&lt;/p&gt;&#xA;&lt;h3 id=&#34;git-clutter--cognitive-load--lower-productivity&#34;&gt;Git Clutter == Cognitive Load &amp;amp; Lower Productivity&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#git-clutter--cognitive-load--lower-productivity&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Only slightly hyperbolic, a messy Git environment &lt;em&gt;does&lt;/em&gt; decrease your overall&#xA;productivity by making the tools you use to interact with Git slower to use.&#xA;Whether it&amp;rsquo;s tab completion or GUIs visualizing branches, keeping a lot of stale&#xA;or merged branches around adds no value and only presents downsides.&lt;/p&gt;&#xA;&lt;h2 id=&#34;alias-breakdown&#34;&gt;Alias Breakdown&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#alias-breakdown&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s go through some key points of some of the lines below for clarity and&#xA;understanding.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-2&#34;&gt;2&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-3&#34;&gt;3&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-4&#34;&gt;4&lt;/a&gt;&#xA;&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-1-5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-1-5&#34;&gt;5&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[alias]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    update-main = !git checkout main \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git pull \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git remote prune origin \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;amp;&amp;amp; git branch --merged | grep -v &amp;#34;\\\\*&amp;#34; | xargs -n 1 git branch -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;h3 id=&#34;running-shell-commands-with-&#34;&gt;Running Shell Commands with &lt;code&gt;!&lt;/code&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#running-shell-commands-with-&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;By default every alias is expected to be a &lt;em&gt;Git command.&lt;/em&gt; If you want to call&#xA;&lt;code&gt;git&lt;/code&gt; itself, you need to prefix the call with &lt;code&gt;!&lt;/code&gt; to indicate it should be&#xA;calling a &lt;em&gt;shell command&lt;/em&gt; instead of a Git command. So &lt;code&gt;!git checkout main&lt;/code&gt; at&#xA;the start of the alias will ensure it and the rest of the commands run as &lt;em&gt;shell&#xA;commands.&lt;/em&gt; No more &lt;code&gt;git: &#39;git&#39; is not a git command.&lt;/code&gt; errors.&lt;/p&gt;&#xA;&lt;h3 id=&#34;pruning-remote-branches&#34;&gt;Pruning Remote Branches&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#pruning-remote-branches&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Over time branches will often be deleted from the remote origin during the&#xA;natural development process. Your development environment however will not be kept up&#xA;to date without either the right default configurations or manual calls as I&amp;rsquo;m&#xA;using here. &lt;code&gt;git remote prune origin&lt;/code&gt; will just ensure that any deleted remote&#xA;branches are cleared from your development environment as well.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-note callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Note&lt;/h5&gt;Everything in this process works &lt;em&gt;best&lt;/em&gt; if you &lt;strong&gt;delete remote branches after&#xA;they&amp;rsquo;re merged.&lt;/strong&gt; Though not fully required, there is &lt;em&gt;no benefit&lt;/em&gt; to keeping&#xA;merged remote branches around.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h3 id=&#34;pruning-merged-local-branches&#34;&gt;Pruning &lt;em&gt;Merged&lt;/em&gt; Local Branches&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#pruning-merged-local-branches&#34; title=&#34;Click to copy link&#34;&gt;&lt;i class=&#34;fas fa-link&#34;&gt;&lt;/i&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&#xA;&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34; id=&#34;hl-2-1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#hl-2-1&#34;&gt;1&lt;/a&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git branch --merged | grep -v &amp;#34;\\\\*&amp;#34; | xargs -n 1 git branch -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;This is an interesting line in that it&amp;rsquo;s listing out all local branches that&#xA;have &lt;em&gt;no changes from the current branch&lt;/em&gt; and then deletes them. &lt;code&gt;grep -v &amp;quot;\\\\*&amp;quot;&lt;/code&gt; is filtering &lt;strong&gt;out&lt;/strong&gt; the &lt;em&gt;current branch&lt;/em&gt; from the output of &lt;code&gt;git branch --merged&lt;/code&gt; so that any other lines can be fed to the delete command.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-warning callout&#34; role=&#34;alert&#34;&gt;&lt;h5 class=&#34;alert-heading&#34;&gt;Run this from your base branch&lt;/h5&gt;&lt;strong&gt;This works best when run from the &lt;em&gt;base branch&lt;/em&gt; you&amp;rsquo;re updating (&lt;code&gt;main&lt;/code&gt;)&lt;/strong&gt; so&#xA;using it within this alias is recommended. There&amp;rsquo;s no harm if it removes the&#xA;local instance of &lt;code&gt;main&lt;/code&gt; however, as it will just be re-checked out from remote&#xA;the next time you need it.&#xA;&lt;/div&gt;&#xA;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;I hope this alias, and some of the backing information, is useful to your every&#xA;day development. &lt;em&gt;There&amp;rsquo;s always something new and shiny in the world of&#xA;software&lt;/em&gt; but I always find it interesting to reflect on those little things set&#xA;up over a decade ago that are still used dozens of times per day and now&#xA;couldn&amp;rsquo;t be lived without.&lt;/p&gt;&#xA;&lt;p&gt;Progress &amp;amp; productivity doesn&amp;rsquo;t always have to be a search for a new tool or&#xA;dependency, &lt;strong&gt;sometimes it&amp;rsquo;s just stitching together various commands into an&#xA;alias that fits your flow, reduces mistakes, and makes your environment more&#xA;enjoyable to work in.&lt;/strong&gt;&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
