<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Side-Projects on NateBent.com</title>
    <link>https://natebent.com/tags/side-projects/</link>
    <description>Recent content in Side-Projects on NateBent.com</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor> (Nathan Bent)</managingEditor>
    <atom:link href="https://natebent.com/tags/side-projects/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Zero to App with AI, Part 6: The App Taught Us What to Build</title>
      <link>https://natebent.com/posts/zero-to-app-with-ai-part-6-the-app-taught-us-what-to-build/</link>
      <pubDate>Sun, 19 Jul 2026 01:00:00 +0000</pubDate><author>Nathan Bent</author>
      <guid>https://natebent.com/posts/zero-to-app-with-ai-part-6-the-app-taught-us-what-to-build/</guid>
      <description>&lt;p&gt;Everything up to here was built from imagination: what I thought logging a workout should feel like. Then I trained with it for real, phone in hand, chalk on fingers, rest timer running, and the roadmap got humbled. This part is about the highest-signal planning input the project ever had: actual use.&lt;/p&gt;
&lt;h2 id=&#34;the-friction-list&#34;&gt;The friction list&lt;/h2&gt;
&lt;p&gt;After the first real gym sessions I dumped every annoyance into a list. Nine items, all of them things no amount of desk-testing had surfaced, because they only exist under gym conditions: tired, hurried, one-thumbed. The highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set grouping. A wall of individual sets is unreadable between exercises; sets want to cluster by exercise visually.&lt;/li&gt;
&lt;li&gt;Post-hoc editing. Fat-fingered 135 as 125? There was no way to fix a set after saving. At a desk you&amp;rsquo;d never notice. At the gym it&amp;rsquo;s constant.&lt;/li&gt;
&lt;li&gt;Smart defaults. The app made me re-enter the weight I just used. The previous set is almost always the best guess for the next.&lt;/li&gt;
&lt;li&gt;Sort order, collapsible sections, calmer check-ins. The start-of-workout screen had grown enthusiastic; under time pressure you want it quiet and fast.&lt;/li&gt;
&lt;li&gt;A Manage Plans page and plan archiving. Plans accumulate, and the picker shouldn&amp;rsquo;t scroll forever past experiments you&amp;rsquo;re done with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these is clever. That&amp;rsquo;s the point. They&amp;rsquo;re obvious in use and invisible in theory.&lt;/p&gt;
&lt;blockquote class=&#34;callout callout-tip&#34;&gt;
    &lt;p class=&#34;callout-title&#34;&gt;
      &lt;span class=&#34;callout-icon&#34;&gt;&lt;svg viewBox=&#34;0 0 16 16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;
      &lt;span class=&#34;callout-label&#34;&gt;Tip&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;Write the friction list in the moment or right after. These observations evaporate within hours, and once they&amp;rsquo;re gone you&amp;rsquo;re back to designing from imagination.&lt;/p&gt;
  &lt;/blockquote&gt;&lt;h2 id=&#34;steal-like-a-product-manager&#34;&gt;Steal like a product manager&lt;/h2&gt;
&lt;p&gt;Before turning the list into work, one session did something teams skip: surveyed the incumbents. Strong, Hevy, FitNotes, Jefit. What do apps with millions of gym-hours of accumulated feedback all agree on? The survey confirmed the friction list from the outside: auto-fill defaults from the previous set are table stakes everywhere, rest timers matter, set editing is universal.&lt;/p&gt;
&lt;p&gt;When your dogfooding complaints and the market&amp;rsquo;s converged solutions agree, you can build without second-guessing. Where they diverge is interesting too: you either have a differentiator or a blind spot, and it&amp;rsquo;s worth a minute deciding which.&lt;/p&gt;
&lt;h2 id=&#34;an-ideation-phase-with-no-code&#34;&gt;An ideation phase with no code&lt;/h2&gt;
&lt;p&gt;The friction list and the survey merged into a formal roadmap phase whose entire output was a document: a rated backlog, one line per idea, rated easy, medium, or hard, with just enough context to build from later, committed to the repo as &lt;code&gt;docs/FEATURE-IDEAS.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then the roadmap grew three implementation phases sorted by that rating: quick wins (the easy column, knocked out in one session), medium features, and the one genuinely hard interaction, supersets, which got a phase to itself rather than being wedged into a grab-bag.&lt;/p&gt;
&lt;p&gt;This sounds like bureaucracy for a hobby app. It&amp;rsquo;s the opposite. It&amp;rsquo;s what let an AI-paced project stay aimed. When building costs hours instead of weeks, the scarce resource is knowing what&amp;rsquo;s worth building. A rated backlog in the repo means every future session picks from a priority queue instead of improvising, and, per the docs-as-memory principle from &lt;a href=&#34;https://natebent.com/posts/zero-to-app-with-ai-part-1-write-the-docs-before-the-code/&#34;&gt;Part 1&lt;/a&gt;, it lives where the agent can actually see it: not in my head, not in chat history, not in a notes app.&lt;/p&gt;
&lt;h2 id=&#34;the-check-in-lesson-features-must-earn-their-place-in-the-flow&#34;&gt;The check-in lesson: features must earn their place in the flow&lt;/h2&gt;
&lt;p&gt;An earlier phase had added daily check-ins (sleep, mood) as cards when you start a workout, designed skippable from day one, on the principle that nothing stands between you and logging a set. Real use upgraded that principle from design guideline to law: even optional cards read as noise at 6 AM, which is why &amp;ldquo;calmer check-ins&amp;rdquo; made the friction list. The dopamine feature (&amp;ldquo;New PR!&amp;rdquo; celebrations) survived contact with reality. The earnest wellness features had to learn to whisper.&lt;/p&gt;
&lt;blockquote class=&#34;callout callout-insight&#34;&gt;
    &lt;p class=&#34;callout-title&#34;&gt;
      &lt;span class=&#34;callout-icon&#34;&gt;&lt;svg viewBox=&#34;0 0 16 16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;M8 0 L9.6 6.4 L16 8 L9.6 9.6 L8 16 L6.4 9.6 L0 8 L6.4 6.4 Z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;
      &lt;span class=&#34;callout-label&#34;&gt;Interesting!&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;The flow you optimize is the one you&amp;rsquo;re in when you&amp;rsquo;re weakest: tired, rushed, low-patience. Design reviews happen at desks by people at their sharpest. Your users, even when that&amp;rsquo;s just you, are neither.&lt;/p&gt;
  &lt;/blockquote&gt;&lt;h2 id=&#34;what-id-tell-you&#34;&gt;What I&amp;rsquo;d tell you&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Ship to yourself early, then actually use it. The milestone &amp;ldquo;log a real workout from my phone&amp;rdquo; was in the roadmap from day zero, and everything after it was better-aimed than everything before it.&lt;/li&gt;
&lt;li&gt;Write the friction list immediately, in the moment or right after.&lt;/li&gt;
&lt;li&gt;Survey the incumbents once, deliberately. Where your complaints and their solutions agree, build with confidence. Where they diverge, you might have a differentiator, or a blind spot.&lt;/li&gt;
&lt;li&gt;Make ideation a phase with a committed artifact. A rated backlog in the repo beats ideas in your head, in your chat history, or in a notes app the agent can&amp;rsquo;t see.&lt;/li&gt;
&lt;li&gt;Rate by effort and let the easy column fund momentum. A session of quick wins after a heavy phase keeps the project fun, and fun is the actual fuel of side projects.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Part 7: the app grows up. Real multi-user auth, the migration that touched every table, and how &amp;ldquo;workout-tracker&amp;rdquo; became RepRepo.&lt;/p&gt;
&lt;h2 id=&#34;references-and-further-reading&#34;&gt;References and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://code.claude.com/docs/en/sub-agents&#34;&gt;Create custom subagents&lt;/a&gt; and &lt;a href=&#34;https://code.claude.com/docs/en/memory&#34;&gt;How Claude remembers your project&lt;/a&gt;, Claude Code docs, if you&amp;rsquo;re wiring the ideation-phase pattern into your own repo: the backlog doc only works because the agent reads the repo.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Zero to App with AI, Part 1: Write the Docs Before the Code</title>
      <link>https://natebent.com/posts/zero-to-app-with-ai-part-1-write-the-docs-before-the-code/</link>
      <pubDate>Thu, 09 Jul 2026 01:00:00 +0000</pubDate><author>Nathan Bent</author>
      <guid>https://natebent.com/posts/zero-to-app-with-ai-part-1-write-the-docs-before-the-code/</guid>
      <description>&lt;p&gt;This is a different kind of post for this blog. Less BGP, more JavaScript.&lt;/p&gt;
&lt;p&gt;I built and shipped a real app, &lt;a href=&#34;https://reprepo.app&#34;&gt;RepRepo&lt;/a&gt;, a workout tracker, essentially over one weekend, working with an AI coding agent (Claude Code, in my case, though most of this translates to any of them). Not a todo list demo either. It has a plans system, PR detection with unit tests, multi-user auth, security hardening, nightly backups, and tagged releases deployed by CI, and it is running on the public internet where I actually use it at the gym.&lt;/p&gt;
&lt;p&gt;I should be clear about my starting point, because it matters for how you read this series. I am a network engineer. I can script, I can read code, and I run a homelab full of self-hosted services, but I had never built a web application. The agent wrote nearly all of the code. My job was direction, review, testing, and the sysadmin work at the end. Whether that counts as &amp;ldquo;building an app&amp;rdquo; is a philosophical question I will leave to the comments section I do not have.&lt;/p&gt;
&lt;p&gt;This series is the honest write-up: what worked, what bit me, and what I would tell anyone starting the same experiment. Part 1 is about the counterintuitive first step, the one hour that made everything after it possible.&lt;/p&gt;
&lt;h2 id=&#34;the-empty-folder&#34;&gt;The empty folder&lt;/h2&gt;
&lt;p&gt;Day zero looked like this:&lt;/p&gt;
&lt;div class=&#34;code-wrap&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;workout-tracker/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CLAUDE.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── docs/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── ROADMAP.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── DATA-MODEL.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── DECISIONS.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── AI-WORKFLOW.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── .claude/agents/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;No code. Not one line. Just markdown.&lt;/p&gt;
&lt;p&gt;That felt backwards. Every instinct says start with &lt;code&gt;npm create vite&lt;/code&gt; and get pixels on screen. But AI-assisted development changes the economics of documentation completely, for one reason.&lt;/p&gt;
&lt;blockquote class=&#34;callout callout-insight&#34;&gt;
    &lt;p class=&#34;callout-title&#34;&gt;
      &lt;span class=&#34;callout-icon&#34;&gt;&lt;svg viewBox=&#34;0 0 16 16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;M8 0 L9.6 6.4 L16 8 L9.6 9.6 L8 16 L6.4 9.6 L0 8 L6.4 6.4 Z&#34;/&gt;&lt;/svg&gt;&lt;/span&gt;
      &lt;span class=&#34;callout-label&#34;&gt;Interesting!&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;The docs are the agent&amp;rsquo;s memory. An AI coding session starts from zero every time. Anything you don&amp;rsquo;t write down, you will re-explain every session, forever, at token prices. Write it down once and every future session starts already knowing the project.&lt;/p&gt;
  &lt;/blockquote&gt;&lt;p&gt;This is not a metaphor. A coding agent does not remember yesterday&amp;rsquo;s session, your preferences, or why you chose SQLite. Chat history dies when the session ends, and while it is alive it costs money to carry, because it rides along with every message you send. The repo, on the other hand, is free, permanent, and versioned. So the docs are not documentation in the usual sense, written after the fact for some hypothetical future reader. They are load-bearing infrastructure, consumed by the agent at the start of every single session.&lt;/p&gt;
&lt;p&gt;Once that clicked, spending the first hour on markdown stopped feeling like procrastination and started feeling like configuring the router before plugging in the clients.&lt;/p&gt;
&lt;h2 id=&#34;the-four-files-that-matter&#34;&gt;The four files that matter&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt; is the file Claude Code reads automatically at the start of every session (other tools have equivalents: rules files, instructions files, whatever your agent auto-loads). Mine is short and rule-dense: the stack, the architecture rules (&amp;ldquo;ALL business logic lives in the server, the client renders and calls /api&amp;rdquo;), workflow rules (&amp;ldquo;small commits, one feature per commit&amp;rdquo;), and cost rules (Part 2 is entirely about those). The guidance from the Claude Code docs is to treat it as the place you write down whatever you would otherwise re-explain, and to keep it concise, because shorter instruction files get followed more reliably.&lt;/p&gt;
&lt;p&gt;One sentence in mine paid for itself fifty times over: &amp;ldquo;The developer is learning, explain non-obvious choices briefly as you go, and prefer simple, readable code over clever code.&amp;rdquo; Every session, every explanation, calibrated to me, for free. If you are using a project like this to learn a stack, put a line like that in on day zero.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;docs/ROADMAP.md&lt;/strong&gt; breaks the project into phases with checkboxes. Phase 1: foundation. Phase 2: core logging loop. Phase 3: plans. Phase 4: PRs and progress charts. Each phase is small enough for one focused session, and each ends with its checkboxes ticked. This file became the heartbeat of the whole project, which is Part 3&amp;rsquo;s story.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;docs/DATA-MODEL.md&lt;/strong&gt; is the schema, written in SQL, before any code existed. Five tables, and, crucially, the business rules in prose: how PR detection should work, what the Epley 1RM formula is and where it stops being trustworthy, what a &amp;ldquo;workout&amp;rdquo; even is. Code can tell you what the app does. Only a doc can tell you what the app is supposed to do, which is exactly the distinction an agent needs when it writes tests.&lt;/p&gt;
&lt;p&gt;The file ended with a section called &amp;ldquo;Open questions (answer these before Phase 2)&amp;rdquo;: lbs or kg? Track RPE? How do bodyweight exercises work? I answered them inline like a form. That ten minute exercise surfaced decisions I would otherwise have made accidentally, mid-implementation, three phases deep, and at least one of them (bodyweight exercises) would have meant a schema migration if I had discovered it late.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;docs/DECISIONS.md&lt;/strong&gt; is a decision log with a rigid little format, three lines per entry:&lt;/p&gt;
&lt;div class=&#34;code-wrap&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Decision: &amp;lt;what we chose&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Why: &amp;lt;the reason, one or two sentences&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Alternatives: &amp;lt;what lost, and why&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It started with two entries. It now has over forty, and it is the file I would rescue from a fire. When a fresh session asks &amp;ldquo;why is weight stored in lbs only?&amp;rdquo; the answer is in the repo, dated, with the alternatives that lost. Future me does not do archaeology, and the agent does not re-litigate settled questions. If you have ever kept a change log for a firewall, this is the same instinct pointed at software decisions.&lt;/p&gt;
&lt;h2 id=&#34;day-one-measured&#34;&gt;Day one, measured&lt;/h2&gt;
&lt;p&gt;With the docs in place, day one went: install Node, &lt;code&gt;git init&lt;/code&gt;, resolve the open data-model questions, then paste a kickoff prompt (pre-written at the bottom of the roadmap) that scaffolded the Express + SQLite server. Schema, a seed script with 52 exercises, and the first three API routes. The agent tested everything with curl before calling it done, including the failure cases: bad exercise IDs got clean 400s, not stack traces.&lt;/p&gt;
&lt;p&gt;By evening there was a working API with validated inputs. The next morning there was a React client. The docs took about an hour of that. Everything else in this series runs on the rails laid in that hour.&lt;/p&gt;
&lt;h2 id=&#34;what-id-tell-you&#34;&gt;What I&amp;rsquo;d tell you&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Write CLAUDE.md first, or whatever your tool&amp;rsquo;s auto-loaded context file is. Rules you write once are rules you never repeat.&lt;/li&gt;
&lt;li&gt;Put the schema and the business rules in a doc, not just in code. Code says what. The doc says why, and what counts as correct, which is exactly what an agent needs to write real tests.&lt;/li&gt;
&lt;li&gt;Keep a decision log with a format. Decision, why, alternatives considered. Three lines per entry. It compounds.&lt;/li&gt;
&lt;li&gt;Answer the open questions before building. Make the doc ask you questions. The ten minutes I spent answering &amp;ldquo;how should bodyweight exercises work?&amp;rdquo; saved a schema migration later.&lt;/li&gt;
&lt;li&gt;Pre-write the next kickoff prompt. Ending each phase by writing the prompt that starts the next one means zero cold-start cost.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The pattern under all five: move context out of your head and out of the chat history, into the repo. Chat history dies with the session and costs money to carry. The repo is free, permanent, and versioned.&lt;/p&gt;
&lt;p&gt;Next up, &lt;a href=&#34;https://natebent.com/posts/zero-to-app-with-ai-part-2-keeping-it-cheap/&#34;&gt;Part 2&lt;/a&gt;: how I kept the whole thing cheap. Model routing, running the boring work through a local LLM on the homelab, and why more automation usually means a bigger bill.&lt;/p&gt;
&lt;h2 id=&#34;references-and-further-reading&#34;&gt;References and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://code.claude.com/docs/en/memory&#34;&gt;How Claude remembers your project&lt;/a&gt;, Claude Code docs. Covers CLAUDE.md loading behavior and what belongs in it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.claude.com/en/docs/claude-code/overview&#34;&gt;Claude Code overview&lt;/a&gt;, the official starting point if the tool is new to you.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
  </channel>
</rss>
