<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shane Wilkey</title>
    <description>The latest articles on Forem by Shane Wilkey (@southwestmogrown).</description>
    <link>https://forem.com/southwestmogrown</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F622425%2F51a9cb88-1c7d-4a26-b01e-b07ffd8b5028.jpeg</url>
      <title>Forem: Shane Wilkey</title>
      <link>https://forem.com/southwestmogrown</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/southwestmogrown"/>
    <language>en</language>
    <item>
      <title>Your RAG Chatbot Lies Because You're Chunking Wrong</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Sun, 12 Apr 2026 03:38:27 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/your-rag-chatbot-lies-because-youre-chunking-wrong-9ii</link>
      <guid>https://forem.com/southwestmogrown/your-rag-chatbot-lies-because-youre-chunking-wrong-9ii</guid>
      <description>&lt;p&gt;Three weeks into building FolioChat — a chatbot that lets portfolio visitors talk to my GitHub — I had a system that gave answers like a drunk Wikipedia editor.&lt;/p&gt;

&lt;p&gt;"Tell me about Shane's React work," someone would ask. It would respond with a rambling paragraph about my Python scripts, a random commit message, and somehow my college graduation year. The context was drifting so badly I was getting ready to scrap the whole thing.&lt;/p&gt;

&lt;p&gt;The fix turned out to be a single architectural decision that most RAG tutorials skip entirely: chunking by meaning instead of by size.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: You're Splitting Text, Not Meaning
&lt;/h2&gt;

&lt;p&gt;RAG — Retrieval Augmented Generation — sounds simple in tutorials. Chunk your documents, embed them, store them in a vector database, retrieve relevant chunks, answer questions.&lt;/p&gt;

&lt;p&gt;Most RAG implementations break at step one: chunking. Token-based splitting, the default in every tutorial, treats your content like a deck of cards to be shuffled. It doesn't care if it splits a function definition from its docstring, or cuts a project description in half. Here's what I started with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Seemed reasonable. It was not.
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simple_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When someone asked "What Python projects has Shane built?", the system would retrieve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Half of a README from a React project&lt;/li&gt;
&lt;li&gt;The middle of a commit message about fixing tests&lt;/li&gt;
&lt;li&gt;A random slice of documentation that mentioned Python once&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Token-based splitting fragments meaning across chunks. The model never sees a complete thought — just shards of context that fit a size budget. So it synthesizes these fragments into something grammatically correct and factually useless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Chunk by Meaning, Not by Size
&lt;/h2&gt;

&lt;p&gt;I almost killed the project before a frustrated Claude Desktop session saved it. The suggestion was simple: chunk by &lt;em&gt;meaning&lt;/em&gt; instead of by &lt;em&gt;size&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;GitHub repositories have natural semantic boundaries. A README has sections. Projects have distinct characteristics. Code has logical groupings. The data has shape — I was just ignoring it.&lt;/p&gt;

&lt;p&gt;The real &lt;code&gt;Chunker&lt;/code&gt; in FolioChat uses a plain dataclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;  &lt;span class="c1"&gt;# identity | project_overview | project_detail | project_tech | project_story
&lt;/span&gt;    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__post_init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five chunk types, each serving a specific retrieval purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;identity&lt;/code&gt; — one chunk per portfolio, who this developer is at the highest level&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project_overview&lt;/code&gt; — elevator pitch per repo, answers "tell me about X"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project_tech&lt;/code&gt; — pure stack signal per repo, answers "do you know PostgreSQL?"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project_story&lt;/code&gt; — the why, built from README intro and commit history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project_detail&lt;/code&gt; — one chunk per README section, architecture stays with architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Chunker&lt;/code&gt; class builds all of these from a single &lt;code&gt;portfolio_data&lt;/code&gt; dict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chunker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_identity_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;repos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_overview_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_tech_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_story_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_detail_chunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When someone asks "What technologies does Shane use?", retrieval targets &lt;code&gt;project_tech&lt;/code&gt; chunks instead of gambling on arbitrary text fragments. The question shape matches the chunk shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Embedding Backends, One Interface
&lt;/h2&gt;

&lt;p&gt;Semantic chunking is the hard part. Embedding, by comparison, is a config choice. FolioChat supports three backends — local (free, no API key), OpenAI, and Voyage — all behind a common interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalEmbedder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEmbedder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;MODEL_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
        &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;convert_to_numpy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;embed_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;384&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local embedder runs entirely on your machine — 384-dimension vectors, no API key, no cost. Swap backends with one config change — the rest of your code doesn't know the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ChromaDB Gotcha Everyone Hits
&lt;/h2&gt;

&lt;p&gt;ChromaDB expects string values in metadata. That sounds fine until you try to store lists or datetimes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This breaks ChromaDB
&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;languages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JavaScript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# lists don't serialize
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;            &lt;span class="c1"&gt;# datetimes don't serialize
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# This works
&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;languages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JavaScript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FolioChat serializes lists to JSON strings before storing and deserializes on the way out. Annoying the first time, invisible after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and After: What Semantic Chunking Actually Fixed
&lt;/h2&gt;

&lt;p&gt;The drunk Wikipedia editor became a chatbot that gives accurate, specific answers. The difference between "Shane has worked on various projects involving different technologies over time" and "Shane's FolioChat project is a RAG pipeline using FastAPI, ChromaDB, and sentence-transformers, deployed on Railway" is the RAG chunking strategy — nothing else.&lt;/p&gt;

&lt;p&gt;The full implementation — chunker, embedder, ChromaDB layer, FastAPI server, and React widget — is &lt;a href="https://github.com/southwestmogrown/foliochat" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Install it, point it at your GitHub username, and have a working portfolio chatbot in about five minutes.&lt;/p&gt;

&lt;p&gt;Next week: the complete FolioChat architecture walkthrough, and why most developer portfolios are effectively invisible to the people looking at them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: rag, python, chromadb, embeddings, llm&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Series: Building With AI Agents — Article 5 of 12&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>python</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>From GitHub Issue to Merged PR: My Complete AI-Powered Development Workflow</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Thu, 09 Apr 2026 04:04:25 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/from-github-issue-to-merged-pr-my-complete-ai-powered-development-workflow-3jo</link>
      <guid>https://forem.com/southwestmogrown/from-github-issue-to-merged-pr-my-complete-ai-powered-development-workflow-3jo</guid>
      <description>&lt;p&gt;A few months ago, I was burning through an embarrassing amount of Claude tokens monthly while shipping maybe two meaningful features. The wake-up call came during a debugging session where I spent the better part of an hour having Claude explain why the same function worked differently in different contexts—only to discover I'd been feeding it outdated code samples from three iterations ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Wing-It Development
&lt;/h2&gt;

&lt;p&gt;Here's what broken AI development looks like: you start a conversation with Claude about adding user authentication, thirty messages later you're discussing database migrations, then somehow you're refactoring your entire error handling system. Each context switch burns tokens, and worse, the AI starts hallucinating solutions based on code it thinks exists but doesn't.&lt;/p&gt;

&lt;p&gt;I was treating Claude like Stack Overflow with a conversation interface—throwing problems at it reactively instead of systematically. The result? Half-implemented features, context drift that made debugging impossible, and token bills that made me question whether AI development was worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning Is Non-Negotiable
&lt;/h2&gt;

&lt;p&gt;The breakthrough came when I stopped coding first and started planning first. Every feature now begins with a structured conversation where Claude and I define three things before touching any code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context boundaries&lt;/strong&gt; — exactly what codebase state we're working from, what's been changed recently, and what assumptions we can make. No "you know that auth system we built" — explicit file references and current state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool constraints&lt;/strong&gt; — which APIs we're using, what our deployment pipeline expects, and what our testing setup can handle. Claude needs to know it's writing for GitHub Actions, not generic CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success criteria&lt;/strong&gt; — what "done" looks like, not just functionally but how we'll know the code is maintainable six months from now.&lt;/p&gt;

&lt;p&gt;This planning phase costs relatively little but saves a lot downstream. Claude generates much better code when it knows exactly what problem it's solving within defined constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document Everything, Start Fresh Sessions
&lt;/h2&gt;

&lt;p&gt;The temptation is to keep one long conversation going because it feels efficient. It's not. After enough back-and-forth, Claude starts making assumptions based on earlier context that may no longer be accurate. I learned to recognize the warning signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude references code I never shared in the current session&lt;/li&gt;
&lt;li&gt;Solutions become overly complex for simple problems&lt;/li&gt;
&lt;li&gt;The AI starts suggesting fixes for problems that don't exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of fighting context drift, I document everything mid-conversation. When planning a feature, I have Claude generate a structured project brief that includes the technical approach, file structure, and key implementation notes. Then I save that brief and start a fresh session when it's time to code.&lt;/p&gt;

&lt;p&gt;The brief becomes the handoff document between sessions. New Claude session gets the brief, current codebase state, and the specific task. No context drift, no hallucinated solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adversarial Code Review Catches Complacency
&lt;/h2&gt;

&lt;p&gt;Here's something I discovered by accident: Claude gets comfortable with its own code. If I ask it to review code it just wrote, it tends to approve its own patterns even when they're problematic. The AI develops blind spots around its own work.&lt;/p&gt;

&lt;p&gt;The solution is adversarial review using different models. After Claude writes a feature, I paste the code into a conversation with a different model and ask for a critical review without mentioning Claude wrote it. Different models catch different issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Claude wrote this authentication decorator
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;require_auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorated_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;decorated_function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude reviewed its own code: "Looks good, handles the authentication check properly."&lt;/p&gt;

&lt;p&gt;The adversarial review: "This loses the function metadata and doesn't handle POST data properly on redirect. Also no CSRF protection."&lt;/p&gt;

&lt;p&gt;Both criticisms were valid. Claude had gotten comfortable with a pattern that worked but wasn't robust. Cross-model review catches these blind spots before they become production issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Workflow in Action
&lt;/h2&gt;

&lt;p&gt;Here's how a typical feature development cycle works now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brainstorming phase&lt;/strong&gt; — I describe the feature need to Claude conversationally. We explore approaches, discuss tradeoffs, and identify the core complexity. This generates understanding, not code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planning phase&lt;/strong&gt; — Claude creates a detailed project plan with milestones, breaking the feature into discrete GitHub issues. Each issue gets acceptance criteria, technical notes, and priority ranking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Issue: Add password reset functionality&lt;/span&gt;
&lt;span class="gs"&gt;**Priority:**&lt;/span&gt; P1
&lt;span class="gs"&gt;**Acceptance Criteria:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; User can request password reset via email
&lt;span class="p"&gt;-&lt;/span&gt; Reset links expire after 24 hours  
&lt;span class="p"&gt;-&lt;/span&gt; Process works with existing auth system
&lt;span class="gs"&gt;**Technical Notes:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Extend User model with reset_token field
&lt;span class="p"&gt;-&lt;/span&gt; Add email service integration
&lt;span class="p"&gt;-&lt;/span&gt; Update auth middleware to handle reset flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Project setup&lt;/strong&gt; — Claude generates GitHub Projects configuration, complete with automation rules. I can literally copy-paste its output into GitHub's project setup interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development&lt;/strong&gt; — Each issue becomes its own focused Claude session using GitHub Codespaces. Claude gets the project brief, current codebase, and single issue context. Write code, test locally, commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review&lt;/strong&gt; — Different AI model reviews the code adversarially before I create the pull request.&lt;/p&gt;

&lt;p&gt;The workflow scales because each step is self-contained and documented. I can pick up development from my phone using Codespaces, spend fifteen minutes with the Claude VS Code extension getting an issue implemented, then put it down again.&lt;/p&gt;

&lt;h2&gt;
  
  
  When It All Clicked
&lt;/h2&gt;

&lt;p&gt;The system clicked during a weekend when I was traveling but wanted to knock out a few small issues. Using just my laptop and GitHub's web interface, I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created three new issues from user feedback&lt;/li&gt;
&lt;li&gt;Had Claude prioritize them within the existing project&lt;/li&gt;
&lt;li&gt;Used Codespaces to implement the highest priority fix&lt;/li&gt;
&lt;li&gt;Got adversarial review from a different model&lt;/li&gt;
&lt;li&gt;Merged the PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What used to take half a day and leave me second-guessing every decision took under an hour — and I was confident in the output because the process held. That's the real payoff.&lt;/p&gt;

&lt;p&gt;The key insight is that sustainable AI development isn't about finding the perfect prompt — it's about building repeatable systems that prevent the expensive mistakes. Planning upfront, documenting everything, and using fresh context prevents the token-burning death spirals that make AI development feel unsustainable.&lt;/p&gt;

&lt;p&gt;Every system has overhead, but this overhead scales. The planning documents become project knowledge. The adversarial reviews catch patterns I need to improve. The structured workflow means I can develop from anywhere without losing momentum.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Next up, we're dipping our toes into &lt;a href="https://github.com/southwestmogrown/foliochat" rel="noopener noreferrer"&gt;FolioChat&lt;/a&gt; to explore how RAG actually works for developers who build things. We'll see if retrieval-augmented generation can maintain context better than conversation history, and whether it's worth the complexity for real development workflows.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/southwestmogrown/ai-workflow-toolkit" rel="noopener noreferrer"&gt;complete-ai-workflow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: ai-development, claude, github, workflow, productivity&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Series: Building With AI Agents — Article 4 of 12&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>From Chaos to Shipped — A Practical Workflow for Solo Developers</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Sat, 14 Mar 2026 06:13:16 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/from-chaos-to-shipped-a-practical-workflow-for-solo-developers-1h9n</link>
      <guid>https://forem.com/southwestmogrown/from-chaos-to-shipped-a-practical-workflow-for-solo-developers-1h9n</guid>
      <description>&lt;p&gt;If you've ever ended a coding session unsure what you actually finished, switched between projects and lost your train of thought, or deployed something only to realize you forgot to set an environment variable — this guide is for you.&lt;/p&gt;

&lt;p&gt;I built this &lt;a href="https://github.com/southwestmogrown/ai-workflow-toolkit/tree/main/workflow-resources" rel="noopener noreferrer"&gt;workflow&lt;/a&gt; to solve my own problems. Context-switching between projects was killing my focus, and half my best ideas were disappearing before I could act on them. I needed a system that would protect my creative momentum — not just track tasks, but give me enough structure to stay loose and keep building. What came out of that is a four-phase workflow I now use daily.&lt;/p&gt;

&lt;p&gt;Solo development is uniquely hard. There's no standup to force clarity, no PR reviewer to catch the obvious, no project manager tracking what's in flight. The chaos isn't a character flaw. It's a systems problem, and systems problems have systems solutions.&lt;/p&gt;

&lt;p&gt;This guide walks through the four phases — &lt;strong&gt;Capture → Plan → Build → Ship&lt;/strong&gt; — designed around tools you're probably already using: a text editor, GitHub, and a bit of Markdown. No new apps required.&lt;/p&gt;




&lt;h2&gt;
  
  
  The four phases at a glance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkor3yhlr9e9ziavxhthy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkor3yhlr9e9ziavxhthy.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Core habit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Drop ideas in &lt;code&gt;IDEA.md&lt;/code&gt;; triage weekly; every real item becomes a GitHub issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write a feature brief (goal, scope, non-goals, done-when) before touching a branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Conventional commits + a breadcrumb note before every context switch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ship&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-review the diff, run the deploy checklist, write the retro note&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Phase 1: Capture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F121p65mbo9cwv3u9udae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F121p65mbo9cwv3u9udae.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first place chaos enters is the moment an idea strikes. Most developers either ignore it (gone by morning) or immediately start building (hello, scope creep). The Capture phase gives you a third option: a frictionless landing spot that doesn't interrupt what you're currently working on.&lt;/p&gt;

&lt;h3&gt;
  
  
  The IDEA.md file
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F237irzd0fk76vndmzltp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F237irzd0fk76vndmzltp.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep a single &lt;code&gt;IDEA.md&lt;/code&gt; per project — or one global backlog. When a thought surfaces, drop a one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [2025-03-14] [QuizQuest]  add breadcrumb nav to lesson view
&lt;span class="p"&gt;-&lt;/span&gt; [2025-03-14] [BAK EOS]    auto-fill shift date from system clock
&lt;span class="p"&gt;-&lt;/span&gt; [2025-03-15] [QuizQuest]  dark mode toggle in settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No polish. No planning. Just capture. The goal is to stop losing ideas to context, not to evaluate them in the moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Weekly triage
&lt;/h3&gt;

&lt;p&gt;Once a week — end of Friday works well — go through &lt;code&gt;IDEA.md&lt;/code&gt; and run each item through three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which project does this belong to?&lt;/li&gt;
&lt;li&gt;What's the rough size — an hour, a day, or a sprint?&lt;/li&gt;
&lt;li&gt;Is this worth a GitHub issue? If yes, create one and delete the line. If no, delete it anyway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An idea that can't survive a 10-second triage wasn't worth building.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turning an idea into a GitHub issue
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6sn7n2pq4dd4s3xlalu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6sn7n2pq4dd4s3xlalu.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every real work item lives in GitHub. When something survives triage, open an issue using a consistent template. At minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What&lt;/strong&gt; you're building and &lt;strong&gt;why&lt;/strong&gt; (one or two sentences)&lt;/li&gt;
&lt;li&gt;What's explicitly &lt;strong&gt;in scope&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What's explicitly &lt;strong&gt;out of scope&lt;/strong&gt; — this is the most important line&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;definition of done&lt;/strong&gt;: how will you know it's complete?&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;task checklist&lt;/strong&gt;: the individual steps you'll take&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Label the issue (&lt;code&gt;feat&lt;/code&gt;, &lt;code&gt;fix&lt;/code&gt;, &lt;code&gt;chore&lt;/code&gt;, &lt;code&gt;docs&lt;/code&gt;), assign it to a milestone if timing matters, and link it to your project board. The issue is now the single source of truth for that work item — your planning doc, task list, and history all in one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Most solo dev chaos traces back to work that exists only in someone's head. GitHub issues externalize your intent. When you context-switch, get interrupted, or return to a project after two weeks away, the issue tells you exactly what you were building and why.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 2: Plan
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmomnyl0k8l4jc6cegmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmomnyl0k8l4jc6cegmw.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Planning feels like overhead until you've spent three hours building the wrong thing. The Plan phase is short by design: answer the questions that will otherwise derail you mid-build — before you start coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write a feature brief
&lt;/h3&gt;

&lt;p&gt;Before touching a branch, write two to four sentences directly on the GitHub issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; what the feature accomplishes, from the user's perspective&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; what's included in this issue, specifically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-goals:&lt;/strong&gt; what's explicitly deferred to later&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Done when:&lt;/strong&gt; a concrete, testable condition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The non-goals line is the most important. Writing "not touching the notification system yet" prevents the 2am realization that you've rebuilt half the app to support something that wasn't in scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build a task checklist on the issue
&lt;/h3&gt;

&lt;p&gt;GitHub issues support Markdown checkboxes. Use them. Break the feature into discrete tasks, each completable in a single session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## tasks&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] design Supabase schema for coach_sessions table
&lt;span class="p"&gt;-&lt;/span&gt; [ ] write pgvector similarity search query
&lt;span class="p"&gt;-&lt;/span&gt; [ ] build useCoach hook with API call
&lt;span class="p"&gt;-&lt;/span&gt; [ ] wire hook to CoachPanel component
&lt;span class="p"&gt;-&lt;/span&gt; [ ] add loading/error states
&lt;span class="p"&gt;-&lt;/span&gt; [ ] smoke test with 3 real quiz attempts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checklist serves three purposes: it's your to-do list while building, a progress indicator visible on the issue, and a record of what actually shipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cut the branch
&lt;/h3&gt;

&lt;p&gt;Name branches with a consistent pattern: &lt;code&gt;feat/42-socratic-coach-ui&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type: &lt;code&gt;feat&lt;/code&gt;, &lt;code&gt;fix&lt;/code&gt;, &lt;code&gt;chore&lt;/code&gt;, &lt;code&gt;docs&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;42&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issue number — ties the branch to your planning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;socratic-coach-ui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short slug describing the work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Never work directly on &lt;code&gt;main&lt;/code&gt;. The branch name alone should tell you what you were building and where to find the context.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3: Build
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyc1v2vyfzz3inpc2509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyc1v2vyfzz3inpc2509.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Build phase is where the actual work happens, and where context gets lost. Two habits make the difference: disciplined commits and breadcrumb notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conventional commit messages
&lt;/h3&gt;

&lt;p&gt;Conventional commits make your git log readable, your diffs reviewable, and automated tooling possible. The format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type(scope): short description
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;feat&lt;span class="o"&gt;(&lt;/span&gt;coach&lt;span class="o"&gt;)&lt;/span&gt;: add pgvector similarity search
fix&lt;span class="o"&gt;(&lt;/span&gt;auth&lt;span class="o"&gt;)&lt;/span&gt;: redirect loop on expired session token
chore: upgrade next to 15.2.1
docs&lt;span class="o"&gt;(&lt;/span&gt;api&lt;span class="o"&gt;)&lt;/span&gt;: document /modules endpoint
feat&lt;span class="o"&gt;(&lt;/span&gt;api&lt;span class="o"&gt;)!&lt;/span&gt;: rename /lessons to /modules    &lt;span class="c"&gt;# breaking change&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Use when...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;adding new functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;correcting a bug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tooling, config, dependency updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;documentation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;refactor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;restructuring without behavior change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;adding or updating tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;perf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;performance improvements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imperative mood: "add" not "added"&lt;/li&gt;
&lt;li&gt;72 characters or fewer in the subject line&lt;/li&gt;
&lt;li&gt;One logical change per commit&lt;/li&gt;
&lt;li&gt;Reference the issue in the body: &lt;code&gt;Closes #42&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The breadcrumb habit
&lt;/h3&gt;

&lt;p&gt;This is the single highest-leverage habit for multi-project solo development. Before closing your editor or switching projects, leave a note indicating exactly where you stopped and what comes next.&lt;/p&gt;

&lt;p&gt;In the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TODO: next — wire Supabase call in useCoach.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Left off: schema works, hook needs error boundary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or as a comment on the GitHub issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pausing here. Completed: schema, pgvector query.
Next: build useCoach hook, then CoachPanel wiring.
Blocker: need to check Supabase RLS policy for coach_sessions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The real cost of skipping this:&lt;/strong&gt; Without a breadcrumb, returning to a project after even two days means 20–30 minutes of re-orienting: re-reading diffs, re-opening tabs, re-figuring out intent. Multiply that across three active projects and it's a real tax on every session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Draft PRs as checkpoints
&lt;/h3&gt;

&lt;p&gt;For features spanning multiple sessions, open a draft pull request early. It gives you a visual diff of everything committed so far, a comment thread for notes and decisions, and a psychological anchor — the work is in progress, not in limbo.&lt;/p&gt;

&lt;p&gt;Prefix the title with &lt;code&gt;[WIP]&lt;/code&gt; or use GitHub's native Draft PR feature. Convert to ready when all checklist items on the issue are ticked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 4: Ship
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajhtvucmn45htup4mbhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajhtvucmn45htup4mbhj.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shipping is where most solo projects fumble — not because the code is wrong, but because the surrounding process gets skipped. A five-minute pre-deploy check prevents the 11pm "why is production broken" investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  PR self-review before merging
&lt;/h3&gt;

&lt;p&gt;Read your own diff before merging. Before you hit merge, verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The linked GitHub issue will be closed by this merge&lt;/li&gt;
&lt;li&gt;All checklist items on the issue are ticked&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;console.log&lt;/code&gt;, debug code, or commented-out blocks remain&lt;/li&gt;
&lt;li&gt;Any changed behavior is reflected in docs or CHANGELOG&lt;/li&gt;
&lt;li&gt;The PR description explains the change for future reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Merge via squash to keep &lt;code&gt;main&lt;/code&gt; history clean. One feature, one commit on &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The deploy checklist
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgiqgnokb76usfxzxl4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgiqgnokb76usfxzxl4y.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run through a checklist before every production push. For a Next.js + Supabase stack:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-push&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;next build&lt;/code&gt; passes locally with zero errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-push&lt;/td&gt;
&lt;td&gt;TypeScript clean: &lt;code&gt;tsc --noEmit&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-push&lt;/td&gt;
&lt;td&gt;No debug code remaining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;All new &lt;code&gt;.env&lt;/code&gt; vars added to Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;No secrets in source code or commit history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Migrations written and tested locally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Migrations applied to production BEFORE deploying app&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-deploy&lt;/td&gt;
&lt;td&gt;Vercel build logs green&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-deploy&lt;/td&gt;
&lt;td&gt;Smoke test the happy path in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-deploy&lt;/td&gt;
&lt;td&gt;GitHub issue closed and PR merged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Critical order:&lt;/strong&gt; Always run database migrations &lt;em&gt;before&lt;/em&gt; deploying the application code that depends on them. Deploying first creates a window where new code runs against the old schema — this is the most common source of production incidents in small Next.js + Supabase projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The retro note
&lt;/h3&gt;

&lt;p&gt;After every deploy, write one sentence in &lt;code&gt;RETRO.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;2025-03-14 QuizQuest — deploy failed: migrations not run before push. Add to checklist.
2025-03-10 BAK EOS   — .env var missing in Vercel; caught it in smoke test.
2025-03-05 QuizQuest — feature took 3x longer: scope wasn't locked. Write the brief first.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review &lt;code&gt;RETRO.md&lt;/code&gt; monthly. When the same note appears three times, you have a process problem worth fixing at the root. These compound into real improvements over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Files to keep in every project
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IDEA.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Idea backlog. Triage weekly. Promote to GitHub issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RETRO.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One line per deploy. Review monthly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.github/ISSUE_TEMPLATE/feature.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issue template. Auto-populates when you open a new issue.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The quick-reference cheat sheets
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6brnfy61tmhaa4ieu2q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6brnfy61tmhaa4ieu2q2.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the commit message reference card — keep it open in a tab until the format is second nature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat(scope): add thing          # new feature
fix(scope): correct thing       # bug fix
chore: update thing             # no prod code change
docs(scope): document thing     # docs only
refactor(scope): reshape thing  # no behavior change
feat(scope)!: rename thing      # breaking change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And branch naming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat/42-socratic-coach-ui
fix/38-auth-redirect-loop
chore/51-upgrade-next
docs/55-api-endpoint-reference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This workflow is intentionally minimal. It lives entirely in GitHub and a couple of Markdown files you can drop into any project in under a minute. No new tools, no subscriptions, no onboarding.&lt;/p&gt;

&lt;p&gt;I built it because I was tired of losing momentum between sessions and watching good ideas evaporate. Using it consistently gave me back creative space I didn't realize I'd lost — fewer "where was I?" mornings, more time actually building.&lt;/p&gt;

&lt;p&gt;The habits that move the needle most, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Breadcrumb notes&lt;/strong&gt; — the fastest fix for context-switch pain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature briefs with non-goals&lt;/strong&gt; — the fastest fix for scope creep&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The deploy checklist&lt;/strong&gt; — the fastest fix for production incidents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RETRO.md&lt;/strong&gt; — the slowest fix, but the one that compounds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start with one. Add the others as the pain point it solves becomes real. &lt;/p&gt;

&lt;p&gt;Grab my free interactive workflow diagram, commit cheat sheet, deploy checklist, and other helpful resources &lt;a href="https://github.com/southwestmogrown/ai-workflow-toolkit/tree/main/workflow-resources" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tooling</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Turned a Bespoke Code Reviewer Into a Skill Any Project Can Use</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Fri, 13 Mar 2026 22:00:00 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/how-i-turned-a-bespoke-code-reviewer-into-a-skill-any-project-can-use-1npo</link>
      <guid>https://forem.com/southwestmogrown/how-i-turned-a-bespoke-code-reviewer-into-a-skill-any-project-can-use-1npo</guid>
      <description>&lt;p&gt;The review layer in Code Genie works. The problem is it's hardwired — the criteria, the persona, the output format are all written specifically for that project. Every new project that wants the same quality gate has to rebuild it from scratch. That's not a system, that's a copy-paste habit. Here's how I extracted it into a reusable Skill.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Bespoke Logic
&lt;/h2&gt;

&lt;p&gt;Here's what the &lt;a href="https://github.com/southwestmogrown/code_genie" rel="noopener noreferrer"&gt;Code Genie&lt;/a&gt; reviewer looks like in its original form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior Code Reviewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Review code critically and objectively. Identify bugs, edge cases,
    convention violations, and anything that would fail in production.
    Do not give the benefit of the doubt.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a senior engineer with high standards and low tolerance
    for sloppy code. You&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ve seen too many production incidents caused by code
    that looked fine in review. You are thorough, specific, and direct.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;review_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Review the following Python code for a crewAI agent workflow.
    Check for: correct use of crewAI Agent and Task APIs, proper error handling
    for LLM failures, retry logic that won&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t loop infinitely, and clear separation
    of agent responsibilities. Return PASS with brief justification, or FAIL with
    specific actionable feedback.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASS or FAIL with specific feedback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the task description. It's reviewing Python code for a crewAI agent workflow. It's checking for correct use of crewAI APIs. It knows the retry logic is there and checks it specifically. Every line of that task is written for Code Genie and Code Genie only.&lt;/p&gt;

&lt;p&gt;Drop this into a Next.js project and it'll review TypeScript like it's looking for crewAI API misuse. Drop it into a data pipeline and it'll flag missing retry logic that was never supposed to be there. The reviewer works — inside the project it was written for.&lt;/p&gt;

&lt;p&gt;That's the cost of bespoke logic. It's not wrong. It's just not portable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Reusable" Actually Means
&lt;/h2&gt;

&lt;p&gt;Before extracting anything, it helps to define what you're extracting toward. A reusable Skill needs to do three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work without project-specific assumptions baked in.&lt;/strong&gt; The Skill can't know in advance what language you're using, what framework you're on, or what your conventions look like. That information has to come from outside — from CLAUDE.md, from the task prompt, from context passed in at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accept context from whatever project it's dropped into.&lt;/strong&gt; The Skill should have explicit slots for project-specific information. Not hardcoded defaults — actual inputs it expects to receive and knows how to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Produce output in a consistent format.&lt;/strong&gt; Whatever calls the Skill — a crewAI agent, a GitHub Actions workflow, a human reading the result — should be able to rely on the output looking the same every time. PASS or FAIL, specific feedback, actionable items. No surprises.&lt;/p&gt;

&lt;p&gt;That's the design spec. If the extracted Skill satisfies all three, it's genuinely reusable. If it satisfies two out of three, it's still bespoke with better formatting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Identifying What's Generic vs What's Project-Specific
&lt;/h2&gt;

&lt;p&gt;The extraction exercise is simple in theory: go through the original reviewer and put everything into one of two buckets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic&lt;/strong&gt; — applies to any code review, regardless of project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The reviewer persona (skeptical, specific, direct)&lt;/li&gt;
&lt;li&gt;The output format (PASS or FAIL with actionable feedback)&lt;/li&gt;
&lt;li&gt;The core review criteria (correctness, edge cases, readability)&lt;/li&gt;
&lt;li&gt;The instruction not to give the benefit of the doubt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Project-specific&lt;/strong&gt; — belongs in CLAUDE.md or the task prompt, not the Skill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The language and framework being reviewed&lt;/li&gt;
&lt;li&gt;The specific APIs being checked&lt;/li&gt;
&lt;li&gt;The architectural patterns to enforce&lt;/li&gt;
&lt;li&gt;The conventions that count as violations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the same reviewer split into those two buckets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GENERIC — goes into the Skill
&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior Code Reviewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review code critically. Identify bugs, edge cases, and anything that would fail in production.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a senior engineer with high standards. You are thorough, specific, and direct.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;output_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASS with brief justification, or FAIL with specific actionable feedback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# PROJECT-SPECIFIC — goes into the task prompt, sourced from CLAUDE.md
&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;framework&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crewAI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;specific_checks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crewAI Agent and Task API usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry logic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent separation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;conventions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[from CLAUDE.md]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it's split this cleanly, the Skill writes itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Writing the Skill
&lt;/h2&gt;

&lt;p&gt;Here's the full &lt;code&gt;SKILL_code_review.md&lt;/code&gt;, built from the generic bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-review&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Review code critically and return a structured PASS or FAIL with&lt;/span&gt;
&lt;span class="s"&gt;specific, actionable feedback. Use this skill whenever an agent or workflow needs&lt;/span&gt;
&lt;span class="s"&gt;a quality gate on generated or submitted code. Trigger when a task involves&lt;/span&gt;
&lt;span class="s"&gt;reviewing, evaluating, or checking code before it proceeds to the next step.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Code Review Skill&lt;/span&gt;

A reusable code review layer for agent workflows and manual review tasks.

&lt;span class="gu"&gt;## Reviewer Persona&lt;/span&gt;

You are a senior software engineer with high standards and low tolerance for
code that merely looks correct. You have seen too many production incidents
caused by plausible-looking code that was never properly scrutinized. You are
thorough, specific, and direct. You do not give the benefit of the doubt.

&lt;span class="gu"&gt;## What You Always Check&lt;/span&gt;

Regardless of language or framework, every review covers:
&lt;span class="p"&gt;
1.&lt;/span&gt; &lt;span class="gs"&gt;**Correctness**&lt;/span&gt; — Does the code do what it's supposed to do?
   Does it handle the happy path? Does it handle failure?
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Edge cases**&lt;/span&gt; — What happens at the boundaries? Empty input,
   null values, unexpected types, off-by-one conditions.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Readability**&lt;/span&gt; — Can another developer understand this code
   without asking questions?
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="gs"&gt;**Side effects**&lt;/span&gt; — Does this code do anything it shouldn't?
   Modify state it doesn't own? Make calls it wasn't asked to make?

&lt;span class="gu"&gt;## What You Check From Project Context&lt;/span&gt;

The following criteria come from the project's CLAUDE.md and task prompt.
Apply them in addition to the universal criteria above:
&lt;span class="p"&gt;
-&lt;/span&gt; Language and framework conventions
&lt;span class="p"&gt;-&lt;/span&gt; Architectural patterns to enforce or avoid
&lt;span class="p"&gt;-&lt;/span&gt; Specific APIs or libraries in use
&lt;span class="p"&gt;-&lt;/span&gt; Anti-patterns explicitly called out in CLAUDE.md

&lt;span class="gu"&gt;## Output Format&lt;/span&gt;

Return exactly one of the following:

&lt;span class="gs"&gt;**PASS**&lt;/span&gt;
Brief justification (1-2 sentences). What makes this code acceptable.

&lt;span class="gs"&gt;**FAIL**&lt;/span&gt;
Specific feedback only. For each issue:
&lt;span class="p"&gt;-&lt;/span&gt; What the problem is
&lt;span class="p"&gt;-&lt;/span&gt; Where it is (function name, line reference, or code snippet)
&lt;span class="p"&gt;-&lt;/span&gt; What the fix should be

Do not return vague feedback. "This could be improved" is not actionable.
"The retry loop on line 24 has no exit condition — add a max_retries check
before the recursive call" is actionable.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the complete Skill. Generic enough to work anywhere, structured enough to produce consistent output, with explicit slots for project context to plug into.&lt;/p&gt;




&lt;h2&gt;
  
  
  Plugging It Back Into Code Genie
&lt;/h2&gt;

&lt;p&gt;With the Skill written, the original Code Genie reviewer slims down considerably. The agent definition drops to its essentials and delegates to the Skill. The project-specific criteria move into the task prompt where they belong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior Code Reviewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Apply the code-review Skill to evaluate the submitted code.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You follow the code-review Skill process precisely.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;review_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Using the code-review Skill, review the submitted Python code.

    Project context (from CLAUDE.md):
    - Language: Python 3.11
    - Framework: crewAI
    - Conventions: agents have single responsibilities, tasks return structured output
    - Anti-patterns: no raw exception swallowing, no infinite retry loops

    Apply both the universal criteria from the Skill and the project context above.
    Return PASS or FAIL with specific feedback.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASS or FAIL with specific feedback per the code-review Skill format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop still works exactly as before. The output format is identical. But the review layer is now a dependency rather than a hardcoded implementation — and that distinction matters the next time you need a quality gate somewhere else.&lt;/p&gt;




&lt;h2&gt;
  
  
  Plugging It Into a Different Project
&lt;/h2&gt;

&lt;p&gt;Here's the same Skill wired into a completely different context — a Next.js TypeScript project with no connection to crewAI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;review_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Using the code-review Skill, review the submitted TypeScript code.

    Project context (from CLAUDE.md):
    - Language: TypeScript strict
    - Framework: Next.js 16, React 19
    - Conventions: named exports, Result pattern for error handling,
      no raw throws in business logic
    - Anti-patterns: no default exports, no useEffect for data fetching,
      do not mutate props

    Apply both the universal criteria from the Skill and the project context above.
    Return PASS or FAIL with specific feedback.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASS or FAIL with specific feedback per the code-review Skill format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same Skill. Different language, different framework, different conventions. The Skill adapts because the project-specific context is coming from the task prompt — sourced from CLAUDE.md — not baked into the Skill itself. The reviewer persona, the universal criteria, the output format are all unchanged.&lt;/p&gt;

&lt;p&gt;That's portability in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Extract vs When to Leave It Bespoke
&lt;/h2&gt;

&lt;p&gt;Not everything should be a Skill. The extraction is worth the effort when all three of the following are true:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The logic is genuinely reusable.&lt;/strong&gt; If you can't imagine using it in at least two different projects, it probably belongs where it is. Extraction for its own sake adds maintenance overhead without payoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project-specific parts can be cleanly separated.&lt;/strong&gt; If you spend more time trying to untangle what's generic from what's specific than you would just rewriting it for the next project, leave it bespoke. The extraction exercise should feel like sorting, not surgery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll actually use it again.&lt;/strong&gt; This one sounds obvious but it's easy to miss. Building a reusable Skill you never reach for is just premature abstraction with extra steps.&lt;/p&gt;

&lt;p&gt;If all three are true, extract it. If one or two are true, extract the concept but not necessarily the implementation — document what worked and use it as a reference when the next similar problem comes up.&lt;/p&gt;

&lt;p&gt;The code review Skill cleared all three bars easily. The reviewer persona and output format are the same everywhere. The project-specific criteria separated cleanly into the task prompt. And a quality gate on generated code is useful in every project that generates code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The code review Skill is now in the &lt;code&gt;[ai-workflow-toolkit](https://github.com/southwestmogrown/ai-workflow-toolkit)&lt;/code&gt; repo alongside the CLAUDE.md generator and the README writer. Three skills, one repo, all portable.&lt;/p&gt;

&lt;p&gt;The pattern behind all three is the same: build it bespoke first, extract what's generic, make it portable. Resist the urge to abstract before you've built something real — you can't know what's generic until you've seen the specific version work.&lt;/p&gt;

&lt;p&gt;Next up: how I'm using these building blocks to wire up a full agent workflow on GitHub issues — from writing the issue to merging the PR, with Claude handling every step in between.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: python, ai, crewai, webdev&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Series: Building With AI Agents — Article 3 of 12&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>crewai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Code Genie: I Built a Self-Reviewing Code Generator with CrewAI</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Fri, 13 Mar 2026 03:56:17 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/-code-genie-i-built-a-self-reviewing-code-generator-with-crewai-1hi</link>
      <guid>https://forem.com/southwestmogrown/-code-genie-i-built-a-self-reviewing-code-generator-with-crewai-1hi</guid>
      <description>&lt;p&gt;Most AI coding tools are one-shot: you ask, they answer, you decide if it's good. That's not an agent — that's autocomplete with better vocabulary. Code Genie works differently. It writes code, reviews it, finds its own problems, and iterates until it's satisfied. I built it by hand while learning crewAI, and the process taught me more about agentic systems than any tutorial ever could.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With One-Shot Code Generation
&lt;/h2&gt;

&lt;p&gt;If you've used Claude, ChatGPT, or any AI coding tool for more than a week, you've hit the wall.&lt;/p&gt;

&lt;p&gt;You write a prompt. You get back code that looks right. You drop it in, run it, and something breaks — not catastrophically, just quietly. An edge case that wasn't handled. An assumption baked in that doesn't match your data. A function that works in isolation but fails in context.&lt;/p&gt;

&lt;p&gt;The problem isn't that the model is bad. The problem is that nobody checked the output before it reached you.&lt;/p&gt;

&lt;p&gt;Human code review exists for exactly this reason. A second set of eyes catches things the original author missed — not because the reviewer is smarter, but because they're reading it fresh, looking for problems instead of assuming it works. One-shot AI tools skip that step entirely. They generate, hand off, and move on. The gap between "looks right" and "is right" is yours to close.&lt;/p&gt;

&lt;p&gt;I wanted to close it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  What an Agentic Loop Actually Is
&lt;/h2&gt;

&lt;p&gt;An agentic loop is: generate → evaluate → decide → repeat.&lt;/p&gt;

&lt;p&gt;That's it. Three steps and a condition. What makes it agentic is the decide step — something has to determine whether the output is good enough to stop, or whether it needs another pass. Without that decision layer, you just have automation. With it, you have something that can improve its own output without a human in the loop.&lt;/p&gt;

&lt;p&gt;Applied to code generation, the loop looks like this: a writer produces a snippet, a reviewer evaluates it against a set of criteria, the reviewer's verdict either passes the output or sends it back with specific feedback, and the writer tries again with that feedback in hand. The loop runs until the output passes review or hits a retry limit.&lt;/p&gt;

&lt;p&gt;Simple pattern. Surprisingly powerful in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why crewAI
&lt;/h2&gt;

&lt;p&gt;crewAI is a Python framework for building multi-agent systems. The core abstraction is exactly what it sounds like: you define a crew of agents, give each one a role, a goal, and a backstory, assign them tasks, and let the framework handle the orchestration.&lt;/p&gt;

&lt;p&gt;crewAI manages the execution order, passes output between agents, and keeps the loop running. You don't write the plumbing. You write the agents and the tasks, and crewAI connects them.&lt;/p&gt;

&lt;p&gt;I chose it because I wanted to learn agentic patterns hands-on, and crewAI's abstractions are close enough to how you'd think about the problem naturally that the learning curve doesn't get in the way of actually building. A writer and a reviewer are intuitive roles. Defining them as crewAI agents felt like a natural translation of the mental model.&lt;/p&gt;

&lt;p&gt;What crewAI doesn't handle — and this is important — is your judgment about what good code means. The framework will run your loop faithfully. It won't tell you whether your review criteria are any good. That part you have to figure out yourself, and that's where most of the real work lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Writer Agent
&lt;/h2&gt;

&lt;p&gt;The writer agent is the simpler of the two — its job is straightforward. Given a task, produce code that accomplishes it.&lt;/p&gt;

&lt;p&gt;In crewAI terms, the writer looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Software Engineer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write clean, correct, production-ready code that solves the given task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are an experienced software engineer who writes clear,
    well-structured code. You follow established conventions, handle edge cases,
    and write code that other developers can read and maintain.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The task it receives includes the prompt — what to build — plus any context about the project: language, conventions, constraints. This is where the system from article one plugs directly in. If the repo has a &lt;code&gt;CLAUDE.md&lt;/code&gt;, that context gets passed to the writer before it generates a single line. If there's a code-writing Skill, that gets included too. The writer doesn't start cold — it starts knowing your project the same way it would in any other session.&lt;/p&gt;

&lt;p&gt;The first pass isn't expected to be perfect. It's expected to be a serious attempt — something substantive enough for the reviewer to actually evaluate. That framing matters. You're not asking for perfection on the first try. You're asking for something worth reviewing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Reviewer Agent
&lt;/h2&gt;

&lt;p&gt;The reviewer is where the real design decisions live.&lt;/p&gt;

&lt;p&gt;The naive approach is to have Claude re-read its own output and ask "is this good?" That doesn't work well. The model has too much context about what it was trying to do — it reads charitably, filling in gaps with intent rather than scrutinizing what's actually there.&lt;/p&gt;

&lt;p&gt;The fix is persona separation. The reviewer is defined as a different agent with a different goal — not "did this accomplish what was intended" but "what's wrong with this code."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior Code Reviewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Review code critically and objectively. Identify bugs, edge cases,
    convention violations, and anything that would fail in production.
    Do not give the benefit of the doubt.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a senior engineer with high standards and low tolerance
    for sloppy code. You&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ve seen too many production incidents caused by code
    that looked fine in review. You are thorough, specific, and direct.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backstory isn't decoration — it shapes behavior. A reviewer told to be skeptical and specific produces meaningfully different feedback than one told to be helpful. Same model, different instruction set, genuinely different output.&lt;/p&gt;

&lt;p&gt;The reviewer's task is to return one of two things: a pass with brief justification, or a fail with specific, actionable feedback. Vague feedback — "this could be improved" — is useless in a loop. The writer needs to know exactly what to fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Loop
&lt;/h2&gt;

&lt;p&gt;With the writer and reviewer defined, the loop itself is surprisingly clean in crewAI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;crew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;write_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;review_task&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sequential process means the writer runs first, hands off to the reviewer, and the reviewer's output determines what happens next. If the review passes, the loop exits and returns the code. If it fails, the feedback gets passed back to the writer as context for the next attempt.&lt;/p&gt;

&lt;p&gt;The retry limit is non-negotiable. Without it, a loop that keeps failing will keep running — and Claude API calls are not free. In practice, three iterations is the sweet spot. If the code hasn't passed review by the third attempt, something is wrong with either the task definition or the review criteria, and a human should look at it. The loop surfaces the best attempt and stops.&lt;/p&gt;

&lt;p&gt;What you learn quickly is that the loop is only as good as the reviewer's criteria. A reviewer that's too strict loops forever. A reviewer that's too lenient rubber-stamps bad code. Calibrating that bar — specific enough to catch real problems, reasonable enough to actually pass good code — is where most of the iteration happens when you're building this.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Building It By Hand Taught Me
&lt;/h2&gt;

&lt;p&gt;The honest answer is that the first version was too strict.&lt;/p&gt;

&lt;p&gt;The reviewer's criteria were thorough — maybe too thorough. It flagged missing docstrings on every function, objected to variable names it considered insufficiently descriptive, and refused to pass anything that didn't include explicit error handling regardless of whether the task called for it. Almost nothing passed on the first review. The loop ran to the retry limit constantly, burning tokens on debates about naming conventions instead of catching real bugs.&lt;/p&gt;

&lt;p&gt;The fix was humbling: I had to think harder about what "good code" actually means in context. A reviewer that holds a utility snippet to production application standards isn't useful — it's just expensive. Tightening the review criteria to focus on correctness and edge cases, and leaving style to the &lt;code&gt;CLAUDE.md&lt;/code&gt; conventions layer, is what made the loop productive rather than just busy.&lt;/p&gt;

&lt;p&gt;The second thing I learned is that verbose output is worth the cost while you're building. crewAI's &lt;code&gt;verbose=True&lt;/code&gt; flag logs every agent action, every task handoff, every decision point. It's noisy. It's also the only way to understand why the loop is behaving the way it is. I left it on far longer than I needed to, and I don't regret it. You can't debug a loop you can't see.&lt;/p&gt;

&lt;p&gt;The third thing — and this one surprised me — is that the reviewer persona genuinely changes the output. I expected the separation to be mostly cosmetic, a way of organizing prompts cleanly. It's not. A reviewer told to be skeptical and specific catches things that a neutral re-read misses. The backstory isn't decoration. It's instruction.&lt;/p&gt;

&lt;p&gt;The moment it clicked was a loop run where the reviewer caught an off-by-one error the writer had introduced in a retry — an error that wasn't in the first attempt, only appeared in the revision, and would have been invisible in a one-shot workflow. That's the whole point, in one example.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/southwestmogrown/code_genie" rel="noopener noreferrer"&gt;Code Genie&lt;/a&gt; is still early. The loop works, the output is measurably better than one-shot generation for the cases it's designed for, and the crewAI abstractions held up well enough that I'd use the framework again.&lt;/p&gt;

&lt;p&gt;But the review layer is still bespoke — written specifically for Code Genie, not portable to other projects. The next logical step is extracting it into a reusable Skill: a code review Skill that any project can plug into its own workflow without building the full loop from scratch. That's the next article.&lt;/p&gt;

&lt;p&gt;If you're thinking about building something similar, start with the reviewer, not the writer. The writer is easy — Claude already knows how to write code. The hard part is defining what "good enough" means precisely enough that a loop can act on it. Get that right first and the rest follows.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: crewai, agentic-ai, code-generation, ai-code-review, python&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Series: Building With AI Agents — Article 2 of 12&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crewai</category>
      <category>agents</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Made Claude Actually Understand My Codebase</title>
      <dc:creator>Shane Wilkey</dc:creator>
      <pubDate>Thu, 12 Mar 2026 08:06:18 +0000</pubDate>
      <link>https://forem.com/southwestmogrown/how-i-made-claude-actually-understand-my-codebase-436c</link>
      <guid>https://forem.com/southwestmogrown/how-i-made-claude-actually-understand-my-codebase-436c</guid>
      <description>&lt;p&gt;Most developers use Claude like a smart search engine — paste code, get answer, move on. That works until you realize how much you're paying for it. Token costs add up fast when Claude has to rediscover your codebase from scratch every single session. A &lt;code&gt;CLAUDE.md&lt;/code&gt; file and a &lt;code&gt;/skills&lt;/code&gt; folder fix that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you've been using Claude as a development tool, you've probably noticed a pattern. You open a new conversation, paste in some code, ask your question, get a decent answer, and close the tab. Next time you need help, you start over. Paste the code again. Re-explain the context again. Watch Claude make the same reasonable-sounding wrong assumptions about your stack again.&lt;/p&gt;

&lt;p&gt;That cycle has a cost — and I don't just mean the token bill, though that's real. Every time Claude starts cold, you're paying to re-establish context that should already be there. You're burning tokens on boilerplate explanation instead of actual problem solving. And the suggestions you get back are generic, because Claude is pattern-matching against codebases it's seen before — not yours.&lt;/p&gt;

&lt;p&gt;This isn't a Claude problem. It's a workflow problem. Claude has no memory between sessions by design. Without a system to feed it context, you'll keep getting capable-but-wrong answers no matter how good the model gets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter CLAUDE.md
&lt;/h2&gt;

&lt;p&gt;The first piece of the system is a file called &lt;code&gt;CLAUDE.md&lt;/code&gt;. It lives in the root of your repository, and Claude reads it automatically at the start of every session. No prompting required. No copy-pasting context into the chat. It's just there.&lt;/p&gt;

&lt;p&gt;Think of it as an onboarding document — except instead of writing it for a new team member, you're writing it for your AI collaborator. The goal is the same: get them up to speed on what this project is, how it's structured, and what the rules of the road are before they touch a single line of code.&lt;/p&gt;

&lt;p&gt;A good &lt;code&gt;CLAUDE.md&lt;/code&gt; answers the questions Claude would otherwise have to guess at. What's the stack? What's the architecture? What patterns do we follow here? What should never happen in this codebase? Where does business logic live? What does a good commit message look like?&lt;/p&gt;

&lt;p&gt;Without it, Claude fills those gaps with assumptions. With it, Claude walks into every session already knowing your project the way a senior dev would after a solid onboarding week.&lt;/p&gt;

&lt;p&gt;The investment is maybe an hour to write it the first time. The return is every future session starting from the right place instead of zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Goes In It
&lt;/h2&gt;

&lt;p&gt;There's no rigid spec for a &lt;code&gt;CLAUDE.md&lt;/code&gt; — but after building several of them across different projects, a few sections show up every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack and environment.&lt;/strong&gt; List your languages, frameworks, major libraries, and any tooling that isn't obvious from the file structure. Include versions where they matter. Claude should never have to guess whether you're on React 17 or 19, or whether you're using Prisma or raw SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture overview.&lt;/strong&gt; A short description of how the project is structured at a high level. Is it a monolith or microservices? Where does the frontend live relative to the backend? Are there multiple services that talk to each other? One paragraph is usually enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conventions.&lt;/strong&gt; This is where you save the most tokens over time. Naming conventions, file organization patterns, how you handle errors, how state is managed, what a component is expected to look like. The more explicit you are here, the less Claude improvises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anti-patterns.&lt;/strong&gt; Equally important — what should never happen. Don't split this component. Don't add a new dependency without flagging it. Don't touch this file. Explicit guardrails prevent a whole category of suggestions you'd just have to reject anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent instructions.&lt;/strong&gt; If you're using Claude as a coding agent assigned to GitHub issues, this section matters a lot. Tell it exactly what a completed issue looks like. Scope what it's allowed to do. My biggest lesson here: agents that do full file audits on every issue are expensive and slow. A single line in &lt;code&gt;CLAUDE.md&lt;/code&gt; that says "do not audit files unrelated to the current issue" cuts that waste immediately.&lt;/p&gt;

&lt;p&gt;Here's a minimal template to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Framework: Next.js 16, React 19
&lt;span class="p"&gt;-&lt;/span&gt; Language: TypeScript (strict)
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL via Prisma 7
&lt;span class="p"&gt;-&lt;/span&gt; Styling: Tailwind CSS v4
&lt;span class="p"&gt;-&lt;/span&gt; Testing: Vitest, Playwright
&lt;span class="p"&gt;-&lt;/span&gt; Package manager: pnpm

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
Monolithic Next.js App Router application. All DB routes are force-dynamic.
Business logic lives in /lib. Components in /components. API routes in /app/api.

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use named exports for components
&lt;span class="p"&gt;-&lt;/span&gt; Co-locate tests with the files they test
&lt;span class="p"&gt;-&lt;/span&gt; Error handling via Result pattern — no raw throws in business logic

&lt;span class="gu"&gt;## Anti-patterns&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Do not split single-file components unless explicitly asked
&lt;span class="p"&gt;-&lt;/span&gt; Do not install new dependencies without flagging them first
&lt;span class="p"&gt;-&lt;/span&gt; Do not modify /prisma/schema.prisma without explicit instruction

&lt;span class="gu"&gt;## Agent Instructions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Work only within the scope of the assigned issue
&lt;span class="p"&gt;-&lt;/span&gt; Do not audit files unrelated to the current task
&lt;span class="p"&gt;-&lt;/span&gt; A completed issue includes passing lint, typecheck, and tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adapt it to your project. The goal isn't completeness — it's giving Claude enough signal to stop guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Skill System
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; solves the context problem. But there's a second problem it doesn't address: repetitive procedures.&lt;/p&gt;

&lt;p&gt;Every project has tasks that follow the same pattern every time. Fixing a bug. Writing a README. Building a new component. Reviewing a pull request. Without any guidance, Claude approaches each of these from scratch — and the output varies based on how well you happened to phrase the prompt that day.&lt;/p&gt;

&lt;p&gt;Skills fix that.&lt;/p&gt;

&lt;p&gt;A Skill is a Markdown file that teaches Claude a specific, repeatable process. It lives in a &lt;code&gt;/skills&lt;/code&gt; folder in your repo, and you reference it when you need it — either in a prompt, or in an agent's instructions. Where &lt;code&gt;CLAUDE.md&lt;/code&gt; gives Claude permanent knowledge about your project, a Skill gives it a procedure to follow for a specific type of task.&lt;/p&gt;

&lt;p&gt;Here's what a bug fix Skill might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# SKILL: Bug Fix&lt;/span&gt;

&lt;span class="gu"&gt;## Process&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Read the issue description and identify the expected vs actual behavior
&lt;span class="p"&gt;2.&lt;/span&gt; Locate the relevant file(s) — do not audit unrelated files
&lt;span class="p"&gt;3.&lt;/span&gt; Identify the root cause before writing any code
&lt;span class="p"&gt;4.&lt;/span&gt; Write the minimal fix that resolves the issue without side effects
&lt;span class="p"&gt;5.&lt;/span&gt; Add or update tests to cover the fixed behavior
&lt;span class="p"&gt;6.&lt;/span&gt; Verify lint and typecheck pass before marking complete

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Do not refactor code outside the scope of the bug
&lt;span class="p"&gt;-&lt;/span&gt; Do not change function signatures unless the bug requires it
&lt;span class="p"&gt;-&lt;/span&gt; If the fix requires touching more than 3 files, flag it before proceeding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No magic. Just a documented process in a format Claude can read and follow consistently.&lt;/p&gt;

&lt;p&gt;The real power is that Skills are modular and reusable. Write a README Skill once and every project gets the same quality README. Write a component Skill and every new component follows your conventions without you having to re-explain them. Stack Skills together and you're not just giving Claude context — you're giving it a repeatable workflow.&lt;/p&gt;

&lt;p&gt;Here's where it gets interesting: Skills can also generate other parts of the system. I built a &lt;code&gt;CLAUDE.md&lt;/code&gt; generator Skill — you point it at a new project, answer a few questions, and it produces a complete, properly structured &lt;code&gt;CLAUDE.md&lt;/code&gt; ready to drop in. No remembering the syntax. No staring at a blank file. The link is in the closing section.&lt;/p&gt;

&lt;p&gt;And at the meta level, there's a skill-builder Skill — a Skill that helps you write new Skills. You describe what you want Claude to be able to do repeatedly, and it walks you through drafting, testing, and refining the Skill until it's solid. The system is fully composable. Once you're in it, you're building your own AI workflow toolkit.&lt;/p&gt;

&lt;p&gt;This is where the system starts to feel less like prompting and more like onboarding a reliable collaborator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It Together
&lt;/h2&gt;

&lt;p&gt;On their own, &lt;code&gt;CLAUDE.md&lt;/code&gt; and Skills are both useful. Together, they cover the two failure modes that make Claude frustrating: starting cold, and improvising the process.&lt;/p&gt;

&lt;p&gt;Here's the mental model: &lt;code&gt;CLAUDE.md&lt;/code&gt; is the onboarding. Skills are the SOPs.&lt;/p&gt;

&lt;p&gt;When a new employee joins a team, you don't just hand them a list of procedures and send them off. You first get them up to speed on what the company does, how it's structured, and what the culture is. Then you hand them the playbooks for the specific tasks they'll be doing. One without the other leaves gaps.&lt;/p&gt;

&lt;p&gt;Claude works the same way. A Skill without &lt;code&gt;CLAUDE.md&lt;/code&gt; means Claude knows how to fix a bug generically, but not how your project specifically handles errors, or what files are off limits. A &lt;code&gt;CLAUDE.md&lt;/code&gt; without Skills means Claude knows your codebase but still improvises the process every time.&lt;/p&gt;

&lt;p&gt;Together, every session starts with Claude already knowing your project — and every task starts with Claude already knowing how to approach it.&lt;/p&gt;

&lt;p&gt;In practice, my workflow looks like this. Each repo has a &lt;code&gt;CLAUDE.md&lt;/code&gt; in the root. A &lt;code&gt;/skills&lt;/code&gt; folder holds Skills for the recurring task types on that project. When I open a session or assign an issue to an agent, both are already there. I don't re-explain anything. I don't re-establish context. I just describe the problem and get to work.&lt;/p&gt;

&lt;p&gt;The token savings are real. The consistency is real. But the bigger shift is psychological — Claude stops feeling like a tool you have to wrangle and starts feeling like a collaborator who already knows the codebase.&lt;/p&gt;

&lt;p&gt;That's the whole system. And the best part is you can start with just the &lt;code&gt;CLAUDE.md&lt;/code&gt; today, in under an hour, and feel the difference immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start Here
&lt;/h2&gt;

&lt;p&gt;Most developers using Claude daily are paying — in tokens, in time, in frustration — for a workflow problem with a straightforward fix.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;CLAUDE.md&lt;/code&gt; in your current project root. The template above is a starting point. Or use the &lt;code&gt;CLAUDE.md&lt;/code&gt; generator Skill and let Claude build it from your answers. An imperfect one written today beats a perfect one you never get around to.&lt;/p&gt;

&lt;p&gt;Pick one recurring task and write a Skill for it. Bug fixing is the obvious first choice. README generation is another easy win. If you're stuck, the skill-builder Skill walks you through the process.&lt;/p&gt;

&lt;p&gt;The first session after this is set up is usually when it clicks.&lt;/p&gt;

&lt;p&gt;Both the &lt;code&gt;CLAUDE.md&lt;/code&gt; template and the generator Skill are available in my &lt;a href="https://github.com/southwestmogrown/ai-workflow-toolkit" rel="noopener noreferrer"&gt;ai-workflow-toolkit&lt;/a&gt;. Fork them, adapt them, make them yours. If you build something interesting on top of this system, I'd genuinely like to see it.&lt;/p&gt;

&lt;p&gt;This is the first article in a series on building with AI agents. Next up: I built an agentic loop that writes code, reviews it, and iterates on its own output — here's how Code Genie works and what it gets right that most AI coding tools get wrong.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: claude-md, claude, ai-coding-tools, developer-productivity, llm-context-management&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Series: Building With AI Agents — Article 1 of 12&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
