<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Codeaholicguy</title>
    <link>https://codeaholicguy.com</link>
    <atom:link href="https://codeaholicguy.com/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Notes on software engineering, leadership, AI coding agents, and the craft of building software.</description>
    <language>en-us</language>
    <lastBuildDate>Sun, 18 Oct 2026 16:58:24 GMT</lastBuildDate>
    <item>
      <title>AI Coding Agent session compaction with Jev</title>
      <link>https://codeaholicguy.com/2026/09/30/ai-coding-agent-session-compaction-with-jev/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/09/30/ai-coding-agent-session-compaction-with-jev/</guid>
      <pubDate>Wed, 30 Sep 2026 11:35:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <category>ai-devkit</category>
      <description>How AI DevKit compacts coding agent sessions with Jev typed decisions so manager, executor, and reviewer agents hand off state instead of raw transcripts.</description>
      <content:encoded><![CDATA[<p>Most of <a href="https://codeaholicguy.com/2026/08/19/how-i-run-ai-coding-agents-as-a-team-with-ai-devkit/">my coding agent work now runs through more than one agent</a>. A manager agent brainstorms with me, starts an executor, checks on it, and later brings in a reviewer. Sometimes I resume a session the next morning. Sometimes the manager is running on a VPS and I talk to it from Telegram.</p>
<p>Every one of those moves is a handoff. Each time, the same question comes up: what does the next agent actually need to know?</p>
<p>The obvious answer is the transcript. That doesn’t work for long. This post is about a small AI DevKit command that tries to answer the question better, and about Jev, the model behind it.</p>
<h2 id="the-problem-is-state-handoff-not-context-length"><a class="heading-link" href="#the-problem-is-state-handoff-not-context-length">The problem is state handoff, not context length</a></h2>
<p>Coding agent sessions get long quickly. An hour of implementation work can produce hundreds of messages: the original instruction, clarifications, file reads, test runs, failed attempts, retries, status updates, and the occasional “let me check that again”.</p>
<p>Somewhere in there are the facts that matter:</p>
<ul>
<li>what the user asked for, including the constraints they added halfway through,</li>
<li>which decisions were made, and why,</li>
<li>which files changed,</li>
<li>which commands ran and what they proved,</li>
<li>what is still blocked,</li>
<li>what the next step is.</li>
</ul>
<p>They are mixed with a lot of noise. The same <code>npm test</code> output appears four times. A path the agent tried and abandoned takes up twenty messages. Status chatter (“Now I’ll look at the service layer”) says nothing a later reader needs.</p>
<p>When I work with a single agent, this is mostly an annoyance. The harness compacts the context, some detail gets lost, and I repeat myself.</p>
<p>In orchestration it turns into a reliability problem. If the manager hands an executor’s state to a reviewer and the handoff drops a constraint, the reviewer approves the wrong thing. If a resumed session loses the validation evidence, the agent reruns work or, worse, claims something passed when nobody checked. A bigger context window doesn’t fix this. What fixes it is choosing the right state to pass on.</p>
<h2 id="jev-in-one-sentence"><a class="heading-link" href="#jev-in-one-sentence">Jev in one sentence</a></h2>
<p><a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev">Jev</a> is a model from TypeSafe that takes unstructured input and returns typed decisions, fast.</p>
<p>TypeSafe calls it a <em>System One Model</em> and sums up the idea as “unstructured state in, typed probabilistic decisions out”. You don’t ask Jev to write. You give it a piece of state and a set of typed questions: pick one of these categories, give me a probability for this yes/no. It answers in that shape, with a probability or confidence attached.</p>
<p>According to TypeSafe, that narrow focus buys a few things:</p>
<ul>
<li><strong>Type-safe answers.</strong> The output always matches the schema you asked for. TypeSafe goes further and says Jev “can’t hallucinate” for these queries, because the answer is constrained to the options you defined.</li>
<li><strong>Calibrated confidence.</strong> Every answer comes with a probability, and TypeSafe says higher confidence means higher accuracy.</li>
<li><strong>Latency.</strong> TypeSafe reports 70–500 ms end to end, and says that is 40 to 200x faster than frontier chat LLMs for “System One shaped” queries.</li>
</ul>
<p>I haven’t benchmarked these numbers carefully, so treat them as TypeSafe’s claims. What matters for this post is the shape: Jev isn’t competing with chat LLMs at writing code or explaining things. It does the small, frequent judgement calls that sit between those bigger steps.</p>
<h2 id="structured-output-isnt-new"><a class="heading-link" href="#structured-output-isnt-new">Structured output isn’t new</a></h2>
<p>I want to be careful here, because it is easy to present this as a brand-new idea. It isn’t.</p>
<p>Jev doesn’t introduce the idea of structured AI output. Engineers have been moving toward “AI as a structured-data engine” for a while, with JSON mode, tool calling, and schema-constrained decoding.</p>
<p>Last year I wrote <a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">The turning point in AI</a>. The first point in that post was that AI isn’t new. What changed was access, and with it, how engineers build.</p>
<p>The example I used was a weather app. The old way: an engineer writes the integration by hand. The early-AI way: the engineer asks an LLM to help write that same integration code. The shift I cared about was a third option: ask the model directly for the temperature, humidity, wind speed, and chance of rain for a location, in a structured format, and build the app around that output. In that post I put it as “Treat AI as an engine you can build around. Prompting becomes programming”.</p>
<p>Structured output was the important part then, and it still is. Once a model returns clean JSON instead of a paragraph, you can put it inside a program. You can branch on it, store it, test it, and pass it to the next step.</p>
<p>What Jev changes is the cost of each call and what the call returns. If a typed decision takes a few hundred milliseconds and comes back with a calibrated probability, you can afford to ask it many times inside a loop, not just once at the edge of a user interaction. Classifying every message in a session stops being an expensive batch job and becomes something you can simply do.</p>
<p>That’s the part I find exciting: an old idea that is now cheap enough to use in places where it used to be too slow.</p>
<h2 id="why-session-compact-needs-a-typed-decision-layer"><a class="heading-link" href="#why-session-compact-needs-a-typed-decision-layer">Why session compact needs a typed decision layer</a></h2>
<p>A normal summary keeps the story of a session. For continuing work, I don’t need the story. I need the operational facts.</p>
<p>So the first design question for session compact was: what should survive?</p>
<p><strong>Keep:</strong></p>
<ul>
<li>user instructions and constraints,</li>
<li>decisions,</li>
<li>code changes,</li>
<li>command evidence,</li>
<li>validation evidence,</li>
<li>blockers and open questions,</li>
<li>the next step,</li>
<li>facts worth proposing as long-term memory.</li>
</ul>
<p><strong>Drop:</strong></p>
<ul>
<li>routine status updates,</li>
<li>duplicated tool output,</li>
<li>abandoned exploration that led nowhere,</li>
<li>anything sensitive, such as tokens, keys, and credentials.</li>
</ul>
<p>Written that way, this is a classification problem, not a writing problem. For every event in the session I want answers to four questions:</p>
<ol>
<li>Which category is this?</li>
<li>How important is it for continuing the work?</li>
<li>Should it survive compaction?</li>
<li>Does it contain something that shouldn’t be stored?</li>
</ol>
<p>Each question has a small, fixed set of possible answers. That is exactly what Jev is built for. The categories AI DevKit uses are:</p>
<ul>
<li>user_instruction</li>
<li>decision</li>
<li>code_change</li>
<li>command_evidence</li>
<li>validation_evidence</li>
<li>blocker</li>
<li>next_step</li>
<li>memory_candidate</li>
<li>discard</li>
</ul>
<p>Once every event has typed labels, building the compact artifact doesn’t need another model call. Plain code can do it.</p>
<h2 id="how-ai-devkit-implements-it"><a class="heading-link" href="#how-ai-devkit-implements-it">How AI DevKit implements it</a></h2>
<p>The command lives under the existing agent session namespace:</p>
<pre><code class="hljs language-bash"><span class="hljs-comment"># Find the session you want to compact</span>

npx ai-devkit@latest agent sessions --all

<span class="hljs-comment"># Human-readable handoff</span>

npx ai-devkit@latest agent session compact --<span class="hljs-built_in">id</span> &#x3C;session-id> --format markdown

<span class="hljs-comment"># Machine-readable handoff for another agent or a script</span>

npx ai-devkit@latest agent session compact --<span class="hljs-built_in">id</span> &#x3C;session-id> --format json
</code></pre>
<p>Markdown is the default. If the same session ID exists for more than one provider, <code>--type</code> narrows the lookup (<code>claude</code>, <code>codex</code>, <code>gemini_cli</code>, <code>opencode</code>, <code>pi</code>, and others).</p>
<p>Here is the pipeline:</p>
<p><img src="/images/2026/09/screenshot-2026-09-23-at-18.43.46.png" alt="Session compact pipeline: session adapter, local redaction, Jev classification, retain filter, and builder to a resume prompt" decoding="async" width="1024" height="180"></p>
<h2 id="a-real-run-compacting-the-session-that-built-this-feature"><a class="heading-link" href="#a-real-run-compacting-the-session-that-built-this-feature">A real run: compacting the session that built this feature</a></h2>
<p>For a real example, I compacted the Codex session that built this feature. A manager agent handed the requirement to a Codex executor, and that executor took it through requirements, design, planning, implementation, tests, commit, rebase, and send PR. It ran for a few hours of wall-clock time.</p>
<p>The adapter returned 55 messages (9 user, 40 assistant, 6 system). Jev classified all of them sequentially in about 0.36s.</p>
<p>Here is how the size changed at each stage. Token counts use the <code>o200k_base</code> tokenizer, so treat them as estimates rather than any specific model’s exact count.</p>






























<table><thead><tr><th>Stage</th><th>File size</th><th>Tokens</th></tr></thead><tbody><tr><td>Raw Codex rollout file (<code>.jsonl</code>)</td><td>5.6 MB</td><td>~1.72M</td></tr><tr><td>Conversation the adapter gave Jev (55 messages)</td><td>96 KB</td><td>~21.6K</td></tr><tr><td><strong>Compact, Markdown</strong></td><td><strong>27 KB</strong></td><td><strong>~5.9K</strong></td></tr><tr><td>Compact, JSON</td><td>32 KB</td><td>~7.1K</td></tr></tbody></table>
<p>The raw file isn’t a fair baseline. Most of its 5.6 MB is event logs, tool calls and outputs, encrypted reasoning, and Codex’s own compaction records, and no model ever reads all of it at once. The numbers I care about are these two:</p>
<ul>
<li><strong>Against the conversation Jev saw: 21.6K → 5.9K tokens, about 73% smaller.</strong></li>
<li><strong>Against what Codex was actually holding in context at the end: 130.6K → 5.9K tokens, about 95% smaller.</strong> A fresh agent can start from roughly 6K tokens instead of inheriting a context that was half full of a 258K window.</li>
</ul>
<h2 id="why-this-matters-for-orchestration"><a class="heading-link" href="#why-this-matters-for-orchestration">Why this matters for orchestration</a></h2>
<p>Once agents start working together, communication is part of the product. Here is where compact fits in the loop I use every day:</p>
<p><img src="/images/2026/09/screenshot-2026-09-23-at-19.49.33.png" alt="Sequence: manager delegates to an executor, compacts its session, hands state to a reviewer, and reports verified status" decoding="async" loading="lazy" width="1024" height="520"></p>
<p>A few things change once handoffs carry compact state instead of transcripts:</p>
<ul>
<li><strong>The manager stays small.</strong> It doesn’t have to read every executor transcript to know where things stand. It reads intent, current state, evidence, and next step.</li>
<li><strong>Reviewers start from facts.</strong> They get the constraints and the validation evidence up front, instead of digging through the transcript for them.</li>
<li><strong>Resuming is cheaper.</strong> A stale session can restart from its resume prompt instead of rereading hours of noise.</li>
<li><strong>Memory gets better input.</strong> Memory candidates come out as a separate list, so a memory skill can review them instead of me remembering to extract them.</li>
</ul>
<p>A good handoff isn’t a longer summary. It’s the right state, chosen carefully. Session compact is a small primitive. Multi-agent setups become dependable through small primitives like this one.</p>
<h2 id="try-it"><a class="heading-link" href="#try-it">Try it</a></h2>
<p>If you’re building coding-agent workflows, and especially manager/executor/reviewer loops or long-running tasks that span sessions, try AI DevKit.</p>
<pre><code class="hljs language-bash"><span class="hljs-comment"># Set up AI DevKit</span>

npm i -g ai-devkit

ai-devkit setup

<span class="hljs-comment"># See the agents and sessions it can discover</span>

ai-devkit agent sessions --all

<span class="hljs-comment"># Get a Jev key from TypeSafe, then compact a session</span>

<span class="hljs-built_in">export</span> TYPESAFE_API_KEY=YOUR_API_KEY_HERE

ai-devkit agent session compact --<span class="hljs-built_in">id</span> &#x3C;session-id>
</code></pre>
<p>Run it on a long session you already have and compare the result with what you’d have written by hand for the next agent. If the compact misses something you needed, or keeps something you didn’t, I’d like to hear about it. That feedback is how the categories and filter will improve.</p>
<p>The code is on <a href="https://github.com/codeaholicguy/ai-devkit">GitHub</a>. If my sharing is helpful to you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>
<p><strong>Sources</strong></p>
<ul>
<li>TypeSafe, <a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev">Introducing System One Models and Jev</a>. Latency, hallucination, and calibration claims in this post come from this source.</li>
<li>Codeaholicguy, <a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">The turning point in AI</a>, May 2025.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>MCP vs Skills</title>
      <link>https://codeaholicguy.com/2026/09/09/mcp-vs-skills/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/09/09/mcp-vs-skills/</guid>
      <pubDate>Wed, 09 Sep 2026 11:30:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Will skills replace MCP? Why agent skills are the instruction layer and MCP is the integration layer, where the two overlap, and how they work together.</description>
      <content:encoded><![CDATA[<p>A common question that I still see flying around is that: will skills replace MCP?</p>
<p>I understand why people ask. Both can extend what an AI agent can do. A skill can include scripts that call APIs, while an MCP server can expose tools that call those same APIs. They can look like two ways of solving the same problem.</p>
<p>But I don’t think one is going to kill the other. They sit at different layers of the architecture, and they become more useful when we combine them.</p>
<h2 id="start-with-the-agent"><a class="heading-link" href="#start-with-the-agent">Start with the agent</a></h2>
<p>Before comparing MCP and skills, it helps to look at what an AI agent needs to do useful work. A model can reason about a request and generate a response. But reasoning alone is not enough, I also explained more detail in <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">Why does MCP matter?</a>.</p>
<p>The agent also needs to know:</p>
<ul>
<li>What process should I follow?</li>
<li>What information can I access?</li>
<li>What actions am I allowed to take?</li>
<li>How should I use those capabilities safely?</li>
</ul>
<p>Skills and MCP help answer different parts of those questions.</p>
<p><a href="/images/2026/09/screenshot-2026-09-08-at-16.30.30.png"><img src="/images/2026/09/screenshot-2026-09-08-at-16.30.30.png" alt="Diagram: a user request and a skill&#x27;s instructions feed the AI agent, which uses an MCP client and server to reach tools" decoding="async" width="864" height="888"></a></p>
<p>The simplest way I think about it is:</p>
<blockquote>
<p>A skill teaches the agent how to do something. MCP gives the agent a standard way to access something.</p>
</blockquote>
<p>That distinction is not perfect, but it is a useful place to start.</p>
<h2 id="a-skill-is-the-instruction-layer"><a class="heading-link" href="#a-skill-is-the-instruction-layer">A skill is the instruction layer</a></h2>
<p>An agent skill is usually a folder containing a <code>SKILL.md</code> file. That file describes what the skill does, when the agent should use it, and what process it should follow.</p>
<p>A skill can also include:</p>
<ul>
<li>Reference documents</li>
<li>Templates</li>
<li>Examples</li>
<li>Scripts</li>
<li>Other supporting assets</li>
</ul>
<p>For example, a spreadsheet skill might tell the agent how to inspect a workbook, preserve formulas, format the output, and verify the final result. It could also include scripts for manipulating spreadsheet files.</p>
<p>The important part is that the agent gains a method to do things.</p>
<p>Skills are also designed to load progressively. The agent can first see a skill’s name and description, then load its full instructions only when the task requires them. This lets an agent have access to many specialized workflows without putting every instruction into its context at once.</p>
<p>In many tools, skills live on the local filesystem. This makes them easy to inspect, edit, commit to Git, and customize for a project, and some time it fragmented between different tools. That also the reason why I built <a href="https://ai-devkit.com/docs/7-skills/">AI DevKit skills management</a>, so that we can manage skills consistently between different tools.</p>
<h2 id="mcp-is-the-integration-layer"><a class="heading-link" href="#mcp-is-the-integration-layer">MCP is the integration layer</a></h2>
<p>MCP, or Model Context Protocol, provides a standard way for an AI application to communicate with external tools and data sources.</p>
<p>Instead of every AI client building a custom integration for GitHub, Google Drive, Slack, or an internal database, it can connect to an MCP server. The server advertises the capabilities it supports and accepts structured requests from the client.</p>
<p><a href="/images/2026/09/screenshot-2026-09-08-at-16.38.05.png"><img src="/images/2026/09/screenshot-2026-09-08-at-16.38.05.png" alt="Diagram: an AI client uses a standard MCP contract to call an MCP server linked to GitHub, a database, and an internal API" decoding="async" loading="lazy" width="1023" height="523"></a></p>
<p>This separation matters from an engineering perspective.</p>
<p>The team operating the MCP server can update its implementation, fix bugs, change an internal API, or improve how it talks to the underlying service. Clients do not need to understand those internal changes, provided the MCP contract remains compatible.</p>
<p>MCP can also provide a more suitable boundary for concerns such as:</p>
<ul>
<li>Authentication and authorization</li>
<li>Input and output schemas</li>
<li>Centralized deployment</li>
<li>Logging and observability</li>
<li>Live data access</li>
<li>Shared capabilities across multiple agents</li>
</ul>
<p>MCP servers are not necessarily remote. MCP supports local communication between processes as well as communication with remote servers over HTTP. <a href="https://modelcontextprotocol.io/docs/2026-07-28/learn/architecture">MCP architecture overview</a>.</p>
<p>So the difference is not simply local versus remote. I personally believe it is closer to instructions versus integration.</p>
<h2 id="where-the-overlap-becomes-real"><a class="heading-link" href="#where-the-overlap-becomes-real">Where the overlap becomes real</a></h2>
<p>The boundary gets blurry when a skill includes executable scripts.</p>
<p>Imagine a skill containing a script that calls the GitHub CLI. If the user has already authenticated the CLI, the agent can use that script to open an issue. An MCP server could expose an <code>open_issue</code> tool that produces the same result.</p>
<p>In that specific case, a skill script and an MCP tool may appear interchangeable. For a local coding agent running on your laptop, they sometimes are. This is why the “skills replace MCP” argument is not completely unreasonable.</p>
<p>A skill can be faster to create and easier to distribute. If the agent already has filesystem and command-line access, adding an MCP server for one small operation may create more infrastructure than the task needs. But the tradeoff changes when the capability must be shared across many agents or users.</p>
<p>If you need centrally managed authentication, consistent permissions, production monitoring, live data, and an implementation that can be updated in one place, an MCP server becomes much more valuable.</p>
<h2 id="they-solve-different-parts-of-the-same-problem"><a class="heading-link" href="#they-solve-different-parts-of-the-same-problem">They solve different parts of the same problem</a></h2>
<p>Here is the comparison I find most useful:</p>








































<table><thead><tr><th>Aspect</th><th>Skill</th><th>MCP</th></tr></thead><tbody><tr><td>Main purpose</td><td>Teach the agent how to perform work</td><td>Connect the agent to tools and data</td></tr><tr><td>Typical contents</td><td>Instructions, references, templates, scripts</td><td>Tools, data, resources, schemas, and authorization</td></tr><tr><td>Common deployment</td><td>Local or bundled with the agent</td><td>Local process or remote server</td></tr><tr><td>How it changes</td><td>Update and redistribute the skill</td><td>Update the server behind a compatible contract</td></tr><tr><td>Best suited for</td><td>Repeatable workflows and specialized guidance</td><td>Live data, shared services, and external actions</td></tr><tr><td>Main dependency</td><td>Model reasoning and execution environment</td><td>Protocol support and server availability</td></tr></tbody></table>
<p>These differences also show why they fit together so naturally.</p>
<p>A skill might tell an agent how to investigate a customer complaint:</p>
<ol>
<li>Find the customer and confirm their identity.</li>
<li>Retrieve recent support tickets.</li>
<li>Check relevant transactions.</li>
<li>Compare the evidence.</li>
<li>Summarize the likely cause without exposing sensitive data.</li>
</ol>
<p>MCP servers could provide access to the support system, transaction database, and customer records.</p>
<p>The skill owns the workflow. MCP provides the connections.</p>
<p>Without the skill, the agent has tools but may not know the right process. Without MCP, the agent understands the process but may not have access to the information needed to complete it.</p>
<p>If my sharing is helpful to you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>How I run AI coding agents as a team with AI DevKit</title>
      <link>https://codeaholicguy.com/2026/08/19/how-i-run-ai-coding-agents-as-a-team-with-ai-devkit/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/08/19/how-i-run-ai-coding-agents-as-a-team-with-ai-devkit/</guid>
      <pubDate>Wed, 19 Aug 2026 11:45:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <category>ai-devkit</category>
      <description>How I run AI coding agents as a team with AI DevKit: a manager agent delegating to Codex and Claude Code executors, Telegram, skills, and shared memory.</description>
      <content:encoded><![CDATA[<p>I spend most of my time in the <a href="https://github.com/codeaholicguy/ai-devkit/">AI DevKit</a> agent console. I start one agent as the manager, usually Codex, and brainstorm with it. Once an idea becomes concrete enough to execute, I ask the manager to create another agent and hand off the work. That executor might run in Codex, Claude Code, Pi, or another harness depending on what I need.</p>
<p>The executor follows the dev lifecycle, implements the change, verifies it, and often creates the pull request without much intervention from me. I check progress with the manager instead of jumping between every agent session. When the pull request is ready, I review it, give feedback if needed, and merge it.</p>
<p>This is what lets me work on several things at the same time without turning my terminal into a wall of tabs.</p>
<p>I am not always at the keyboard either. When I step away, I connect the manager agent to Telegram and continue working with the same session from my phone.</p>
<p><a href="https://ai-devkit.com/">AI DevKit</a> gives me an agent manager, a shared operating model, and enough common context for different harnesses to work as one team.</p>
<blockquote>
<p>I'm running Claude Code, Codex, and Gemini CLI in parallel.</p>
<p>The worst part isn't the token cost. It's the tab switching.</p>
<p>AI DevKit's Agent Console fixes this. One view, all your agents, send messages without leaving, quick hotkey to focus when you need detail.</p>
<p>Works in iTerm2,… <a href="https://t.co/3VHg8LIUBa">pic.twitter.com/3VHg8LIUBa</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://x.com/codeaholicguy/status/2059975841602257065?ref_src=twsrc%5Etfw">May 28, 2026</a></p>
</blockquote>
<h2 id="the-agent-console"><a class="heading-link" href="#the-agent-console">The agent console</a></h2>
<p>The main interface for me is the <a href="https://ai-devkit.com/docs/13-agent-console/">AI DevKit agent console</a>.</p>
<p><a href="/images/2026/04/screenshot-2026-08-17-at-15.07.52-1.png"><img src="/images/2026/04/screenshot-2026-08-17-at-15.07.52-1.png" alt="AI DevKit agent console in tmux listing 15 agents with status and harness, and a preview of one Codex agent&#x27;s output" decoding="async" width="1023" height="621"></a></p>
<p>The console gives me one place to open, message, and monitor agents across different harnesses. I can see what is running, which agent is waiting, and where I need to step in.</p>
<p>I still use commands such as <a href="https://ai-devkit.com/docs/8-agent-management/"><code>agent list</code>, <code>agent detail</code>, and <code>agent send</code></a>, but mostly for debugging, automation, or inspecting something the console does not make obvious. They are the low-level controls, not the center of my workflow.</p>
<p>I did not want a collection of commands that made me a faster dispatcher. I wanted an operating environment where one agent could manage other agents while I stayed focused on the problem.</p>
<p><a href="/images/2026/08/image.png"><img src="/images/2026/08/image.png" alt="Diagram: via agent console or Telegram, I direct a Codex manager agent that delegates to Codex, Claude Code, Pi, and more" decoding="async" loading="lazy" width="1024" height="576"></a></p>
<p>The manager does more than route messages. It keeps track of the goal, the active workstreams, dependencies between agents, and anything waiting for a decision. It also checks whether an agent claiming completion has actually produced a verified result.</p>
<p>I also mentioned in <a href="https://codeaholicguy.com/2026/06/03/more-agents-same-human-brain/">the previous post</a> why I have so many executor types. Basically, I want to make full use of my subscriptions while also taking advantage of what each executor is good at and being able to run different models.</p>
<h2 id="the-manager-follows-me-to-telegram"><a class="heading-link" href="#the-manager-follows-me-to-telegram">The manager follows me to Telegram</a></h2>
<p>The agent console is my main interface when I am at the computer. When I am away from the keyboard, I use the <a href="https://ai-devkit.com/docs/12-channel/">AI DevKit channel connector</a>.</p>
<p>I select the manager agent in the console and connect it to my configured Telegram channel. The bridge runs in the background, so I can close the terminal view and keep talking to the manager from my phone. I also have the same AI DevKit stack running on a VPS so that I can chat with the agent manager 24/7.</p>
<blockquote>
<p>Your AI agent no longer has to sit inside your terminal.</p>
<p>Pi integration is now available in AI DevKit.</p>
<p>Start a Pi session in Agent Console, or remote control it from Telegram with one click. <a href="https://t.co/tt6M4nhR3O">https://t.co/tt6M4nhR3O</a> <a href="https://t.co/MRtHq4yhgz">pic.twitter.com/MRtHq4yhgz</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://x.com/codeaholicguy/status/2065893416748093867?ref_src=twsrc%5Etfw">June 13, 2026</a></p>
</blockquote>
<p>This is still the same manager session. Telegram is simply another transport into it. The manager keeps its working context, access to the filesystem and memory, and the ability to coordinate executor agents.</p>
<p>From Telegram, I can ask for progress, answer a question that is blocking an executor, give feedback, or ask the manager to start another piece of work. I do not need to wait until I am back at my desk to keep the orchestration loop moving.</p>
<p>The equivalent low-level command is:</p>
<pre><code class="hljs language-bash">ai-devkit channel start telegram \

--agent &#x3C;manager-name> \

--daemon
</code></pre>
<p>I normally connect it from the console. The command is useful when I am debugging the bridge or automating the setup.</p>
<h2 id="i-encode-the-workflow-in-skills"><a class="heading-link" href="#i-encode-the-workflow-in-skills">I encode the workflow in skills</a></h2>
<p>Anything I expect to repeat becomes a <a href="https://ai-devkit.com/skills/">skill</a>.</p>
<p>This is a large part of how I keep different harnesses consistent. Codex, Claude Code, OpenCode, Pi, or any different harnesses have different capabilities and session behavior, but they can load the same engineering process from the repository.</p>
<p>The skills I use heavily for this workflow are:</p>
<ul>
<li><code>agent-management</code> for starting, selecting, inspecting, and stopping agents safely</li>
<li><code>agent-communication</code> for sending assignments, reading recent context, and relaying results between agents</li>
<li><code>agent-orchestration</code> for the repeated supervision loop: check progress, unblock agents, coordinate dependencies, and verify completion</li>
<li><code>dev-lifecycle</code> for moving a change through requirements, design, planning, implementation, testing, and review</li>
<li><code>verify</code> for requiring fresh evidence before an agent says the work is done</li>
</ul>
<p>The manager knows how to manage because that behavior is encoded in skills. The executor knows how to take a requirement through to a pull request for the same reason.</p>
<p>This saves me from writing a giant orchestration prompt every morning and makes the process reusable. If I replace Codex with another harness tomorrow, I do not want to redesign how my team of agents works.</p>
<h2 id="the-filesystem-and-memory-are-the-shared-context"><a class="heading-link" href="#the-filesystem-and-memory-are-the-shared-context">The filesystem and memory are the shared context</a></h2>
<p>Delegation fails when the new agent starts with an empty head.</p>
<p>The filesystem solves part of this. Source code, <a href="https://ai-devkit.com/docs/3-development-with-ai-devkit/">requirements, design decisions, plans, test results, and implementation notes</a> live in files that every agent can read. An executor does not need the entire brainstorming transcript if the outcome has already been captured in a clear requirement and plan.</p>
<p>The filesystem is not enough, though. Some context does not belong in the codebase:</p>
<ul>
<li>a recurring repository convention that is easy to miss</li>
<li>a decision made during an earlier task and the reason behind it</li>
<li>a known failure pattern and the verified way to avoid it</li>
<li>a personal workflow preference that applies across projects</li>
</ul>
<p>I use <a href="https://ai-devkit.com/docs/6-memory/">AI DevKit memory</a> for this layer. It stores narrow, reusable knowledge in local SQLite. Agents search it when a task needs context instead of loading everything into every session.</p>
<p>Memory is the key layer that lets different harnesses start with the same understanding. Codex may have one conversation history and Claude Code another, but both can read the same files and retrieve the same durable knowledge.</p>
<p>I think of the context model like this:</p>
<ul>
<li>Filesystem: current state and explicit project artifacts</li>
<li>Memory: durable decisions, conventions, and verified lessons</li>
<li>Session: temporary reasoning and the conversation happening now</li>
</ul>
<p>Keeping these separate also prevents memory from becoming a dumping ground. I do not store raw conversations, temporary progress, or guesses. If a future agent is unlikely to reuse it, it does not belong in memory.</p>
<h2 id="my-actual-day-to-day-loop"><a class="heading-link" href="#my-actual-day-to-day-loop">My actual day-to-day loop</a></h2>
<h3 id="1-start-a-manager"><a class="heading-link" href="#1-start-a-manager">1. Start a manager</a></h3>
<p>I usually start a Codex agent as the manager and keep that session alive. It becomes my main interface for the work, even when the actual implementation happens elsewhere.</p>
<p>The manager has the agent-management, agent-communication, and agent-orchestration skills. Actually, in my setup, these skills are installed globally. This gives it the ability to create agents, inspect their state, send follow-up instructions, and supervise work across several passes.</p>
<p>I still own the outcome. The manager owns the coordination loop.</p>
<h3 id="2-brainstorm-before-creating-workers"><a class="heading-link" href="#2-brainstorm-before-creating-workers">2. Brainstorm before creating workers</a></h3>
<p>I use the manager as a thinking partner first. We explore the problem, challenge assumptions, compare approaches, and narrow the idea.</p>
<p>I do not spawn an executor for every thought. That would create activity, not progress. I wait until there is something worth handing off: a requirement to develop, an investigation to run, a design to review, or a plan ready to execute.</p>
<p>This is also where <a href="https://codeaholicguy.com/2025/01/04/why-ai-wont-replace-human-engineers-and-why-it-shouldnt/">human judgment</a> has the most leverage. Agents can execute a well-framed task remarkably well. They are much less useful when several of them are confidently solving the wrong problem in parallel.</p>
<h3 id="3-ask-the-manager-to-create-the-right-executor"><a class="heading-link" href="#3-ask-the-manager-to-create-the-right-executor">3. Ask the manager to create the right executor</a></h3>
<p>Once the direction is clear, I tell the manager what I want executed. Sometimes I name the harness. Other times I describe the demand and let the manager choose between Codex, Claude Code, Pi, or another available agent.</p>
<p>A typical instruction is closer to this than a long shell command:</p>
<blockquote>
<p>We have agreed on the requirement and approach. Create an executor agent in an isolated worktree, hand over the relevant context, and have it continue the dev lifecycle through implementation, testing, review, and PR creation. Monitor it and tell me when you need a decision or when the PR is ready.</p>
</blockquote>
<p>The handoff includes the objective, scope, relevant files, constraints, validation expectations, and the artifact the executor should produce. The shared filesystem and memory carry the rest of the context.</p>
<p>The manager also prevents two executors from editing the same files at the same time. Parallel work needs clean boundaries or separate worktrees. Otherwise, the time saved during implementation comes back as conflict resolution.</p>
<h3 id="4-let-the-manager-supervise"><a class="heading-link" href="#4-let-the-manager-supervise">4. Let the manager supervise</a></h3>
<p>After the handoff, I usually move on to another idea or another workstream.</p>
<blockquote>
<p>I had Claude Code and Codex working together like a real software team.</p>
<p>In the last video, I showed a simple setup: one Claude Code agent orchestrating other agents.</p>
<p>This time, I pushed it further.</p>
<p>I used ai-devkit agent orchestration to run a small “team”:<br>
– Leader: Sonnet… <a href="https://t.co/76PA3hvK7C">https://t.co/76PA3hvK7C</a> <a href="https://t.co/9lziHWn8cX">pic.twitter.com/9lziHWn8cX</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://x.com/codeaholicguy/status/2041855579396706559?ref_src=twsrc%5Etfw">April 8, 2026</a></p>
</blockquote>
<p>The manager runs the orchestration loop. It checks agent status, inspects anything waiting or stale, relays necessary information, and sends corrections without duplicating instructions. If one executor depends on another, the manager sequences the work.</p>
<p>I check in with the manager rather than opening every executor session:</p>
<blockquote>
<p>What is the current progress? Is anything blocked or waiting for me?</p>
</blockquote>
<p>Most updates do not need my involvement. I care about exceptions: a product decision, an unresolved conflict, repeated failures, a security-sensitive action, or a result that cannot be verified.</p>
<p>This is where agent orchestration becomes different from launching a few background processes. Someone, in this case the manager agent, owns the loop until the work is complete or genuinely blocked.</p>
<h3 id="5-step-into-the-executor-when-needed"><a class="heading-link" href="#5-step-into-the-executor-when-needed">5. Step into the executor when needed</a></h3>
<p>Even with a manager, I sometimes open the executor session from the console and get involved myself. This can easily be done by choosing the agent in the AI DevKit Agent Console and pressing “o”.</p>
<p>I usually do this when the agent is drifting, a technical trade-off needs a quick back-and-forth, or I want to steer the implementation before it goes too far. Direct interaction has more bandwidth than relaying every detail through the manager.</p>
<p>Once the direction is clear again, I leave the executor to continue and let the manager resume supervision. The manager remains my default coordination path, while direct access is always available when it is faster.</p>
<h3 id="6-review-the-pull-request"><a class="heading-link" href="#6-review-the-pull-request">6. Review the pull request</a></h3>
<p>For a well-prepared task, the executor can often complete the dev lifecycle and create the pull request on its own.</p>
<p>I still review every pull request as an engineer: does it solve the original problem, are the trade-offs acceptable, is the diff understandable, and is the verification strong enough?</p>
<p>If I have feedback, I give it to the manager. The manager can send the work back to the same executor or create another review pass. Once I am satisfied, I merge.</p>
<p>My role moves up a level. I spend more time on framing, judgment, and review, while agents handle more of the execution and coordination.</p>
<h2 id="how-i-choose-a-harness"><a class="heading-link" href="#how-i-choose-a-harness">How I choose a harness</a></h2>
<p>I do not assign permanent job titles to tools. Codex is not always the implementer, and Claude Code is not always the reviewer.</p>
<p>I choose based on the work, the context already available, the model that I want to use, and what I need from that session. I may also want a different harness for a fresh perspective. The choice can change as the tools change.</p>
<p>This is another reason the shared layer matters. The dev lifecycle, project memory, and task artifacts should survive a change in harness; I might stop the execution in the middle and continue the process with a different harness. Tool preference should not become workflow architecture.</p>
<h2 id="what-makes-the-workflow-scale"><a class="heading-link" href="#what-makes-the-workflow-scale">What makes the workflow scale</a></h2>
<p>The practical benefit is that I no longer need to personally carry every task from idea to pull request in one continuous block of attention.</p>
<p>While one executor implements a feature, another can investigate a production issue, and I can brainstorm the next idea with the manager. I return when a decision or review needs me.</p>
<p>There are still limits, such as parallel work increases review load and poorly framed tasks fail faster. A manager without strong skills becomes a message-forwarding bot.</p>
<p>A few principles keep the setup useful:</p>
<ul>
<li>One manager owns the coordination loop.</li>
<li>Executors receive bounded work and clear artifacts.</li>
<li>I can step into an executor session when direct steering is faster.</li>
<li>Skills carry the process across harnesses.</li>
<li>Files and memory carry shared context across sessions.</li>
<li>Worktrees or file boundaries isolate parallel changes.</li>
<li>Fresh verification is better than an agent’s completion message.</li>
<li>The human owns the final decision.</li>
</ul>
<p>The engineer remains responsible, but spends attention in different places.</p>
<h2 id="start-with-a-manager-and-one-executor"><a class="heading-link" href="#start-with-a-manager-and-one-executor">Start with a manager and one executor</a></h2>
<p>If you want to try this workflow, start small:</p>
<pre><code class="hljs language-bash">npx ai-devkit@latest setup

npx ai-devkit@latest agent console
</code></pre>
<p>Choose the harnesses you already trust. Start one manager and give it the agent orchestration skills. Brainstorm one real task, capture the result in files, and ask the manager to create one executor to carry it through the development lifecycle.</p>
<p>Then watch where the handoff loses context. Encode the repeated fixes in skills. Store durable decisions in memory. Add another executor only when the first loop is reliable.</p>
<p>If my sharing is helpful to you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>I read every Claude Code hook so you don’t have to</title>
      <link>https://codeaholicguy.com/2026/06/17/i-read-every-claude-code-hook-so-you-dont-have-to/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/06/17/i-read-every-claude-code-hook-so-you-dont-have-to/</guid>
      <pubDate>Wed, 17 Jun 2026 04:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>A practical guide to all 27 Claude Code hook events: how hooks work, which are worth using, like PreToolUse and SessionStart, and pitfalls to avoid.</description>
      <content:encoded><![CDATA[<p>When building <a href="https://github.com/codeaholicguy/ai-devkit">AI DevKit</a>, I had to look much deeper into different coding agent harnesses. Each one has its own structure, <a href="https://codeaholicguy.com/2026/04/11/how-my-ai-workflow-evolved-from-prompts-to-workflow/">workflow</a>, and mental model.</p>
<p>In this post, I’ll share what I learned from digging into Claude Code <a href="https://codeaholicguy.com/2026/01/31/ai-coding-agents-explained-rules-commands-skills-mcp-hooks/">hooks</a>.</p>
<p><a href="https://codeaholicguy.com/2026/05/20/claude-code-guide-how-to-use-claude-code-like-youve-used-it-for-a-year/">Claude Code</a> has 27 hook events. I went through all of them.</p>
<p>Most are not worth your time, a handful are useful, one or two can change how you use the tool.</p>
<p>The important thing to note is that hooks let you stop relying on the model to remember your rules. Instead, you can put those rules around the agent.</p>
<h2 id="how-hooks-work"><a class="heading-link" href="#how-hooks-work">How hooks work</a></h2>
<p>You put something like this in <code>.claude/settings.json</code>:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>

<span class="hljs-attr">"hooks"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>

<span class="hljs-attr">"PreToolUse"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>

<span class="hljs-punctuation">{</span>

<span class="hljs-attr">"matcher"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Bash"</span><span class="hljs-punctuation">,</span>

<span class="hljs-attr">"hooks"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>

<span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"command"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"command"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"/path/to/hook.sh"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"timeout"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">30</span> <span class="hljs-punctuation">}</span>

<span class="hljs-punctuation">]</span>

<span class="hljs-punctuation">}</span>

<span class="hljs-punctuation">]</span>

<span class="hljs-punctuation">}</span>

<span class="hljs-punctuation">}</span>
</code></pre>
<p>When the event fires, Claude Code runs your command, pipes JSON to stdin, and reads JSON back on stdout.</p>
<p>The input includes the event name, session id, transcript path, cwd, and event-specific fields.</p>
<p>Your stdout decides what happens next. You can abort with continue: false, deny the tool with <code>decision: "block"</code>, or inject text into the next model turn with <code>hookSpecificOutput.additionalContext</code>.</p>
<p>Exit 0 with empty stdout is a no-op. Non-zero exits show up in the transcript as a hook error.</p>
<p>That’s it.</p>
<h2 id="what-i-actually-use"><a class="heading-link" href="#what-i-actually-use">What I actually use</a></h2>
<h3 id="pretooluse"><a class="heading-link" href="#pretooluse">PreToolUse</a></h3>
<p>Fires before every tool call. It can approve, block, or rewrite the input via <code>updatedInput</code>.</p>
<p>This is the one that matters most, because rewriting input is different from just observing. You can strip secrets out of commands before they run. You can refuse to run <code>terraform apply</code> outside an allowed directory.</p>
<p>A few examples:</p>
<ul>
<li>Regex scan for credentials or <code>.env</code> paths in Bash commands, then block with a reason</li>
<li>Auto-redact tokens by rewriting the command instead of blocking it</li>
<li>Hard-deny <code>DELETE</code> or <code>DROP TABLE</code></li>
<li>If the current branch is <code>main</code> and the command is <code>git push</code>, block</li>
</ul>
<h3 id="userpromptsubmit"><a class="heading-link" href="#userpromptsubmit">UserPromptSubmit</a></h3>
<p>Fires when you hit enter. It returns <code>additionalContext</code>, which gets injected as a system message for the next turn.</p>
<p>This is basically a dynamic version of <code>CLAUDE.md</code>. Per-turn, conditional on whatever your script can compute.</p>
<p>Some ideas that work:</p>
<ul>
<li>Compliance reminders when the prompt mentions “user data” or “PII”</li>
<li>A quick grep over the prompt that injects “relevant files: A, B, C”</li>
<li>Refuse to submit if today’s API spend has crossed a threshold</li>
</ul>
<h3 id="posttooluse"><a class="heading-link" href="#posttooluse">PostToolUse</a></h3>
<p>Fires after a tool call succeeds. It can return <code>additionalContext</code> for the next turn, or <code>updatedMCPToolOutput</code> to rewrite what the model sees.</p>
<p>This is a good place to enforce “always run test after an Edit”.</p>
<p>Small thing, but it removes a recurring class of stale-context bugs.</p>
<p>It is also useful for redacting secrets from Bash output before the model sees them. The model does not need to know your access tokens to keep working.</p>
<h3 id="sessionstart"><a class="heading-link" href="#sessionstart">SessionStart</a></h3>
<p>Fires on startup, resume, clear, and after compact. It can return <code>additionalContext</code>, <code>initialUserMessage</code>, or <code>watchPaths</code>.</p>
<p>The <code>source</code> field tells you what kind of start it is, so you can do different things on resume vs fresh boot.</p>
<p><code>watchPaths</code> is the underrated part. It lets you register paths outside cwd so <code>FileChanged</code> fires for them. That is handy when a shared config repo lives somewhere else.</p>
<h3 id="permissionrequest"><a class="heading-link" href="#permissionrequest">PermissionRequest</a></h3>
<p>Fires when a tool call is about to ask you for permission. It can allow, deny, rewrite the input, or update persistent permissions.</p>
<p>This gets me off the “yolo allow everything” reflex without going full <code>bypassPermissions</code>.</p>
<p>Auto-allow <code>ls</code>, <code>cat</code>, <code>grep</code>, <code>git status</code>, etc. Defer everything else to the UI. In work repos, deny anything that writes outside the repo. In scratch repos, allow more.</p>
<p>The value is encoding the policy in code instead of clicking through the same prompts forever.</p>
<h2 id="useful-in-specific-situations"><a class="heading-link" href="#useful-in-specific-situations">Useful in specific situations</a></h2>
<p>These are not always wired up for me, but I reach for them when the situation calls.</p>





































<table><thead><tr><th>Hook</th><th>Best for</th></tr></thead><tbody><tr><td><code>PostToolUseFailure</code></td><td>Auto-retry transient failures; inject diagnostics like “Bash failed because port 3000 is taken, here’s the PID”</td></tr><tr><td><code>PermissionDenied</code></td><td>Tell the model why denial happened via <code>additionalContext</code>, or set <code>retry: true</code> with a hint</td></tr><tr><td><code>Stop</code></td><td>“Are you really done?” loops. Inject a checklist and let the model decide if it should keep going</td></tr><tr><td><code>PreCompact</code> / <code>PostCompact</code></td><td>Pin facts across compaction. Probably the biggest quality win for long sessions</td></tr><tr><td><code>SubagentStop</code></td><td>Aggregate subagent results into the parent context with structure</td></tr><tr><td><code>InstructionsLoaded</code></td><td>Debug “where did this rule come from?” The <code>load_reason</code> field tells you whether it came from session-start, nested-traversal, glob-match, include, or compact-restore</td></tr><tr><td><code>Notification</code></td><td>Route Claude’s idle or awaiting-input alerts through notification, Slack or different channels</td></tr></tbody></table>
<p><code>PreCompact</code> and <code>PostCompact</code> deserve a callout.</p>
<p>Long sessions get worse over time because compaction throws away things you wanted to keep. Pinning the current task and recent decisions across compaction is a small change, but it makes a visible difference if you do a lot of long-running work.</p>
<h2 id="the-rest"><a class="heading-link" href="#the-rest">The rest</a></h2>
<p>These exist. Most people will never wire them.</p>
<ul>
<li><code>StopFailure</code>, audit-only, for when stop handling itself crashes</li>
<li><code>SubagentStart</code>, rarely useful alone, but pairs with <code>SubagentStop</code></li>
<li><code>Setup</code>, fires on <code>/init</code> and maintenance. Useful if you ship <code>/init</code> templates</li>
<li><code>SessionEnd</code>, runs outside the REPL, cannot block exit, fails silently. Do not put critical cleanup here</li>
<li><code>CwdChanged</code>, <code>FileChanged</code>, building blocks for “watch this thing” workflows, mostly paired with <code>SessionStart.watchPaths</code></li>
<li><code>WorktreeCreate</code>, <code>WorktreeRemove</code>, only fire for the worktree feature. The <code>worktreePath</code> response lets you override where worktrees get created</li>
<li><code>ConfigChange</code>, fires when settings files change on disk. Audit-only in practice</li>
<li><code>Elicitation</code>, <code>ElicitationResult</code>, MCP-only, for servers that request structured forms. Useful if you have a server that asks the same question every time</li>
<li><code>TeammateIdle</code>, <code>TaskCreated</code>, <code>TaskCompleted</code>, only relevant if you use Claude Code’s team features</li>
</ul>
<h2 id="things-that-bit-me"><a class="heading-link" href="#things-that-bit-me">Things that bit me</a></h2>
<p><code>hookSpecificOutput</code> is where the structured fields go. Older examples, and a lot of posts still floating around, put <code>additionalContext</code>, <code>updatedInput</code>, and <code>watchPaths</code> at the top level of the response. The schema moved. If your hook is not doing anything, check that first. Print the actual stdout and look at the shape.</p>
<p>Every <code>PreToolUse</code> adds latency to every tool call. Spawn cost on macOS is around 10 to 50ms. In a turn that makes 30 tool calls, that adds up. For logging hooks where you do not care about the response, set <code>async: true</code> and an <code>asyncTimeout</code> so the tool does not wait.</p>
<p>Hook commands run with your full shell. A project’s <code>.claude/settings.json</code> can register a <code>SessionStart</code> hook. Claude Code prompts you before trusting new project hooks, which is good. But it is still worth reading the hook before saying yes. If you ever clone an untrusted repo, look at its <code>.claude/</code> before opening it.</p>
<p>If my sharing resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>A practical roadmap to learn agentic engineering</title>
      <link>https://codeaholicguy.com/2026/06/10/a-practical-roadmap-to-learn-agentic-engineering/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/06/10/a-practical-roadmap-to-learn-agentic-engineering/</guid>
      <pubDate>Wed, 10 Jun 2026 05:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>A staged roadmap to learn agentic engineering: master one coding agent, context engineering, verification, and workflows before MCP and multi-agent work.</description>
      <content:encoded><![CDATA[<p>A lot of engineers want to learn agentic engineering now. I think many of them are starting from the wrong end.</p>
<p>They jump straight into <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">MCP</a>, <a href="https://codeaholicguy.com/2026/06/03/more-agents-same-human-brain/">multi-agent systems</a>, <a href="https://codeaholicguy.com/2026/04/11/how-my-ai-workflow-evolved-from-prompts-to-workflow/">autonomous workflows</a>, and every shiny demo on X. It looks exciting and also feels like the future. But for most engineers, that is not the right place to start.</p>
<p>The real path is much less glamorous.</p>
<p>You do not become good at agentic engineering by collecting tools. You become good at it by building the right skills in the right order.</p>
<p>Today, the tools are clearly moving in an agentic direction. Coding tools are no longer only giving autocomplete. They can now read codebases, edit files, run commands, create plans, work on branches, and connect to external tools. But the important part is not the tool itself. The important part is what kind of engineer you need to use these tools well.</p>
<p>To me, agentic engineering means doing software engineering with AI agents embedded across the workflow, not just for writing code, but also for understanding requirements, reviewing designs, planning work, implementing changes, testing, and connecting to tools and systems.</p>
<h2 id="why-many-engineers-are-learning-this-backward"><a class="heading-link" href="#why-many-engineers-are-learning-this-backward">Why many engineers are learning this backward</a></h2>
<p>A lot of people are trying to start from the advanced layer first.</p>
<p>They want to build agent swarms, wire up MCP servers, or run multiple coding agents in parallel before they can even manage one agent well. I understand why.</p>
<p>But in practice, most engineers still struggle with the basics. They do not give enough context. They do not define constraints clearly. They do not review generated code carefully. They trust output that only looks right. Then, when the result is messy, they blame the tool.</p>
<p>I think this is the wrong mental model.</p>
<p>Agentic engineering is still engineering. The tools changed, the responsibility did not. If you cannot guide one agent through a small feature with good context, good review, and good verification, adding more agents will usually create more noise, not more leverage.</p>
<h2 id="what-agentic-engineering-actually-means"><a class="heading-link" href="#what-agentic-engineering-actually-means">What agentic engineering actually means</a></h2>
<p>People use this term more and more now, so it is worth making it simple.</p>
<p><a href="https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/">Agentic engineering</a> is not just asking AI to write a function.</p>
<blockquote>
<p>It is using AI agents across every phases of the software engineering workflow. That means an agent can help you understand requirements, review gaps, propose a design, turn the design into a plan, implement changes, generate tests, inspect the codebase, and connect to other tools or systems when needed.</p>
</blockquote>
<p>That does not mean it replaces engineers. It means engineers now need to learn how to work with a system that can do more than autocomplete, and treat it like your co-workers.</p>
<h2 id="stage-1-learn-to-work-well-with-one-coding-agent"><a class="heading-link" href="#stage-1-learn-to-work-well-with-one-coding-agent">Stage 1: learn to work well with one coding agent</a></h2>
<p>This is where most engineers should start.</p>
<p>Pick one tool. It can be <a href="https://codeaholicguy.com/2025/04/12/what-i-learned-using-cursorai-every-day-as-an-engineer/">Cursor</a>, <a href="https://codeaholicguy.com/2026/05/20/claude-code-guide-how-to-use-claude-code-like-youve-used-it-for-a-year/">Claude Code</a>, <a href="https://openai.com/codex/">Codex</a>, <a href="https://github.com/features/copilot/cli">Copilot</a>, or whatever you already have access to. The point is not to chase the best tool immediately. The point is to build the habit of working well with one agent.</p>
<p>At this stage, focus on small and contained tasks.</p>
<p>Ask the agent to explain a piece of code you do not understand. Ask it to suggest a small refactor. Ask it to generate a unit test. Ask it to debug an error message. Ask it to implement a small helper function.</p>
<p>What you are really learning here is not prompting tricks. You are learning clarity.</p>
<ul>
<li>Can you describe the task in plain language?</li>
<li>Can you break a problem into smaller pieces?</li>
<li>Can you tell the agent what success looks like?</li>
<li>Can you spot when the output is wrong or too shallow?</li>
</ul>
<p>Fast output is not the same as good <a href="https://codeaholicguy.com/2025/11/29/the-illusion-of-output/">output</a>. If you are early in your career, this stage is extremely valuable because it forces you to read code more carefully. It also shows you very quickly where your own fundamentals are weak.</p>
<p>Do not rush past this stage.</p>
<p>If you cannot manage one agent well, you are not ready for five.</p>
<h2 id="stage-2-learn-context-engineering"><a class="heading-link" href="#stage-2-learn-context-engineering">Stage 2: learn context engineering</a></h2>
<p>This is where the quality difference starts to show.</p>
<p>Most <a href="https://codeaholicguy.com/2026/02/14/tokens-context-windows-and-why-your-ai-agent-feels-stupid-sometimes/">bad AI output is not caused by a weak model</a>. It is caused by weak context. When engineers say, “AI is inconsistent” a lot of the time what they really mean is, “I gave it a vague task and hoped it would figure out the rest”.</p>
<p>Context engineering sounds fancy, but the core idea is simple: give the agent the information it actually needs to do the job well.</p>
<p>That usually means:</p>
<ul>
<li>what problem you are solving</li>
<li>what constraints matter</li>
<li>what the expected output looks like</li>
<li>what patterns already exist in the codebase</li>
<li>what edge cases matter</li>
<li>what should not change</li>
</ul>
<p>If the task is ambiguous, say so. If you are making assumptions, list them. If there are trade-offs, ask the agent to call them out before it writes code.</p>
<p>A lot of people think prompting is the key skill. I think context is the deeper one. A good engineer with average prompts but strong context will often get much better output than someone with clever prompts and weak problem framing.</p>
<h2 id="stage-3-learn-review-verification-and-testing-discipline"><a class="heading-link" href="#stage-3-learn-review-verification-and-testing-discipline">Stage 3: learn review, verification, and testing discipline</a></h2>
<p>This is one of the most important stages. AI makes it much easier to produce code quickly. That means the bottleneck is no longer typing speed. The bottleneck becomes judgment.</p>
<ul>
<li>Can you review the code properly?</li>
<li>Can you tell if the abstraction is wrong?</li>
<li>Can you see whether the agent handled edge cases, failure modes, backward compatibility, or long-term maintainability?</li>
</ul>
<p>This is why I keep saying agentic engineering still sits on top of engineering fundamentals.</p>
<p>You still need to understand systems. You still need to reason about trade-offs. You still need to care about testing and quality. In some ways, these skills matter even more now because the volume of output is higher.</p>
<p>At this stage, build strict habits:</p>
<ul>
<li>review generated code line by line when the change matters</li>
<li>ask what could break</li>
<li>generate tests, then improve them</li>
<li>compare the output against the original requirement</li>
<li>run the code and verify behavior</li>
<li>do not trust “should work”</li>
</ul>
<p>One of the easiest traps with AI coding tools is that they often produce something that looks complete. The UI renders, tests pass, code seems reasonable. But software engineering is full of cases where something works and is still the wrong solution.</p>
<p>You need the discipline to catch that.</p>
<h2 id="stage-4-build-repeatable-workflows"><a class="heading-link" href="#stage-4-build-repeatable-workflows">Stage 4: build repeatable workflows</a></h2>
<p>Once you are comfortable with one agent, better context, and stronger verification, the next step is to stop working randomly.</p>
<p>This is where you move from scattered prompting to a <a href="https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/">repeatable workflow</a>.</p>
<p>For me, the workflow still looks very familiar: <a href="https://ownthecraftbook.com/chapters/9-the-new-engineering-workflow/">requirement -> assumptions -> design -> plan -> implement -> review -> test -> verify</a></p>
<p>AI does not remove these steps. It just helps execute them <a href="https://codeaholicguy.com/2025/03/13/how-much-faster-can-ai-actually-make-your-team/">faster</a>.</p>
<p>That is a very important mindset shift. If your workflow is weak without AI, adding AI usually just helps you move faster in a messy direction. If your workflow is clear, AI can remove a lot of friction.</p>
<p>At this stage, start turning your habits into something reusable.</p>
<p>It can be a markdown checklist. It can be a set of reusable prompts. It can be command shortcuts inside your editor. It does not need to be fancy.</p>
<p>The goal is consistency. When you have a repeatable workflow, the quality of your AI-assisted work becomes much more stable. You are no longer relying on memory or improvisation every time.</p>
<h2 id="stage-5-connect-agents-to-tools-and-real-systems"><a class="heading-link" href="#stage-5-connect-agents-to-tools-and-real-systems">Stage 5: connect agents to tools and real systems</a></h2>
<p>Now we start getting closer to what people usually imagine when they hear the word “agentic”.</p>
<p>At this stage, the agent is not only responding to the text you paste into chat. It can access files, run commands, inspect code, search documentation, call APIs, or pull information from other systems.</p>
<p>This matters because real software engineering does not happen in one text box.</p>
<p>Requirements live in docs, tickets live in Jira, designs live in Figma, code lives in repositories, logs live somewhere else. Part of becoming good at agentic engineering is learning how to let agents work with this wider environment safely and usefully.</p>
<p>But notice the order. Tool use becomes powerful only after you already know how to frame work, review output, and maintain a good workflow. Otherwise, you are just giving a confused agent more ways to be confidently wrong.</p>
<p>This is the bridge from AI-assisted coding to real workflow-level leverage.</p>
<h2 id="stage-6-learn-mcp-when-it-solves-a-real-problem"><a class="heading-link" href="#stage-6-learn-mcp-when-it-solves-a-real-problem">Stage 6: learn MCP when it solves a real problem</a></h2>
<p>A lot of engineers want to start here because MCP is one of the hottest topics right now. I would not.</p>
<p><a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a> matters, but it makes more sense after you already understand why tool integration matters in your workflow.</p>
<p>In simple terms, MCP is a standard way for AI systems to connect to tools and data sources. That is useful because once you have many integrations, ad hoc connections get messy very quickly.</p>
<p>So yes, MCP is important.</p>
<p>But no, it should not be the first thing to learn. If you do not already have a clear use case, MCP will feel like extra abstraction. Learn it when you can answer a more important question first: what real friction am I removing by connecting this agent to this tool?</p>
<p>Sometimes the right answer is MCP. Sometimes a CLI is enough. Sometimes a simple manual step is still the better trade-off.</p>
<p>Use the right level of complexity for the problem you actually have.</p>
<h2 id="stage-7-only-then-explore-multi-agent-workflows"><a class="heading-link" href="#stage-7-only-then-explore-multi-agent-workflows">Stage 7: only then explore multi-agent workflows</a></h2>
<p>This is the advanced stage.</p>
<p>Now you can start experimenting with workflows where different agents take on different parts of the work.</p>
<p>For example:</p>
<ul>
<li>one agent reads the codebase and summarizes the current state</li>
<li>one agent proposes a plan</li>
<li>one agent implements</li>
<li>one agent reviews or looks for risks</li>
<li>one agent explores an alternative approach</li>
</ul>
<p>This can be powerful, but it can also get chaotic very quickly.</p>
<p>Because the moment you run multiple agents in parallel, a new bottleneck appears. The bottleneck is not coding anymore. It is <a href="https://codeaholicguy.com/2026/06/03/more-agents-same-human-brain/">context recovery, attention management, and judgment</a>. Suddenly, the work is not limited by how fast code can be generated. It is limited by how quickly you can understand what each agent is doing and make a good call.</p>
<p>That is why I see multi-agent workflows as stage seven, not stage one. They become useful only after you already know how to manage smaller loops well.</p>
<h2 id="what-i-would-focus-on-first-if-i-were-junior-or-mid-level-today"><a class="heading-link" href="#what-i-would-focus-on-first-if-i-were-junior-or-mid-level-today">What I would focus on first if I were junior or mid-level today</a></h2>
<p>If I were early or mid-career and wanted to build agentic engineering skills, I would keep it very simple.</p>
<p>First, I would get good at one coding agent. Not ten. Not the most advanced stack. Just one. I would use it every day for small tasks until I learned how to guide it well and where it tends to fail.</p>
<p>Second, I would get much better at writing context.</p>
<p>I would practice turning vague tasks into clear problem statements with constraints, edge cases, expected outputs, and assumptions. This skill is useful even without AI, which is one reason I think it is such a strong investment.</p>
<p>Third, I would strengthen my review and testing habits.</p>
<p>I would treat AI output like code from a fast junior engineer: useful, often impressive, but still needing review, correction, and verification.</p>
<p>Fourth, I would build one repeatable workflow for myself.</p>
<p>It does not need to be perfect. It just needs to be consistent enough that I stop relying on random prompting every time.</p>
<p>Fifth, I would delay advanced orchestration.</p>
<p>I would not spend most of my time on multi-agent setups, complex MCP architecture, or fancy demos until I had a strong grasp of the basics.</p>
<p>That path may look slower, but I think it gets you useful faster.</p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>I think agentic engineering will become a normal part of software engineering.</p>
<p>Not because every engineer will run a swarm of autonomous agents, but because the default workflow is already changing. AI tools are becoming more capable. They can read more, do more, and act more.</p>
<p>But the right response is not to chase everything new. The right response is to build the right skills.</p>
<p>Learn to work well with one agent. Learn to build context. Learn to review and verify. Learn to design repeatable workflows. Then learn how to delegate more.</p>
<p>That is how I think most engineers should learn agentic engineering.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>; it’s free. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>More agents, same human brain</title>
      <link>https://codeaholicguy.com/2026/06/03/more-agents-same-human-brain/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/06/03/more-agents-same-human-brain/</guid>
      <pubDate>Wed, 03 Jun 2026 05:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <category>ai-devkit</category>
      <description>Running Claude Code, Codex, and Gemini CLI in parallel showed me the bottleneck is human attention, plus my agent orchestration experiments.</description>
      <content:encoded><![CDATA[<p>I have been <a href="https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/">running multiple coding agents at the same time</a> for a while now.</p>
<p>Sometimes I have Claude Code working on one feature, Codex reviewing a plan, Gemini CLI exploring another direction, and another Claude session fixing a smaller issue in parallel. On paper, this sounds like the future of software engineering. One engineer, multiple agents, multiple streams of execution are happening at the same time.</p>
<p>And honestly, it is exciting.</p>
<p>For the first few minutes, it feels like I suddenly have a small engineering team around me. One agent can build, one agent can review, one agent can explore an alternative, one agent can check if the plan makes sense. Instead of waiting for one task to finish, I can start several pieces of work and let them run.</p>
<p>But after doing this for long enough, I realized the hard part is not starting more agents.</p>
<p>The hard part is keeping up with them.</p>
<h2 id="the-old-productivity-equation-is-changing"><a class="heading-link" href="#the-old-productivity-equation-is-changing">The old productivity equation is changing</a></h2>
<p>In the past, engineering capacity mostly scaled with people.</p>
<p>If we wanted to do more work, we hired more engineers. That gave us more hands, more thinking capacity, more reviews, more parallel execution, and more ownership. Of course, it also added communication overhead, meetings, planning complexity, alignment cost, and all the usual coordination problems that come with growing a team.</p>
<p>But the basic model was simple: more people meant more capacity.</p>
<p><a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">AI changes that model</a>.</p>
<p>With coding agents, one engineer can now start multiple streams of work at the same time. The amount of work we can attempt is no longer limited only by the number of engineers in the team. It also depends on how many agents we can safely direct, review, and coordinate. This is a big thing.</p>
<p>It does not mean headcount no longer matters. That is too simplistic. <a href="https://codeaholicguy.com/2025/01/04/why-ai-wont-replace-human-engineers-and-why-it-shouldnt/">Great engineers still matter a lot</a> because judgment, system understanding, product context, and ownership still sit with humans.</p>
<p>But the scaling model is changing.</p>
<p>In the future, the question may not only be “how many engineers do we have?” It may also be “how many agent workflows can these engineers safely orchestrate?”</p>
<p>The word “safely” matters because <strong>generating more code is not the same as creating more good software</strong>.</p>
<h2 id="why-i-started-running-everything-at-once"><a class="heading-link" href="#why-i-started-running-everything-at-once">Why I started running everything at once</a></h2>
<p>Part of my motivation is practical.</p>
<p>I already paid for multiple AI subscriptions. Claude, ChatGPT, Google AI, and a few different tools around them. I do not want to use them in a slow sequence: use Claude first, wait until I hit the limit, then switch to Codex, then switch to Gemini when something else runs out. That feels wasteful.</p>
<p>If I have multiple tools available, I want to use them at the same time. Not because every tool is equally good at every task, but because different tools have different strengths, speeds, limits, and behaviors.</p>
<p>Claude Code may be better for one kind of implementation. Codex may be useful for reviewing a plan or taking another pass at the same problem. Gemini CLI may give me a different perspective. Sometimes the value is not that one model is clearly better, but that they do not fail in exactly the same way.</p>
<p>So I started thinking less about “which tool should I use?” and more about “how do I compose these tools together?”</p>
<p>That is where things get interesting.</p>
<p>One agent can implement a feature, another can review the plan before implementation, another can generate tests, another can inspect the diff and look for edge cases, another can explore a simpler version of the same solution.</p>
<p>I am no longer just doing AI-assisted coding. It starts to look more like orchestration.</p>
<h2 id="the-first-wall-i-hit-was-not-code-quality"><a class="heading-link" href="#the-first-wall-i-hit-was-not-code-quality">The first wall I hit was not code quality</a></h2>
<p>I expected the first major bottleneck to be code quality.</p>
<p>That is still a real problem. AI-generated code can be functional but wrong. It can pass the happy path while missing the edge cases, it can create abstractions that look clean in isolation but do not fit the existing system, it can confidently change things it does not fully understand.</p>
<p>But surprisingly, that was not the first wall I hit.</p>
<p>The first wall was tab switching.</p>
<p>Personally, I use tmux heavily. I arrange agents by session, window, and pane. One session for a feature, one session for another experiment, one pane for a running server, one pane for tests, one pane for a coding agent, another pane for a review agent.</p>
<p>This works quite well when the number of active streams is small.</p>
<p>If an agent needs my attention once an hour, no problem. I can switch to it, read the output, make a decision, and move on.</p>
<p>But agents do not work at human speed.</p>
<p>They generate code fast, they ask for confirmation, they hit errors, they finish tasks, they need permission, they ask whether to continue, they suggest a plan, they wait for feedback. When that happens every few minutes, tab switching becomes more than a small annoyance.</p>
<p>I found myself jumping between sessions, trying to remember what each agent was doing, what I asked it to do, whether I had already reviewed its plan, whether the current output was good, and whether I still trusted the direction.</p>
<p>The visible problem was switching tabs, and the real problem was switching context.</p>
<h2 id="more-agents-create-more-open-loops"><a class="heading-link" href="#more-agents-create-more-open-loops">More agents create more open loops</a></h2>
<p>Every agent creates an open loop in your head.</p>
<p>One agent is working on the API layer, another is touching the frontend, another is reviewing tests, another is exploring a refactor, another is checking if your architecture plan makes sense. Each one has its own goal, assumptions, progress, risk, and current state.</p>
<p>The agent may remember its own context, but you still need to remember your context.</p>
<ul>
<li>Why did I start this task?</li>
<li>What constraint did I give it?</li>
<li>Did it follow the existing pattern?</li>
<li>Did it make a decision I did not approve?</li>
<li>Is this output still aligned with the original intent?</li>
<li>Should I stop it now before it goes too far?</li>
</ul>
<p>This is the part I think many people underestimate.</p>
<p>Running multiple agents does not mean there is more of you. Your attention does not automatically scale just because the execution layer scales. <strong>You may have five agents generating output, but there is still one human brain trying to understand, judge, and integrate that output</strong>.</p>
<p>That is the next bottleneck. The bottleneck is whether the engineer can safely navigate and orchestrate multiple agents without losing control of the work.</p>
<h2 id="the-engineer-becomes-the-orchestrator"><a class="heading-link" href="#the-engineer-becomes-the-orchestrator">The engineer becomes the orchestrator</a></h2>
<p>I used to think AI productivity was mostly about writing better prompts.</p>
<p>Prompting matters, but it is only the first layer. Once you move from one AI assistant to multiple agents, the skill changes. You are no longer only asking for code. You are managing work.</p>
<ul>
<li>You need to decide which task should be delegated.</li>
<li>You need to split work into clear boundaries.</li>
<li>You need to decide which agent should review which output.</li>
<li>You need to avoid two agents making conflicting changes.</li>
<li>You need to know when to interrupt an agent before it digs a deeper hole.</li>
<li>You need to create verification loops so you do not become the only test system.</li>
</ul>
<p>This feels much closer to <a href="https://codeaholicguy.com/2025/07/26/what-next-level-actually-looks-like/">engineering leadership</a>.</p>
<p>A good tech lead does not randomly assign work to five engineers and hope everything magically merges at the end. They clarify the goal, define boundaries, create checkpoints, review important decisions, and make sure the pieces fit together.</p>
<p>Multi-agent engineering needs the same discipline. The difference is that agents move faster, produce more output, and can create more mess in less time.</p>
<h2 id="experiment-1-hierarchical-orchestration"><a class="heading-link" href="#experiment-1-hierarchical-orchestration">Experiment 1: hierarchical orchestration</a></h2>
<p>One experiment I have been working on is a hierarchical setup.</p>
<p>The idea is simple: instead of me directly managing every agent, <a href="https://github.com/codeaholicguy/ai-devkit/blob/main/skills/agent-orchestration/SKILL.md">one agent acts as the coordinator</a>. It works like a team lead. It understands the goal, breaks down the work, delegates tasks to other agents, collects progress, and summarizes what needs my attention.</p>
<blockquote>
<p>I had Claude Code and Codex working together like a real software team.</p>
<p>In the last video, I showed a simple setup: one Claude Code agent orchestrating other agents.</p>
<p>This time, I pushed it further.</p>
<p>I used ai-devkit agent orchestration to run a small “team”:<br>
– Leader: Sonnet… <a href="https://t.co/76PA3hvK7C">https://t.co/76PA3hvK7C</a> <a href="https://t.co/9lziHWn8cX">pic.twitter.com/9lziHWn8cX</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://x.com/codeaholicguy/status/2041855579396706559?ref_src=twsrc%5Etfw">April 8, 2026</a></p>
</blockquote>
<p>In theory, this reduces the interruption cost.</p>
<p>Instead of five agents asking me five different questions, the coordinator can filter the noise. It can decide which updates are worth surfacing, ask one agent to review another agent’s plan, compare outputs, and tell me where the work is stuck.</p>
<p>This is powerful when it works. It changes the interaction model from “I need to monitor every agent” to “I need to monitor the system”.</p>
<p>But it also introduces a new problem: How much do I trust the coordinator?</p>
<p>If the coordinator misunderstands the goal, the whole group may move in the wrong direction. If it summarizes too aggressively, I may miss an important detail. If it delegates poorly, I may only discover the problem after multiple agents have already produced a lot of code.</p>
<p>So the coordinator agent does not remove the need for human judgment. It moves the judgment point.</p>
<p>Instead of reviewing every small step, I now need to review the coordinator’s decisions, assumptions, and summary quality. That is useful, but it is not free.</p>
<p>The lesson so far: hierarchy helps reduce direct noise, but it needs strong checkpoints.</p>
<h2 id="experiment-2-mission-control-for-agents"><a class="heading-link" href="#experiment-2-mission-control-for-agents">Experiment 2: mission control for agents</a></h2>
<p>The second experiment is a mission-control style interface.</p>
<p>The idea is to have one place where I can see all active agents, understand what each one is doing, switch to any of them, and talk to them directly without jumping across many terminal tabs.</p>
<blockquote>
<p>I'm running Claude Code, Codex, and Gemini CLI in parallel.</p>
<p>The worst part isn't the token cost. It's the tab switching.</p>
<p>AI DevKit's Agent Console fixes this. One view, all your agents, send messages without leaving, quick hotkey to focus when you need detail.</p>
<p>Works in iTerm2,… <a href="https://t.co/3VHg8LIUBa">pic.twitter.com/3VHg8LIUBa</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://x.com/codeaholicguy/status/2059975841602257065?ref_src=twsrc%5Etfw">May 28, 2026</a></p>
</blockquote>
<p>This came from my own frustration with tmux.</p>
<p>Tmux is great. I still use it, but tmux was not designed specifically for managing AI agents. It helps organize terminal windows, but it does not understand agent state.</p>
<ul>
<li>It does not tell me which agent is waiting for input.</li>
<li>It does not summarize what changed.</li>
<li>It does not show which agent is blocked.</li>
<li>It does not help me understand which task is risky.</li>
<li>It does not tell me which session needs my attention first.</li>
</ul>
<p>That is what I want from a mission-control interface, not a fancy UI for the sake of it. I want a lower-friction way to manage attention.</p>
<p>I want to glance at one place and know what is happening.</p>
<ul>
<li>Which agents are running?</li>
<li>Which agents are waiting?</li>
<li>Which agents have produced a diff?</li>
<li>Which agents are stuck?</li>
<li>Which agents need review?</li>
<li>Which agents can continue without me?</li>
</ul>
<p>This sounds small, but I think it matters a lot.</p>
<p>When the cost of navigation is high, you avoid checking. When you avoid checking, agents drift. When agents drift, review becomes harder. When review becomes harder, you either slow down or you merge things with less confidence.</p>
<p>A good mission-control layer should reduce the cost of staying on top of the work. You can try it out by running <code>npx ai-devkit@latest agent console</code>.</p>
<h2 id="ai-devkit-as-my-experiment-foundation"><a class="heading-link" href="#ai-devkit-as-my-experiment-foundation">AI DevKit as my experiment foundation</a></h2>
<p>This is also why I have been spending more time experimenting with <a href="https://ai-devkit.com/">AI DevKit</a>. Not as “the answer” to everything, but as a foundation for experiments.</p>
<p>Once you start playing with multi-agent workflows, you quickly realize you need reusable building blocks. You need a way to manage agent sessions. You need a way to structure phases. You need a way to keep context organized. You need shared memory. You need a way to make experiments repeatable instead of hacking together a one-off script every weekend.</p>
<p>For example, having a foundation like an <a href="https://github.com/codeaholicguy/ai-devkit/tree/main/packages/agent-manager">agent manager</a> makes it easier to build higher-level workflows on top. I do not want to rebuild the basic plumbing every time I want to test a new orchestration idea.</p>
<p>That is the useful part. The product is not the point here. The research question is the point.</p>
<ul>
<li>How should engineers work when they have multiple agents running at the same time?</li>
<li>How much autonomy should each agent have?</li>
<li>When should an agent interrupt?</li>
<li>What should be summarized?</li>
<li>What should never be summarized?</li>
<li>Which review tasks can be delegated to another agent?</li>
<li>Where must the human stay directly involved?</li>
</ul>
<p>I do not think we have the final answer yet. That is what makes it interesting.</p>
<h2 id="the-next-tooling-layer-is-not-a-better-code-editor"><a class="heading-link" href="#the-next-tooling-layer-is-not-a-better-code-editor">The next tooling layer is not a better code editor</a></h2>
<p>Most AI coding tools today still feel centered around one main interaction: human asks, AI responds.</p>
<p>That works for many workflows, but multi-agent work needs a different shape.</p>
<ul>
<li>We need tools that understand agent state, not just terminal output.</li>
<li>We need attention routing, so the engineer is interrupted only when needed.</li>
<li>We need review queues, so generated work does not disappear into random sessions.</li>
<li>We need task boundaries, so agents do not step on each other.</li>
<li>We need verification loops, so speed does not destroy confidence.</li>
<li>We need better ways to compare parallel attempts at the same problem.</li>
<li>We need summaries that preserve decision quality, not just compress text.</li>
</ul>
<p>This is where I think the next wave of engineering tooling will go.</p>
<p>The future is not only a smarter code editor. It is an operating layer for agentic work. A place where engineers can define intent, spin up agents, observe progress, redirect work, review outputs, and keep ownership without drowning in context switching.</p>
<h2 id="more-output-is-not-the-same-as-more-progress"><a class="heading-link" href="#more-output-is-not-the-same-as-more-progress">More output is not the same as more progress</a></h2>
<p>There is a trap here.</p>
<p>When you first run multiple agents, it feels productive because many things are happening. It looks like progress. But <a href="https://codeaholicguy.com/2025/11/29/the-illusion-of-output/">output is not progress</a>.</p>
<p>Progress means the system is moving closer to the right outcome. That requires judgment.</p>
<ul>
<li>If five agents generate five different implementations that do not fit the codebase, you did not become five times more productive. You just created five things to review.</li>
<li>If one agent builds a feature and another breaks a shared abstraction, you did not save time. You moved the cost to integration.</li>
<li>If agents produce code faster than your verification loop can handle, you are not accelerating engineering. You are accumulating uncertainty.</li>
</ul>
<p>This is why I keep coming back to the same idea: AI makes execution cheaper, which pushes the constraint somewhere else.</p>
<p>For software engineering, I think the constraint is moving toward orchestration, verification, and attention.</p>
<h2 id="where-i-am-now"><a class="heading-link" href="#where-i-am-now">Where I am now</a></h2>
<p>I am still early in this exploration. I have some working experiments, some feel promising, some feel awkward.</p>
<p>That is fine. This is exactly the phase I enjoy. The interesting part is figuring out how humans and agents should work together when the number of active agents keeps increasing.</p>
<p>I do not think the best engineers in the AI era will simply be the ones who know how to prompt. They will be the ones who know how to decompose problems, assign work, create feedback loops, verify outputs, and keep ownership while multiple agents execute in parallel.</p>
<p>That is a different skill, and like every real engineering skill, it will take practice.</p>
<p>I also do not believe <a href="https://codeaholicguy.com/2025/01/18/reimagining-software-engineering-whats-next-for-engineers-for-the-ai-powered-future/">the future</a> is as simple as “AI replaces engineers”. That framing is lazy.</p>
<p>A more realistic future is that one engineer may coordinate many agents. Those agents may write code, review plans, generate tests, inspect logs, compare designs, or explore alternatives. The engineer still <a href="https://ownthecraftbook.com/chapters/">owns the problem, judgment, and the final call</a>. The shape of the job changes, less time typing every line by hand, more time deciding what should be done, how work should be split, what good looks like, and whether the result is safe enough to ship.</p>
<p>That future is exciting to me, but it will only work if we design the workflow carefully. Otherwise, we will not remove the bottleneck. We will just move it into the engineer’s head.</p>
<p><strong>More agents, same human brain.</strong></p>
<p>That is the problem I want to keep exploring.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>; it’s free. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to become a 10x engineer with AI</title>
      <link>https://codeaholicguy.com/2026/05/27/how-to-become-a-10x-engineer-with-ai/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/05/27/how-to-become-a-10x-engineer-with-ai/</guid>
      <pubDate>Wed, 27 May 2026 04:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Practical habits to become a 10x engineer with AI: write better context, ask for tradeoffs, review AI output, and build verification loops and workflows.</description>
      <content:encoded><![CDATA[<p>In the <a href="https://codeaholicguy.com/2026/05/13/the-new-10x-engineer-with-ai">previous post</a>, I shared my perspective on the new 10x engineer in the AI era.</p>
<p>A modern 10x engineer creates outsized impact by turning unclear problems into safe, shippable outcomes with less friction across the team.</p>
<p>AI helps with that, but only if the engineer knows how to use it well.</p>
<p>This is a practical follow-up. Not a framework or checklist, just a set of habits that matter more now if you want to create real leverage with AI.</p>
<h2 id="build-strong-fundamentals-first"><a class="heading-link" href="#build-strong-fundamentals-first">Build strong fundamentals first</a></h2>
<p>Do not skip the basics because AI can generate code.</p>
<p>This is the biggest trap I see. AI makes it <a href="https://codeaholicguy.com/2025/11/29/the-illusion-of-output/">easy to produce something that looks correct</a>. It can generate a feature, a test suite, a migration, a script, or a full page in minutes.</p>
<p>But if you do not understand the system, you cannot judge the output.</p>
<p>You still need to understand how APIs evolve. You need to understand data modeling, transactions, caching, queues, deployment, monitoring, debugging, performance, and security. You need to know how production fails.</p>
<p>AI can help you learn faster, but it should not remove your ability to think.</p>
<p>If you are early in your career, use AI as a learning partner, not as a replacement for learning. Ask it to explain the code. Ask it why a design works. Ask it what can go wrong. Ask it to compare two approaches.</p>
<p>Then verify the answer yourself.</p>
<p>A 10x engineer with AI is not someone who depends on AI blindly. It is someone who understands enough to guide it, challenge it, and correct it.</p>
<h2 id="write-better-context"><a class="heading-link" href="#write-better-context">Write better context</a></h2>
<p>The quality of AI output depends heavily on the quality of context. Before asking AI to implement something, slow down and write the problem clearly.</p>
<p>What are we trying to solve? What is the current behavior? What should change? What should not change? What are the constraints? What are the edge cases? What is the blast radius? How do we verify the result?</p>
<p>This sounds boring, but it is where a lot of leverage comes from.</p>
<p>A vague prompt creates vague output. A clear context creates better execution. That’s exactly why I <a href="https://ai-devkit.com/docs/10-dev-lifecycle-skill/">use the dev-lifecycle skill</a>, it forces the right questions, so the context becomes clear before any execution starts.</p>
<p>For example, “Add a notification when an order is completed” is too vague. A better context would explain when an order is considered completed, which events trigger the notification, which users should receive it, what should happen if the notification fails, which channels are supported, and what tests should pass before shipping.</p>
<p>The goal is not to write a beautiful document. The goal is to make the work executable.</p>
<p>Good context helps humans. Good context helps agents. Good context reduces rework.</p>
<h2 id="use-ai-for-understanding-before-implementation"><a class="heading-link" href="#use-ai-for-understanding-before-implementation">Use AI for understanding before implementation</a></h2>
<p>When you enter an unfamiliar codebase, don’t rush to ask AI to change it. Ask it to explain the system first.</p>
<p>Where does this behavior start? Which files are involved? What is the data flow? What are the important abstractions? What patterns does this codebase follow? Where are similar implementations?</p>
<p>This habit matters a lot.</p>
<p>AI can make changes quickly, but fast changes in an unfamiliar system can be risky. You may fix one thing and quietly break another. You may follow the wrong pattern. You may add complexity to a place that already has a simpler convention.</p>
<p>Understanding first, implementation second. If you don’t know where to start, try this <a href="https://ai-devkit.com/docs/5-understand-existing-code-with-ai-devkit/">skill</a>.</p>
<p>This is also how engineers become more domain-agnostic with AI support.</p>
<p>You may not know the system at the start. That is fine. But with structured knowledge, code search, team agents, and good prompts, you should be able to build enough understanding to contribute safely.</p>
<p>The mindset shifts from “this is not my domain” to “I don’t know this domain yet, but I can inspect it and find a safe path”.</p>
<h2 id="ask-for-tradeoffs-not-only-code"><a class="heading-link" href="#ask-for-tradeoffs-not-only-code">Ask for tradeoffs, not only code</a></h2>
<p>Many engineers use AI like a code generator. That is useful, but limited. A better habit is to use AI as a partner before asking it to write code.</p>
<p>Ask it:</p>
<ul>
<li>What are the possible approaches?</li>
<li>What is the simplest version?</li>
<li>What can go wrong?</li>
<li>What is the risky part?</li>
<li>What is the blast radius?</li>
<li>What should we test?</li>
<li>What would make this hard to maintain later?</li>
</ul>
<p>These questions force better thinking.</p>
<p>They also make you slower in the right part of the process. You spend a bit more time before implementation, so you spend less time cleaning up a bad implementation later.</p>
<p>This is especially useful for junior and mid-level engineers. AI can expose you to tradeoffs you may not naturally think about yet.</p>
<p>But again, don’t accept everything blindly. Use the output as input for your own judgment.</p>
<h2 id="review-ai-output-like-production-code"><a class="heading-link" href="#review-ai-output-like-production-code">Review AI output like production code</a></h2>
<p>Treat AI-generated code like code written by a <a href="https://ownthecraftbook.com/chapters/7-ai-as-your-junior-engineer/">fast junior engineer</a>.</p>
<p>It may be useful. It may even be impressive. But it still needs review.</p>
<p>Check whether the code actually solves the problem. Check whether it follows existing patterns. Check whether it handles failure. Check whether it creates hidden coupling. Check whether the test proves the right behavior.</p>
<p>Do not only review whether the code works locally. Review whether it should exist in the system.</p>
<p>That difference matters.</p>
<p>A feature can work and still be the wrong abstraction. A test can pass and still test the wrong thing. A refactor can look clean and still break a subtle behavior in production.</p>
<p>AI reduces implementation time, but it does not remove the need for engineering review.</p>
<p>If anything, review becomes more important because the volume of generated code can increase quickly.</p>
<h2 id="build-verification-loops"><a class="heading-link" href="#build-verification-loops">Build verification loops</a></h2>
<p>The faster you generate code, the more you need strong verification.</p>
<p>Tests matter. Type checks matter. Linters matter. CI speed matters. Logs, metrics, alerts, feature flags, canary rollout, and rollback plans matter.</p>
<p>AI speed without verification is just a faster way to create problems.</p>
<p>This is where strong engineering teams will separate themselves from noisy AI adoption.</p>
<p>A weak team may use AI to produce more changes. A strong team uses AI to produce more reliable changes.</p>
<p>The difference is verification.</p>
<p>For every meaningful change, ask yourself:</p>
<ul>
<li>How do I know this works?</li>
<li>How do I know it did not break existing behavior?</li>
<li>How do I know production is healthy after release?</li>
<li>How do I roll it back if something goes wrong?</li>
</ul>
<p>That is not bureaucracy. That is ownership.</p>
<h2 id="improve-the-agent-after-every-task"><a class="heading-link" href="#improve-the-agent-after-every-task">Improve the agent after every task</a></h2>
<p>This is one of the biggest mindset shifts.</p>
<p>Do not only use the agent. Improve the agent.</p>
<p>If the agent misunderstood a convention, update the rule. If it misses context, improve the documentation. If it repeated a mistake, improve the prompt. If it needed a command, script, or checklist, create one.</p>
<p>This is how one task turns into a multiplier.</p>
<p>The average engineer uses AI to finish the task in front of them. A high-leverage engineer uses the task to make the next task easier.</p>
<p>That small difference compounds over time.</p>
<p>After a few months, your agents become better. Your docs become better. Your workflows become clearer. Your team spends less time explaining the same thing again and again.</p>
<p>That is real leverage.</p>
<h2 id="build-reusable-workflows"><a class="heading-link" href="#build-reusable-workflows">Build reusable workflows</a></h2>
<p>A 10x engineer with AI does not repeat the same manual prompting forever.</p>
<p>If <a href="https://ai-devkit.com/docs/0-what-is-ai-devkit/">a workflow works</a>, turn it into something reusable.</p>
<p>It can be a command. A prompt template. A checklist. A test harness. A script. A repo convention. A team agent. A small internal tool.</p>
<p>The format does not matter as much as the habit.</p>
<p>When you find yourself doing the same thing three times, pause and ask whether it should become a reusable workflow.</p>
<p>For example:</p>
<ul>
<li>Requirement review</li>
<li>Design review</li>
<li>Test generation</li>
<li>Code review</li>
<li>Migration planning</li>
<li>Incident investigation</li>
<li>Release note generation</li>
<li>Monitoring checklist</li>
</ul>
<p>These are all workflows that can be partially standardized.</p>
<p>The goal is not to remove human thinking. The goal is to remove repeated setup cost so humans can spend more energy on judgment.</p>
<h2 id="think-in-systems"><a class="heading-link" href="#think-in-systems">Think in systems</a></h2>
<p>Delivering a ticket is useful. Improving the system that delivers future tickets is leverage.</p>
<p>The new 10x engineer does both.</p>
<p>They ship the feature, but they also improve the path. They fix the bug, but they also improve detection. They complete the task, but they also improve the agent, the docs, and the verification around it.</p>
<p>This is the practical meaning of leverage thinking.</p>
<p>You are not only asking, “How do I finish this?”. You are also asking, “How do I make this easier, safer, or faster next time?”</p>
<p>That is the difference between being productive and creating leverage.</p>
<h2 id="stay-accountable-for-the-outcome"><a class="heading-link" href="#stay-accountable-for-the-outcome">Stay accountable for the outcome</a></h2>
<p>The last habit is simple, but probably the most important.</p>
<p><strong>Own the outcome.</strong></p>
<p>AI can help you move faster. It can help you understand code, generate tests, write implementation, summarize logs, and draft documentation.</p>
<p>But the responsibility still belongs to the engineer.</p>
<p>If the code breaks production, it does not matter that AI generated it. If the test misses the real edge case, it does not matter that AI wrote it. If the rollout has no monitoring, it does not matter that AI suggested the plan.</p>
<p>The engineer owns the final decision. This is why I think AI will not reduce the importance of engineering maturity. It will increase it.</p>
<p>The more leverage we have, the more judgment we need.</p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>Becoming a 10x engineer with AI is not about learning a magic prompt. It is about building the habits that let you use AI safely and repeatedly.</p>
<p>AI can make you faster, but speed alone is not the goal.</p>
<p>The goal is to create better outcomes with less wasted effort, less coordination overhead, and more reusable leverage for the team.</p>
<p>That is what the new 10x engineer should look like.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>Claude Code Guide: How to use Claude Code like you’ve used it for a year</title>
      <link>https://codeaholicguy.com/2026/05/20/claude-code-guide-how-to-use-claude-code-like-youve-used-it-for-a-year/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/05/20/claude-code-guide-how-to-use-claude-code-like-youve-used-it-for-a-year/</guid>
      <pubDate>Wed, 20 May 2026 06:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <category>ai-devkit</category>
      <description>Advanced Claude Code tips: context and cache mechanics, /compact, plan mode, thinking budgets, subagents, CLAUDE.md, slash commands, hooks, and worktrees.</description>
      <content:encoded><![CDATA[<p>Most <a href="https://codeaholicguy.com/2026/01/10/claude-code-vs-cursor/">Claude Code</a> tutorials stop at “install it and start chatting”. That gets you maybe 20% of what the tool does. The other 80% comes from understanding how <a href="https://codeaholicguy.com/2026/01/31/ai-coding-agents-explained-rules-commands-skills-mcp-hooks/">context, modes, subagents, and hooks<br>
fit together</a>. It’s the stuff that’s technically in the docs but reads as marketing until you’ve burned a few thousand tokens learning it the hard way.</p>
<p>This is what I’d tell a teammate on day one if I had ten minutes. Patterns that change how fast you actually ship, and a few that will save you from yourself.</p>
<h2 id="what-claude-code-is-briefly"><a class="heading-link" href="#what-claude-code-is-briefly">What Claude Code is, briefly</a></h2>
<p><a href="https://claude.com/product/claude-code">Claude Code</a> is <a href="https://www.anthropic.com/">Anthropic</a>‘s CLI agent. You run <code>claude</code> in a terminal, and it can read files, run shell commands, edit code, and call tools. There are extensions for VS Code and a desktop app. The CLI is where the interesting features live.</p>
<p>The mental model that matters: it’s a <a href="https://ownthecraftbook.com/chapters/7-ai-as-your-junior-engineer/">junior engineer who usually forgets things</a>. Vague input gets vague output. Specific input (file paths, constraints, what you’ve ruled out, what “done” looks like) gets results you don’t have to redo.</p>
<h2 id="context-and-cache-mechanics"><a class="heading-link" href="#context-and-cache-mechanics">Context and cache mechanics</a></h2>
<p>This is probably the biggest lever on cost and latency, and almost nobody who hasn’t used the API understands it.</p>
<p>The <a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching">prompt cache has a 5-minute TTL</a>. When you send a message, Anthropic caches the prefix of your conversation. If your next message comes within 5 minutes, the cached prefix is essentially free. If you step away for a meeting and come back 20 minutes later, the entire conversation is re-read from scratch, uncached. Slower, more expensive.</p>
<p>Practical implication: if you’re about to step away, finish your turn at a clean boundary. Don’t leave a half-finished plan dangling. The next reply will replay the whole thing.</p>
<p>There’s an extended 1-hour cache too, but it’s opt-in. Default is 5 minutes.</p>
<h2 id="compact-accepts-instructions"><a class="heading-link" href="#compact-accepts-instructions">/compact accepts instructions</a></h2>
<p>Plain <code>/compact</code> summarizes your conversation and replaces the full history with the summary. It’s lossy, and the default summarization will often drop the wrong things. Better:</p>
<blockquote>
<p><code>/compact</code> preserve the auth refactor decisions and the failing test output, drop the file listings</p>
</blockquote>
<p>You’re telling Claude what matters before it summarizes. Use it before context gets tight, not after.</p>
<p><code>/clear</code> isn’t <code>/compact</code></p>
<p><code>/clear</code> wipes the conversation entirely. Use it when switching topics. Starting a new task with a clean slate keeps later prompts fast and prevents earlier wrong turns from biasing the answer. <code>/compact</code> summarizes. <code>/clear</code> resets.</p>
<h2 id="context-drifts"><a class="heading-link" href="#context-drifts">Context drifts</a></h2>
<p>If Claude went down a wrong path and you corrected it, that wrong path is still in the conversation, still affecting what it pattern-matches on. For non-trivial pivots, <code>/clear</code> and restart with what you learned. Hoping it “remembers the correction” gets unreliable past a certain point.</p>
<h2 id="resuming-sessions"><a class="heading-link" href="#resuming-sessions">Resuming sessions</a></h2>
<p><code>claude --resume</code> lists your past sessions in the current project and lets you pick one up. Each session is stored as a .jsonl file under ~/.claude/projects/. Useful when coming back to a stalled task with full history intact.</p>
<h2 id="modes-plan-edit-accept-edits"><a class="heading-link" href="#modes-plan-edit-accept-edits">Modes: plan, edit, accept-edits</a></h2>
<p><code>Shift+Tab</code> cycles between modes in an active session.</p>
<p>Plan mode is read-only. It can read files, grep, run safe shell commands, but it can’t write or edit. This is the mode you want for anything you don’t fully understand yet. It forces investigation before code gets touched. When the plan looks right, Shift+Tab back to edit mode and execute.</p>
<p>People underuse plan mode because muscle memory says “just ask and let it do it”. That works for trivial changes. For anything load-bearing, ten minutes in plan mode saves an hour of unwinding bad edits.</p>
<p>Accept-edits mode auto-accepts file edits without prompting per change. Good for refactors where you’ll review the full diff at the end anyway. Pair it with frequent git diff checks. You get the speed of unattended editing without losing the ability to catch a bad change before it compounds.</p>
<h2 id="thinking-budgets"><a class="heading-link" href="#thinking-budgets">Thinking budgets</a></h2>
<p>Claude Code supports keyword-triggered extended thinking. The keywords escalate:</p>
<ul>
<li><code>think</code> is a small budget</li>
<li><code>think hard</code> is larger</li>
<li><code>think harder</code> is larger still</li>
<li><code>ultrathink</code> is the maximum</li>
</ul>
<p>Use these for architectural decisions, hard debugging, or anything where Claude gave you a confidently wrong answer on a subtle question. Don’t use them for simple tasks, such as “rename this variable”. You’re paying for thinking tokens that don’t change the outcome. Some tasks fail without thinking. Most don’t need it. You learn the difference by trying a hard task without it and seeing where it breaks.</p>
<h2 id="subagents-protect-your-main-context"><a class="heading-link" href="#subagents-protect-your-main-context">Subagents protect your main context</a></h2>
<p>The Agent tool spawns a subagent that works in isolation. It has its own context window. When it finishes, you get a summary back, not the full transcript.</p>
<p>This matters because some operations bloat your main context badly. A large grep across the repo. Reading several files to find one piece of information. Web research with multiple page fetches. Big find commands that dump thousands of paths.</p>
<p>Delegating those to an Explore or general-purpose subagent keeps your main thread clean. You get the answer.</p>
<p>A couple of patterns that pay off here.</p>
<ul>
<li>Parallel subagents: if you have three independent questions, launch three subagents in one message. They run concurrently. Sequential launches are wasted time.</li>
<li>Trust but verify: the agent’s summary is what it intended to do. If it claims it edited files, run git diff and check. Subagents will sometimes report success on a partial implementation. Treat the report as a hypothesis, not a fact.</li>
</ul>
<h2 id="input-shortcuts-that-change-the-workflow"><a class="heading-link" href="#input-shortcuts-that-change-the-workflow">Input shortcuts that change the workflow</a></h2>
<p>Three small features most people miss.</p>
<p><code>!</code> runs a shell command directly in the conversation. Output lands in context. Useful for interactive things you’d otherwise have to switch terminals for:</p>
<pre><code class="hljs language-bash">! gcloud auth login

! git <span class="hljs-built_in">log</span> --oneline -20
</code></pre>
<p>The shell output becomes part of the conversation, so Claude can act on it immediately.</p>
<p><code>@path/to/file</code> pulls the file into context inline. You don’t have to ask Claude to read it. Handy when referencing a specific file in your prompt:</p>
<pre><code class="language-text">Refactor @src/auth/session.ts to use the new token format from @src/auth/tokens.ts
</code></pre>
<p><code>#</code> prefix quick-saves the line to a CLAUDE.md. It prompts you to pick which one (project, user, or local). Catch yourself repeating an instruction? Save it once.</p>
<pre><code class="language-text"># never edit files under generated/

# this project uses pnpm, not npm
</code></pre>
<p>These three together cut a huge amount of friction out of normal use.</p>
<h2 id="the-claudemd-hierarchy"><a class="heading-link" href="#the-claudemd-hierarchy">The CLAUDE.md hierarchy</a></h2>
<p>CLAUDE.md files auto-load as context every session. The hierarchy:</p>
<ul>
<li><code>~/.claude/CLAUDE.md</code> is user-level. Loads in every session everywhere.</li>
<li><code>/CLAUDE.md</code> loads in this project.</li>
<li><code>/CLAUDE.md</code> loads when you’re working inside that directory.</li>
</ul>
<p>Use the hierarchy. Module-specific belongs in <code>src/payments/CLAUDE.md</code>, not the project root. Personal preferences (“I always want explanations in terms of TypeScript analogies”) belong in your user-level file, not committed to the repo.</p>
<p>The best use of CLAUDE.md is for the things you find yourself explaining twice. Example:</p>
<ul>
<li>Use the existing logger, don’t import a new one.</li>
<li>Tests live in tests/, not next to the source.</li>
</ul>
<p>Save it once, never explain it again.</p>
<h2 id="custom-slash-commands"><a class="heading-link" href="#custom-slash-commands">Custom slash commands</a></h2>
<p>If you paste the same prompt twice, make it a slash command. Drop a markdown file in <code>.claude/commands/</code>. Example:</p>
<pre><code class="language-text">.claude/commands/changelog.md

Generate a changelog for the commits since the last release tag. Group by feat / fix / chore. Include PR numbers where present.
</code></pre>
<p>Now <code>/changelog</code> runs that prompt. Commit <code>.claude/commands/</code> to your repo, and your team gets them too.</p>
<p>A few worth having: <code>/review</code> for code review of pending changes, <code>/explain</code> to explain a file or function in plain English, <code>/test</code> to write tests for the current changes. You might also use skills, but command is the simplest form, use skills only when you need to run scripts.</p>
<h2 id="hooks-for-always-do-x"><a class="heading-link" href="#hooks-for-always-do-x">Hooks for “always do X”</a></h2>
<p>Memory and <code>CLAUDE.md</code> are read by Claude, not enforced by the harness. If you want a behavior that always happens, regardless of what Claude decides, that’s a hook.</p>
<p>Hooks live in <code>settings.json</code> and trigger on events:</p>
<ul>
<li><code>PreToolUse</code> runs before a tool call. Can block it.</li>
<li><code>PostToolUse</code> runs after. Can react.</li>
<li><code>Stop</code> runs when Claude finishes a response.</li>
<li><code>UserPromptSubmit</code> runs when you submit a prompt.</li>
</ul>
<p>What people actually do with them, for example:</p>
<ul>
<li>A <code>Stop</code> hook that runs <code>npm run lint</code> after every response.</li>
<li>A <code>PreToolUse</code> hook that refuses any <code>rm -rf</code> outside <code>/tmp</code>.</li>
<li>A PostToolUse hook that auto-formats edited files.</li>
</ul>
<p>If you’ve ever told Claude “always run tests after editing” and it forgot two responses later, that’s a hooks problem, not a memory problem.</p>
<h2 id="switching-models-mid-session"><a class="heading-link" href="#switching-models-mid-session">Switching models mid-session</a></h2>
<p><code>/model</code> swaps the model for the current conversation. Use it.</p>
<p>The common pattern is <code>Opus</code> for design, debugging, and the hard reasoning bits, then <code>Sonnet</code> for high-volume edits where speed matters more than depth. You don’t need <code>Opus</code> to update a hundred import paths.</p>
<p>Most people pick one model at session start and never switch. Matching the model to the phase of work is one of the easier wins available.</p>
<h2 id="worktrees-for-parallel-work"><a class="heading-link" href="#worktrees-for-parallel-work">Worktrees for parallel work</a></h2>
<p>Git worktrees let you check out multiple branches of the same repo into separate directories. With Claude Code, that means multiple sessions on different branches without stomping on each other.</p>
<pre><code class="hljs language-bash">git worktree add ../task-manager-feature-x feature-x

<span class="hljs-built_in">cd</span> ../task-manager-feature-x

claude
</code></pre>
<p>Run a second claude from your main worktree on a different branch. Two sessions, two branches, one repo.</p>
<p>The Agent tool also accepts isolation: “worktree”, which sandboxes a subagent’s work in its own worktree. Useful when you want an agent to attempt a risky change without touching your working tree until you’ve reviewed it.</p>
<h2 id="prompt-quality-matters-more-than-model-choice"><a class="heading-link" href="#prompt-quality-matters-more-than-model-choice">Prompt quality matters more than model choice</a></h2>
<p>This is the most underrated thing about getting good output.</p>
<p>Compare:</p>
<ul>
<li>“Fix the session bug”</li>
<li>“Fix the bug in src/auth/session.ts:142 where expiresAt is set in seconds but compared in ms”</li>
</ul>
<p>The second one will get the right fix from any model. The first will get speculation, even from the smartest one. Two prompt patterns that work. Telling Claude what not to do is often higher leverage than telling it what to do:</p>
<blockquote>
<p>Don’t refactor surrounding code, don’t add tests, don’t touch the migration. Only change the bug on line 142.</p>
</blockquote>
<p>Without that, you’ll often get a “while I was in there” cleanup pass that adds noise to your diff. For exploratory questions, ask for a recommendation with the tradeoff, not a plan. “How should we approach X?” answered as a recommendation gets you a useful starting point. Answered as a plan, you get premature commitment to a path before you’ve decided it’s the right one.</p>
<h2 id="verify-before-claiming-done"><a class="heading-link" href="#verify-before-claiming-done">Verify before claiming done</a></h2>
<p>The most common failure mode is “should work” or “looks correct” without actually running it.</p>
<p>Type checks and unit tests verify code, not feature behavior. If you changed UI, open the browser. If you changed an endpoint, hit it. If you changed a script, run it.</p>
<p>When Claude reports a task as complete, the safe move is to ask what it actually ran to verify. If the answer is “I read the code”, that isn’t verification.</p>
<h2 id="if-you-dont-want-to-remember-all-of-this"><a class="heading-link" href="#if-you-dont-want-to-remember-all-of-this">If you don’t want to remember all of this</a></h2>
<p><a href="https://ai-devkit.com">AI DevKit</a> is a Claude Code setup that bakes these patterns into skills and hooks. The right thing happens automatically, so you don’t have to remember.</p>
<p>A few of the skills line up directly with sections from this post.</p>
<p>The <code>verify</code> skill auto-triggers when Claude claims a task is done. It runs the verification step Claude was about to skip.</p>
<p>The <strong>dev-lifecycle</strong> skill walks a feature from idea to merged code: requirements, clarification, design review, TDD-style planning, implementation, tests, code review. At every phase, it forces you to spell out the goal, the constraints, and what done looks like before Claude writes anything. You can’t skip ahead with a half-formed plan.</p>
<p>The <strong>debug</strong> skill kicks in when you’re chasing a bug. It runs Claude through a real RCA: reproduce, isolate, hypothesize, then verify the fix.</p>
<p>The <strong>document-code</strong> skill documents code without your guiding function by function.</p>
<p>Beyond the skills, AI DevKit adds a few things to the harness.</p>
<p>A local memory service, for the long-term memory <code>CLAUDE.md</code> can’t do. CLAUDE.md is a static file you maintain. The memory service learns across sessions, so Claude stops making the same mistake twice in different conversations.</p>
<p>An orchestration layer for running multiple Claude Code in parallel. Worth it once you are trying to run many agents at the same time.</p>
<p>Telegram integration, so a long-running agent can ping you when it needs input or hits a blocker. Frees you from babysitting the terminal.</p>
<p>If my sharing resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>The new 10x Engineer with AI</title>
      <link>https://codeaholicguy.com/2026/05/13/the-new-10x-engineer-with-ai/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/05/13/the-new-10x-engineer-with-ai/</guid>
      <pubDate>Wed, 13 May 2026 11:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>What makes a 10x engineer in the AI era: strong fundamentals, context engineering, judgment, ownership, and thinking in leverage rather than raw output.</description>
      <content:encoded><![CDATA[<p>The idea of the “10x engineer” has always been a bit controversial.</p>
<p>Some people see it as a <a href="https://antirez.com/news/112">myth</a>. Some people see it as a harmful label that creates hero culture. Some people have worked with engineers who clearly create much more impact than others, and believe the idea is real.</p>
<p>I sit somewhere in the middle.</p>
<p>I don’t think a 10x engineer means someone who writes 10x more code than everyone else. That version of the idea was never useful to me. Writing more code is not the same as creating more value. Sometimes, the best engineer is the one who removes code, simplifies the system, or prevents a problem from happening in the first place.</p>
<p>To me, a 10x engineer is someone who creates much higher impact than expected.</p>
<p>That impact comes from how they understand problems, how they recognize patterns, how they design systems, how they debug issues, and how they make decisions with simplicity in mind.</p>
<h2 id="whats-changed"><a class="heading-link" href="#whats-changed">What’s changed</a></h2>
<p>Now, AI changes the equation a little.</p>
<p>Not because AI magically turns every engineer into a 10x engineer. It does not. In some cases, it does the opposite. It helps people produce <a href="https://codeaholicguy.com/2025/11/29/the-illusion-of-output/">more code, more documents, more prototypes, and more noise</a> without necessarily producing better outcomes.</p>
<p>What AI really changes is where leverage sits.</p>
<p>Before AI, a lot of engineering time was spent on manual implementation. Writing boilerplate. Searching the codebase. Creating test cases. Reading unfamiliar files. Updating similar patterns across different places.</p>
<p>AI can <a href="https://ownthecraftbook.com/chapters/8-where-ai-helps-and-where-it-doesnt/">compress a lot of that work</a>.</p>
<p>But when execution becomes cheaper, the bottleneck moves somewhere else. The harder part is no longer only writing the code. It is deciding what should be built, giving the right context, supervising the output, verifying correctness, and making sure the change is safe to ship.</p>
<p>That is where the new 10x engineer starts to look different.</p>
<p>A modern 10x engineer consistently turns ambiguous problems into safe, shippable outcomes with minimal coordination overhead, while using AI agents to compress execution time.</p>
<p>That sounds simple, but it is not easy.</p>
<p>Because AI does not remove the engineering discipline. It makes the lack of discipline more visible.</p>
<p>If the requirement is unclear, AI will still generate something. If the system context is missing, AI will still make assumptions. If tests are weak, AI-generated code can still look correct while quietly breaking behavior. If the engineer does not understand the domain, the output may feel convincing but be completely wrong.</p>
<h2 id="fundamentals-matter-more"><a class="heading-link" href="#fundamentals-matter-more">Fundamentals matter more</a></h2>
<p>Engineers still need to understand systems deeply enough to judge what AI produces. They need to understand architecture, data flow, API contracts, failure modes, testing, security, performance, and production behavior.</p>
<p>AI can help you move faster, but it <a href="https://codeaholicguy.com/2025/01/04/why-ai-wont-replace-human-engineers-and-why-it-shouldnt/">cannot own the judgment</a> for you.</p>
<p>This is the part I think many people underestimate. If you cannot review the code properly, AI does not make you senior. It only makes you faster at producing things you may not understand.</p>
<h2 id="context-engineering"><a class="heading-link" href="#context-engineering">Context engineering</a></h2>
<p>The new 10x engineer has a few traits that matter even more in an agentic organization.</p>
<p>The first one is context engineering.</p>
<p>Good engineers already know how to <a href="https://codeaholicguy.com/2025/07/19/the-real-stages-of-engineering-experience/">explain problems clearly to other humans</a>. In the AI era, this becomes even more important because agents also depend on context.</p>
<p>A strong engineer can turn a messy idea into a clear problem statement. They document assumptions, constraints, edge cases, success criteria, and boundaries. They make the work understandable enough for both humans and AI agents to execute correctly.</p>
<p>Good context reduces back and forth. It reduces hallucination. It reduces rework. It makes execution more predictable.</p>
<h2 id="problem-solving"><a class="heading-link" href="#problem-solving">Problem solving</a></h2>
<p>The second trait is problem solving.</p>
<p>A high-impact engineer does not just take a ticket and ask AI to implement it. They clarify what problem we are solving. They break the work down. They understand the system impact. They think about blast radius. They decide what can be automated and what needs careful human review.</p>
<p>AI helps with execution, but the engineer still needs to direct the work.</p>
<h2 id="judgment"><a class="heading-link" href="#judgment">Judgment</a></h2>
<p>The third trait is judgment.</p>
<p>Good engineers know when to go fast and when to slow down.</p>
<p>Not every change deserves the same level of review. A copy update, a small UI tweak, a low-risk refactor, and a payment-related logic change should not go through the same mental model.</p>
<p>The AI era needs engineers who can adjust the level of control based on risk.</p>
<p>Move fast when the risk is low. Slow down when correctness matters. Add more verification when the blast radius is high. Keep humans in the loop where judgment is required.</p>
<h2 id="ownership"><a class="heading-link" href="#ownership">Ownership</a></h2>
<p>The fourth trait is ownership.</p>
<p>AI can generate code. AI can write tests. AI can summarize logs. AI can draft rollout plans. But AI does not carry production responsibility.</p>
<p>The engineer still owns the outcome. Testing is part of the work. Observability is part of the work. Rollout is part of the work. Debugging after release is part of the work.</p>
<p>A strong engineer does not say: “AI generated it”. That is not an excuse.</p>
<p>If you merge it, you own it.</p>
<h2 id="ambition"><a class="heading-link" href="#ambition">Ambition</a></h2>
<p>The fifth trait is ambition.</p>
<p>I don’t mean ambition as in chasing titles or trying to look busy. I mean the ambition to solve the problem properly.</p>
<p>A high-impact engineer does not stop at fixing symptoms. They ask why the issue happened. They look for the root cause. They remove repeated manual steps. They improve the system so the same class of problem is less likely to happen again.</p>
<p>This is where AI can create a lot of leverage.</p>
<h2 id="the-best-engineers-think-in-leverage"><a class="heading-link" href="#the-best-engineers-think-in-leverage">The best engineers think in leverage</a></h2>
<p>The best AI-fluent engineers I see do not only use AI to deliver features faster. They use AI to improve the way work gets done.</p>
<p>They improve the agent. They improve the prompt. They improve the documentation. They improve test coverage. They improve the workflow. They create reusable patterns so the next engineer and the next agent can move faster.</p>
<p>This is a different way of thinking.</p>
<p>In the past, work scaled mostly with people. If you wanted more output, you added more engineers. Of course, that also added coordination cost, onboarding cost, and communication overhead.</p>
<p>With AI agents, some types of work can scale differently.</p>
<p>Work starts to scale with the number of useful agents we can add and orchestrate. But that only works if the agents have enough context, enough guardrails, and enough verification.</p>
<p>This is why the 10x engineer in the AI era thinks in leverage. They don’t ask only, “How do I finish this task?”</p>
<p>They also ask:</p>
<ul>
<li>How do I make the next similar task easier?</li>
<li>How do I make the agent better next time?</li>
<li>How do I make this knowledge reusable?</li>
<li>How do I reduce coordination for the team?</li>
<li>How do I turn this solution into a multiplier?</li>
</ul>
<h2 id="domain-agnostic-engineering"><a class="heading-link" href="#domain-agnostic-engineering">Domain-agnostic engineering</a></h2>
<p>This also changes how we think about domain ownership.</p>
<p>In the past, it was common for engineers to say, “This is not my domain”.</p>
<p>Sometimes that was reasonable. Systems were complex. Knowledge was scattered. Documentation was incomplete. The fastest path was to find the person who already knew the answer.</p>
<p>But with AI support, team agents, structured knowledge, and better context access, I expect engineers to become more domain-agnostic.</p>
<p>That does not mean every engineer becomes an expert in every system.</p>
<p>It means engineers should be able to contribute across codebases more often. They should be able to ask the agent to explain the flow, inspect the code, understand the patterns, identify risks, and propose a safe path.</p>
<p>When structured knowledge is accessible, “this is not my domain” becomes less acceptable as the default answer.</p>
<p>The better answer is:</p>
<p>“I don’t know this domain yet, but I can use the available context, inspect the system, and figure out a safe way to contribute.”</p>
<p>That is a very different mindset.</p>
<h2 id="the-environment-matters"><a class="heading-link" href="#the-environment-matters">The environment matters</a></h2>
<p>One thing I’ve learned over time is that engineers don’t become high-impact just by trying harder. The system they work in matters more than we like to admit.</p>
<p>In an agentic setup, that system is everything around them: repo structure, documentation quality, test coverage, CI speed, dependency graph, observability, and how easy it is to understand the system.</p>
<p>If these are messy, even strong engineers slow down. If these are clean, engineers can produce much better outcomes.</p>
<p>Creating more 10x outcomes is less about finding rare individuals. It is also about building an environment where good decisions are easier to make and bad ones are harder to ship.</p>
<p>That is a leadership responsibility as much as an individual responsibility.</p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>The AI era does not remove the need for great engineers. It changes what greatness looks like.</p>
<p>The new 10x engineer is not a solo hero sitting in a corner producing ten times more code. It is the engineer who combines strong fundamentals, clear judgment, good context, agent orchestration, and deep ownership to create outcomes that scale beyond themselves.</p>
<p>AI gives us more leverage. But leverage is only useful when someone knows where to point it.</p>
<p>In the next post, I’ll go deeper into the practical habits engineers can build to create this kind of leverage with AI. If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>AI adoption is not AI maturity</title>
      <link>https://codeaholicguy.com/2026/05/09/ai-adoption-is-not-ai-maturity/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/05/09/ai-adoption-is-not-ai-maturity/</guid>
      <pubDate>Sat, 09 May 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>AI adoption means people use AI; AI maturity means it improves real outcomes. Why problem clarity, ownership, verification, and reusable workflows matter.</description>
      <content:encoded><![CDATA[<p>I used to think the hard part was getting people to use AI. Now I think that is only the first step.</p>
<p>AI is becoming part of how we write, build, research, design, analyze, and operate. That is a good thing. I use AI heavily myself, and I believe it can meaningfully increase the leverage of individuals and teams.</p>
<p>But usage alone is not the goal.</p>
<p>A company can use AI everywhere and still become slower, noisier, and less disciplined. More generated documents, more prototypes, more code, more summaries, more dashboards, more activity, but not necessarily better outcomes.</p>
<p>That is the difference between AI adoption and AI maturity.</p>
<p>AI adoption means people are using AI.</p>
<p>AI maturity means AI is improving real outcomes.</p>
<p>The bar should not be “Did we use AI?”. The bar should be “What got better because we used AI?”.</p>
<ul>
<li>Did we reduce delivery cycle time without lowering quality?</li>
<li>Did we reduce repetitive work?</li>
<li>Did we improve product quality?</li>
<li>Did we make better decisions?</li>
<li>Did we lower the cost per unit of impact?</li>
<li>Did we learn faster?</li>
<li>Did customers or users feel the difference?</li>
</ul>
<p>That is the part I care about.</p>
<p>AI maturity starts with problem clarity. Before using AI, we still need to understand what problem we are solving, who we are solving it for, and why it matters. If the problem is unclear, AI will only help us generate a faster version of the wrong thing.</p>
<p>This is one of the traps I see with AI demos. It is easy to <a href="https://codeaholicguy.com/2025/11/29/the-illusion-of-output/">generate something that looks impressive</a>.</p>
<p>The second part is ownership.</p>
<p>AI can suggest, summarize, generate, refactor, test, and analyze. But AI cannot be accountable for the final decision. The person using AI still owns the thinking, trade-offs, quality, rollout, and outcome.</p>
<p>“My AI said so” is not a decision.</p>
<p>It is only an input.</p>
<p>The owner still needs to explain the reasoning. They still need to understand what was changed. They still need to know what could go wrong. They still need to own the result when it reaches users or production.</p>
<p>The third part is verification.</p>
<p>This is where speed becomes dangerous if we are not careful.</p>
<p>AI can help us move faster, but faster output without stronger verification creates hidden risk. In engineering, that means we still need tests, code review, observability, rollback plans, and clear acceptance criteria. In product work, that means user validation, quality review, success metrics, and business context.</p>
<p>AI output without verification is not acceleration.</p>
<p>It is risk moving faster.</p>
<p>The last part is reusability.</p>
<p>A one-off AI win is useful for learning. But mature AI usage should slowly turn into <a href="https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/">reusable workflows, tools, prompts, patterns, and knowledge</a>. If every team keeps rediscovering the same prompt or building the same workflow from scratch, we are not really building maturity. We are just collecting scattered experiments.</p>
<p>That is where AI maturity starts to compound. A good workflow becomes a team habit. A good prompt becomes a shared pattern. A good automation removes repeated toil. A good agent workflow becomes part of how the team works.</p>
<p>To me, mature AI usage is not about replacing human judgment.</p>
<p>It makes human judgment more important.</p>
<p>As execution becomes cheaper, the bottleneck moves somewhere else. <a href="https://ownthecraftbook.com/chapters/15-you-still-own-the-craft/">The hard part</a> becomes choosing the right problem, giving the right context, reviewing the output, making the trade-off, and deciding what good actually looks like.</p>
<p>That is why I do not think the best AI-first teams will be the teams that generate the most output.</p>
<p>They will be the teams that turn AI into verified impact.</p>
<p>More speed, with ownership.</p>
<p>More output, with quality.</p>
<p>More automation, with judgment.</p>
<p>More ambition, without losing discipline.</p>
<p>That is the AI maturity bar I care about.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>How my AI workflow evolved from prompts to workflow</title>
      <link>https://codeaholicguy.com/2026/04/11/how-my-ai-workflow-evolved-from-prompts-to-workflow/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/04/11/how-my-ai-workflow-evolved-from-prompts-to-workflow/</guid>
      <pubDate>Sat, 11 Apr 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <category>ai-devkit</category>
      <description>How my AI coding workflow evolved from reusable prompts in Cursor, Claude Code, and Codex to a structured AI DevKit workflow, and what still needs me.</description>
      <content:encoded><![CDATA[<p>About six months ago, <a href="https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/">my AI workflow was already useful</a>.</p>
<p>I had reusable commands. I had predefined templates. I had a decent setup in <a href="https://codeaholicguy.com/2026/01/10/claude-code-vs-cursor/">Cursor and later Claude Code</a> and Codex. It was enough to make AI generate code faster, help me plan, and reduce some repeated prompting. That was already a big step up from starting every session from scratch.</p>
<p>But after using it every day, I noticed that the biggest problem in my workflow was no longer code generation.</p>
<p>It was me.</p>
<p>I was still the one remembering what command to run next. I was still the one dragging context from one step to another. I was still the one making sure the AI did not skip verification, drift from the requirement, or quietly move too fast into implementation.</p>
<p>In other words, the AI could generate decent code, but I was still doing too much of the workflow orchestration myself.</p>
<p>That is the part that changed the most for me over the last six months while building and dogfooding <a href="https://github.com/codeaholicguy/ai-devkit/">AI DevKit</a>.</p>
<blockquote>
<p>I've believed for a while that AI-assisted development or Automatic Programming is the real future.</p>
<p>The engineer's workflow has fundamentally changed: code is now auto-generated at speed, so what matters most is sharp vision, deciding WHAT to build, fast proofs of concept, and… <a href="https://t.co/dmi6n65OCV">https://t.co/dmi6n65OCV</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://twitter.com/codeaholicguy/status/2017568482133495837?ref_src=twsrc%5Etfw">January 31, 2026</a></p>
</blockquote>
<h2 id="the-old-workflow-reusable-but-still-manual"><a class="heading-link" href="#the-old-workflow-reusable-but-still-manual">The old workflow: reusable, but still manual</a></h2>
<p>Six months ago, my main idea was simple: make prompting more reusable.</p>
<p>Instead of rewriting the same long instructions again and again, I created reusable commands and predefined templates. That helped a lot. I could kick off requirement writing faster. I could structure planning better. I could avoid some of the prompt fatigue that happens when you keep repeating yourself in every new project.</p>
<p>It worked, but it still felt like a power user setup, not a real workflow system.</p>
<p>The commands were reusable, but they still needed me to manually invoke them at the right time. The templates were helpful, but they still depended on me to connect the dots between requirement, design, implementation, and review. If I was tired or moving too fast, the workflow could still become inconsistent.</p>
<p>The system was better than raw prompting but it just was not carrying enough of the burden yet.</p>
<h2 id="what-changed-in-the-last-six-months"><a class="heading-link" href="#what-changed-in-the-last-six-months">What changed in the last six months</a></h2>
<p>That is where the newer version of AI DevKit feels different.</p>
<p>The evolution was not just adding more commands. It was moving from commands and templates into <a href="https://github.com/codeaholicguy/ai-devkit/tree/main/skills">skills, memory, auto-triggered behavior, and automatic verification</a>.</p>
<p>That shift sounds small on paper. In daily use, it changed a lot.</p>
<ul>
<li><a href="https://codeaholicguy.com/2026/01/31/ai-coding-agents-explained-rules-commands-skills-mcp-hooks/">Skills</a> made the workflow more reusable at the right level of abstraction. Instead of only saving a prompt, I could package a way of working.</li>
<li><a href="https://codeaholicguy.com/2026/01/24/i-use-ai-devkit-to-develop-ai-devkit-features/">Memory</a> helped the system carry forward context that I had already established before, so I did not need to keep restating the same engineering preferences or workflow rules.</li>
<li>Auto skill triggering reduced the need for me to remember exactly when to push the next step.</li>
<li><a href="https://github.com/codeaholicguy/ai-devkit/tree/main/skills/verify">Auto verification</a> made it harder for the process to silently skip the boring but important parts.</li>
</ul>
<p>That is the real upgrade. My current workflow is better at carrying context, verifying work, and keeping me from repeating myself.</p>
<h2 id="recent-development-with-ai-devkit"><a class="heading-link" href="#recent-development-with-ai-devkit">Recent development with AI DevKit</a></h2>
<p>A recent feature in AI DevKit made that difference very obvious to me.</p>
<p>I was building interactive skill selection for <code>ai-devkit skill add</code>.</p>
<p>For the context, previously, if you wanted to add a skill with ai-devkit CLI, you needed to know the exact skill name. If you did not know what was available in a registry, you had to search first, then copy the name, then run the add command. It worked, but it was clunky. Two steps when one should be enough.</p>
<p>The feature itself was small. If the user runs <code>ai-devkit skill add &#x3C;registry></code> without a skill name, the CLI should show an interactive multi-select list so they can choose what to install.</p>
<p>I opened <a href="https://openai.com/codex/">Codex</a> and gave one sentence of instruction.</p>
<blockquote>
<p>$dev-lifecycle new requirement: when user install skill <code>ai-devkit s kill add &#x3C;registry></code> but no skill name define, we will display an interactive selection for user to select the skill to install, the list of selection will be extract from the registry</p>
</blockquote>
<p>From there, the dev-lifecycle skill took over and moved through the phases: requirement, review requirement, review design, execute plan, update planning, check implementation, write tests, and code review.</p>
<p>A few months ago, I could have built the same feature with reusable commands and templates. But I would still be the one manually stitching the process together. I would need to remember which command came next, decide when to review, decide when to verify, and keep the workflow aligned in my head.</p>
<p>Now the workflow itself carries more of that load.</p>
<p>The system pulled in a memory I had stored earlier about CLI commands needing a non-interactive fallback for CI environments. I did not have to remember that rule in the moment. It was already there. It became part of the requirement conversation automatically.</p>
<p>That sounds small, but this is exactly the kind of thing that gets lost in normal AI sessions.</p>
<p>Then it scaffolded requirement, design, and planning docs. It created the artifacts that support the code.</p>
<p>Then it reviewed its own requirement output. Then it reviewed the design against the requirement. Then, during implementation, it updated the planning progress as tasks were completed. Then it checked the implementation back against the requirement and design. Then it wrote tests against the requirement, not just against the implementation.</p>
<h2 id="what-feels-more-convenient-now"><a class="heading-link" href="#what-feels-more-convenient-now">What feels more convenient now</a></h2>
<p>The convenience is not only that the AI writes code for me.</p>
<p>The convenience is that I do not have to keep pushing the workflow forward manually.</p>
<ul>
<li>I do not have to remember every next move.</li>
<li>I do not have to restate every important rule.</li>
<li>I do not have to rely on discipline alone to make verification happen.</li>
</ul>
<p>The workflow is becoming much better at carrying its own structure.</p>
<p>The whole session took under 30 mins from ideas to PR. For a feature that produced requirements, design, planning, implementation, verification, tests, and review artifacts, that still feels a bit wild to me. The artifact of this feature <a href="https://github.com/codeaholicguy/ai-devkit/blob/main/docs/ai/design/feature-skill-add-interactive-selection.md">can be found on GitHub</a>.</p>
<h2 id="what-still-needs-me"><a class="heading-link" href="#what-still-needs-me">What still needs me</a></h2>
<p>That does not mean it replaces judgment.</p>
<p>I still made product decisions during the implementation. I decided to keep the first version simple. I chose not to over-design the selection UI. I decided when a flat list was good enough for v1. The workflow drove the process, but I still drove the product and the tradeoffs.</p>
<p>I do not want AI to pretend to be a product engineer making judgment calls it does not understand. I want it to handle more of the repetitive engineering workflow around me so I can spend more time on the decisions that actually need me.</p>
<p>That is also why automatic verification matters so much.</p>
<p>Without verification, AI workflows feel productive right until they are not. The code looks ok. The output feels fast. Then later you realize the implementation drifted from the requirement, skipped an edge case, or quietly missed something obvious.</p>
<p>I have become more convinced that the real value in AI-assisted engineering is not just code generation. It is <a href="https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/">workflow discipline</a> made easier to sustain.</p>
<p>That is what I have been trying to push AI DevKit toward over the last six months.</p>
<h2 id="try-it-yourself"><a class="heading-link" href="#try-it-yourself">Try it yourself</a></h2>
<p>If you want to test this workflow yourself, here is the easiest way to start.</p>
<p>The fastest way to get the full setup is with the <a href="https://github.com/codeaholicguy/ai-devkit/blob/main/templates/senior-engineer.yaml">engineer template</a>, then run one command to install the dev-lifecycle skill, verify, tdd, debug, and everything else I use daily:</p>
<pre><code class="hljs language-bash">npx ai-devkit@latest init -t engineer.yaml
</code></pre>
<p>That <code>engineer.yaml</code> looks like this:</p>
<pre><code class="hljs language-yaml"><span class="hljs-attr">environments:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">cursor</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">claude</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">codex</span>
<span class="hljs-attr">phases:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">requirements</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">design</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">planning</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">implementation</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">testing</span>
<span class="hljs-attr">skills:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">dev-lifecycle</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">debug</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">verify</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">tdd</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">memory</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">capture-knowledge</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">simplify-implementation</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">registry:</span> <span class="hljs-string">codeaholicguy/ai-devkit</span>
    <span class="hljs-attr">skill:</span> <span class="hljs-string">technical-writer</span>
</code></pre>
<p>One command, all three agents (<a href="https://claude.com/product/claude-code">Claude Code</a>, Codex, and <a href="https://cursor.com/">Cursor</a>) are configured, all skills installed.</p>
<p>Then open your favorite agent (Claude Code, Cursor, or Codex), then type: “Use dev-lifecycle skill to build [your feature].” Watch what happens.</p>
<p>The code is open source: <a href="https://github.com/codeaholicguy/ai-devkit">github.com/codeaholicguy/ai-devkit</a></p>
<p>Docs: <a href="https://ai-devkit.com">ai-devkit.com</a></p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>Six months ago, I was mainly optimizing how to talk to the model better.</p>
<p>Now I care more about giving the model a workflow that can carry context forward, reuse proven skills, trigger the right behavior automatically, and verify work before I trust it too much.</p>
<p>A workflow system that stays useful across Cursor, Claude Code, Codex, and any other Coding Agent. Reduce the amount of repeated orchestration I need to do myself.</p>
<p>I still review the code and make the important decisions, and it is clearly better than where I started.</p>
<p>For me, that is the real evolution.</p>
<p>If your current AI workflow mostly helps you write code, but still makes you carry too much of the process in your head, that is exactly the gap I have been trying to close with AI DevKit.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>My Experience in Agentic Engineering</title>
      <link>https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/02/28/my-experience-in-agentic-engineering/</guid>
      <pubDate>Sat, 28 Feb 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>One year into agentic engineering: why discipline beats speed, how engineers shift from implementer to orchestrator, and why portable workflows compound.</description>
      <content:encoded><![CDATA[<p>It has been one year since I wrote about <a href="https://codeaholicguy.com/2025/03/13/how-much-faster-can-ai-actually-make-your-team/">how much faster AI can make a team</a>.</p>
<p>Back then, the conversation was about speed. Today, it is about discipline.</p>
<p>If you are still coding the same way you did two years ago, you are underperforming. Not because you are not capable. But because the leverage has moved.</p>
<p>AI did not just make us faster. It changed what matters.</p>
<blockquote>
<p>I read AI news almost every day. Every day there is something new, new model, new benchmark, new workflow. I try not to overhype or overreact. But if I’m honest, there is a bit of panic inside me.</p>
<p>For the first time in my career, I see machines doing things that I spent my whole…</p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://twitter.com/codeaholicguy/status/2022757297898844658?ref_src=twsrc%5Etfw">February 14, 2026</a></p>
</blockquote>
<h2 id="what-changed-in-one-year"><a class="heading-link" href="#what-changed-in-one-year">What changed in one year</a></h2>
<p>One year ago, using <a href="https://codeaholicguy.com/2026/01/10/claude-code-vs-cursor/">Cursor or Claude Code</a> already felt powerful. You describe a feature and get working code in minutes. That alone reshaped daily engineering work.</p>
<p>Today, multi-agent workflows are common. Structured reviews are expected. Running parallel agents in isolated worktrees no longer feels experimental.</p>
<p>You can let one agent draft a plan, another implement, and a third review. You can compare outputs from different models and choose deliberately instead of accepting the first answer.</p>
<p>In my day-to-day workflow with <a href="https://ai-devkit.com/">AI DevKit</a>, I usually start by brainstorming with Claude Code using the <a href="https://ai-devkit.com/docs/3-development-with-ai-devkit/"><code>/new-requirement</code> command</a>. Then I switch to Codex CLI to review both the requirement and the design through <code>/review-requirement</code> and through <code>/review-design</code>. Then, I move back to Claude Code for the implementation. It allows me to leverage the strengths of different tools and models, using each one where it performs best instead of forcing a single agent to handle everything.</p>
<p>I try to keep my agents busy. While I review the requirement with one agent, I let others handle implementation or review the design in parallel.</p>
<p>The tools evolved fast. But more importantly, the expectations evolved.</p>
<p>Speed is no longer the differentiator. Discipline is.</p>
<h2 id="engineer-a-vs-engineer-b"><a class="heading-link" href="#engineer-a-vs-engineer-b">Engineer A vs Engineer B</a></h2>
<p>Let me contrast two senior engineers.</p>
<p>Engineer A uses AI every day. He writes prompts, gets code, scans the diff, and merges. He is clearly faster than before. He ships more tickets per sprint. He feels productive.</p>
<p>Engineer B also uses AI every day. But his workflow looks different.</p>
<p>He starts from a helicopter view. The requirement is ambiguous at first. Instead of jumping to code, he lets the agent drive the clarification loop. He answers in chat. He pushes the agent to keep asking until edge cases, constraints, and trade-offs are explicit.</p>
<p>Engineer A edits the code directly when something looks off.</p>
<p>Engineer B communicates with the agent. He explains why it is wrong. He updates constraints. He adjusts the plan. He asks the agent to restate the decision before touching implementation. He treats the agent as a collaborator, not a code generator.</p>
<p>Engineer A trusts the output if it compiles and the happy path works.</p>
<p>Engineer B strengthens the tests. He asks the agent to generate a test suite, then adds edge cases around null inputs, boundary values, concurrency, and error handling. He forces the agent to explain why each test exists. He treats testing as the backbone, not a checkbox.</p>
<p>Both engineers are faster than they were two years ago.</p>
<p>This difference in behavior looks small. But it changes everything.</p>
<h2 id="the-discipline-gap"><a class="heading-link" href="#the-discipline-gap">The discipline gap</a></h2>
<p>AI amplifies your engineering discipline.</p>
<p>If your thinking is vague, AI produces vague structure at scale.</p>
<p>If your requirements are weak, AI generates confident but misaligned code.</p>
<p>If your review process is shallow, AI accelerates bugs.</p>
<p>The dangerous part is that both engineers think they are doing fine. Both are shipping. Both are faster than before. The difference only shows up months later.</p>
<p>The more I practice agentic workflows, the more I realize that testing becomes more important, not less.</p>
<p>When code generation becomes cheap, validation becomes the bottleneck.</p>
<p>In an agentic workflow, I recommend responding in chat. Let the agent drive the clarification loop. It will keep asking questions until the requirement is clear enough to move forward.</p>
<p>Requirements, design notes, and decisions are not documentation. They are execution inputs.</p>
<p>If the agent does something wrong, most of the time it is not about intelligence. <a href="https://codeaholicguy.com/2026/02/14/tokens-context-windows-and-why-your-ai-agent-feels-stupid-sometimes/">It is about context</a>.</p>
<p>Managing context is now part of the job.</p>
<p>Senior engineers must learn how to enrich context deliberately. And also how to keep it lean. Too little context creates mistakes. Too much context creates noise. Both require <a href="https://codeaholicguy.com/2025/01/04/why-ai-wont-replace-human-engineers-and-why-it-shouldnt/">judgment</a>.</p>
<h2 id="from-implementer-to-orchestrator"><a class="heading-link" href="#from-implementer-to-orchestrator">From implementer to orchestrator</a></h2>
<p>The role of a senior engineer quietly changed.</p>
<p>Before AI, your value often came from writing the hardest 500 lines in the system.</p>
<p>With AI, implementation cost drops. But the decision cost increases.</p>
<p>Now your value comes from defining the 50 lines that must never be wrong. The parts of the system where a small mistake creates long-term damage.</p>
<p>A senior engineer orchestrates both humans and machines.</p>
<p>You design the workflow. You define the phases. You decide when to branch into parallel agents. You decide when to compare multiple model outputs. You decide when to stop the agent and take over manually.</p>
<p>Multi-agent setups are powerful. You can run two models on the same task and pick the better solution. But this introduces cognitive load.</p>
<p>If you cannot clearly explain what each agent is responsible for, you are not orchestrating.</p>
<p>Context switching becomes a real skill. Energy management becomes part of engineering.</p>
<p>If you cannot hold the mental model of what each agent is doing and why, you lose control of the system.</p>
<h2 id="the-compounding-effect"><a class="heading-link" href="#the-compounding-effect">The compounding effect</a></h2>
<p>In the first month, the difference between Engineer A and Engineer B looks small.</p>
<p>Engineer A finishes a feature in one day. Engineer B also finishes in one day, maybe slightly slower because he invests in tests and structure.</p>
<p>After three months, Engineer B’s codebase is cleaner. Tests are stronger. Review loops are tighter. Decisions are documented as inputs, not afterthoughts.</p>
<p>After six months, Engineer B has reusable commands, templates, and structured phases. He can spin up new features with clarity. He can onboard agents faster. His feedback loops are consistent.</p>
<p>After twelve months, the gap compounds.</p>
<p>Engineer A is still faster than pre AI days. He can only focus on one problem at a time because his workflow is still single-threaded. He prompts, waits, reviews, merges, then moves to the next task. He was never trained to coordinate multiple agents running in parallel, so his output scales with his attention and hours, not with the number of agents he can control.</p>
<p>Engineer B is operating a system. He can scale the number of things he builds by increasing the number of agents he can clearly define, coordinate, and control.</p>
<p>The exponential effect does not come from typing speed. It comes from accumulated workflow leverage.</p>
<h2 id="why-workflow-must-be-portable"><a class="heading-link" href="#why-workflow-must-be-portable">Why workflow must be portable</a></h2>
<p>One more lesson I learned: do not couple your discipline to a single tool.</p>
<p>Cursor, Claude Code, Codex. They are interfaces. They will continue to evolve.</p>
<p>An orchestrator does not depend on one instrument.</p>
<p>If your process only works inside one UI, it is fragile. The moment the tool changes, your leverage resets.</p>
<p>Your workflow should be portable.</p>
<p>Structured phases. Reusable commands. Explicit review steps. Clear testing loops. Configurations that can move across environments.</p>
<p>Wherever the agent runs, your discipline should follow.</p>
<p>On your local machine. On a different laptop. On a teammate’s machine. Inside a CI pipeline. On a remote server. On the cloud.</p>
<p>This is one of the reasons I built <a href="https://github.com/codeaholicguy/ai-devkit">AI DevKit</a>. Not to lock into one model. But to standardize how I work with any agent.</p>
<p>The tool matters less than the workflow.</p>
<h2 id="the-uncomfortable-truth"><a class="heading-link" href="#the-uncomfortable-truth">The uncomfortable truth</a></h2>
<p>If you are still using AI as autocomplete, you are slowly left behind.</p>
<p>If you treat the agent as a faster junior engineer without redesigning your process, you are leaving leverage on the table.</p>
<p>Last year was about discovering speed.</p>
<p>This year is about building discipline.</p>
<p>Agentic engineering is not about clever prompts. It is about workflow design. It is about context management. It is about structured review. It is about testing as a first class citizen.</p>
<p>It is orchestration.</p>
<p>Rethink your workflow. Upgrade it.</p>
<p>Speed was only the beginning.</p>
<p>If this perspective resonates with you, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I share what I learn while building real systems with AI in the loop. You can also follow me on <a href="https://x.com/codeaholicguy/">X</a> or <a href="https://www.threads.com/@codeaholicguy">Threads</a> for more thoughts and ongoing experiments.</p>]]></content:encoded>
    </item>
    <item>
      <title>Tokens, Context Windows, and why your AI agent feels stupid sometimes</title>
      <link>https://codeaholicguy.com/2026/02/14/tokens-context-windows-and-why-your-ai-agent-feels-stupid-sometimes/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/02/14/tokens-context-windows-and-why-your-ai-agent-feels-stupid-sometimes/</guid>
      <pubDate>Sat, 14 Feb 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>How tokens and context windows make AI coding agents forget, drift, and hallucinate, plus techniques that keep Claude Code and Cursor sessions stable.</description>
      <content:encoded><![CDATA[<p>When an AI coding agent forgot a constraint, drifted halfway through a task, or confidently invented things that never existed, some of our first reactions are that “this model is not good enough”.</p>
<p>After using Cursor, Claude Code, and other AI coding agents daily for a long time, I changed my mind.</p>
<p>When a capable model feels dumb, the problem is often not intelligence. It is <a href="https://codeaholicguy.com/2025/01/18/reimagining-software-engineering-whats-next-for-engineers-for-the-ai-powered-future/">context</a>, <a href="https://codeaholicguy.com/2025/03/27/the-missing-piece-in-ai-adoption-for-organizations/">what you gave it, what you did not</a>, and what quietly fell out of the window.</p>
<p>This post is my experience with tokens and context windows, plus techniques to keep AI coding agents stable when tasks get long.</p>
<h2 id="tokens-and-context-windows"><a class="heading-link" href="#tokens-and-context-windows">Tokens and context windows</a></h2>
<p>A token is just a chunk of text that the model (such as Claude Opus or GPT Codex) processes.</p>
<ul>
<li>Sometimes it is a whole word.</li>
<li>Sometimes it is part of a word.</li>
<li>Sometimes it is punctuation.</li>
</ul>
<p>There are some good, <a href="https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them">detailed explanations</a> on the internet, so I won’t deep dive into how the tokenizer works. You do not need to know the exact tokenization rules. What matters is that everything becomes tokens in the end: code, comments, logs, chat messages, etc.</p>
<p>You also do not need to count them, but you do need to understand relative cost, for example:</p>
<ul>
<li>A short chat message is cheap.</li>
<li>A function or interface is moderate.</li>
<li>A full file is expensive.</li>
<li>A long stack trace or repeated logs are very expensive.</li>
</ul>
<p>The <a href="https://platform.claude.com/docs/en/build-with-claude/context-windows">context window</a> is the maximum number of tokens the model can hold in its head at the same time.</p>
<blockquote>
<p>Just a simple illustration for understanding how context window affect your ChatGPT conversation. <a href="https://t.co/Aqqwx1d7p8">https://t.co/Aqqwx1d7p8</a> <a href="https://t.co/51IUOHbIb6">pic.twitter.com/51IUOHbIb6</a></p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://twitter.com/codeaholicguy/status/1954912821235642745?ref_src=twsrc%5Etfw">August 11, 2025</a></p>
</blockquote>
<p>If you imagine the model as reading from left to right, the context window is the size of the table it can spread papers on. Once the table is full, new papers push old ones off the edge.</p>
<p>When something falls off the table, the model does not partially remember it. It is simply gone. From that point on, the model can only guess based on patterns it has seen before.</p>
<p>This is why agents can feel sharp at the beginning of a task and unreliable later. It is not that they get tired. It is that the table gets crowded.</p>
<h2 id="the-failures-you-keep-seeing"><a class="heading-link" href="#the-failures-you-keep-seeing">The failures you keep seeing</a></h2>
<p>These failures look different, but they usually share the same root cause: context pressure.</p>
<h3 id="forget"><a class="heading-link" href="#forget">Forget</a></h3>
<p>The agent stops following a rule you stated earlier.</p>
<p>Examples:</p>
<ul>
<li>You said do not change API shape and it changes it anyway.</li>
<li>You said use existing error types and it invents new ones.</li>
</ul>
<p>What is actually happening under the hood:</p>
<p>Early in the session, your constraint competes with very little. It has strong weight in attention. As the session grows, more tokens arrive, such as diffs, logs, partial refactors, explanations. The original constraint is still technically present, but it is now one small signal inside a much larger pool of text.</p>
<p>Even before the hard window limit is reached, attention dilution happens. The model “sees” the rule, but it no longer treats it as central.</p>
<h3 id="drift"><a class="heading-link" href="#drift">Drift</a></h3>
<p>The agent starts solving a different problem than the one you are solving.</p>
<p>Examples:</p>
<ul>
<li>It starts refactoring style instead of finishing the feature.</li>
<li>It optimizes performance when correctness is not done.</li>
</ul>
<p>What is actually happening under the hood:</p>
<p><a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">Language models are next-token predictors</a>. They optimize for locally coherent continuations. If the most recent context heavily features refactoring, performance discussion, or style comments, the model will naturally continue along that trajectory.</p>
<p>Drift is often a local optimum problem. The short-term pattern in context overpowers the long-term goal.</p>
<h3 id="hallucinate"><a class="heading-link" href="#hallucinate">Hallucinate</a></h3>
<p>The agent claims things exist that do not exist.</p>
<p>Examples:</p>
<ul>
<li>Referencing functions that are close but wrong.</li>
<li>Claiming files were modified when they were not.</li>
</ul>
<p>What is actually happening under the hood:</p>
<p>When grounding context is incomplete, the model does not return “I do not know” by default. It fills the gap with the most statistically plausible continuation. If you showed it a similar function earlier, it may reconstruct a nearby variant. If you mentioned a file pattern, it may assume another file exists.</p>
<p>This is not malicious behavior. It is pattern completion under uncertainty.</p>
<h2 id="why-this-happens-more-in-coding"><a class="heading-link" href="#why-this-happens-more-in-coding">Why this happens more in coding</a></h2>
<p>Coding work has high dependency density.</p>
<p>In normal chat, if one sentence is slightly wrong, the conversation still flows. In code, one missing import breaks compilation. One wrong assumption about a type breaks behavior. One renamed field silently corrupts data.</p>
<p>Coding agents juggle multiple representations simultaneously: source code, diffs, chat, logs, etc. Each of these is token-heavy. More importantly, they have different semantic roles.</p>
<p>When all of them are mixed in a long session, the signal-to-noise ratio drops. The model is not just reading text. It is trying to reconcile structure, behavior, and history at the same time. Even before hitting the hard context limit, attention gets fragmented. The agent may technically “see” the goal, but its attention is pulled toward the most recent diff or the loudest stack trace.</p>
<p>This is why long coding sessions degrade faster than normal chat. It is a challenging problem, and <a href="https://x.com/gdb/status/2000453104006025652">Codex</a> or <a href="https://cursor.com/blog/scaling-agents">Cursor</a> shared that their focus is improving the long coding session.</p>
<h2 id="techniques-that-actually-keep-agents-stable"><a class="heading-link" href="#techniques-that-actually-keep-agents-stable">Techniques that actually keep agents stable</a></h2>
<p>I think about context in three layers.</p>
<ul>
<li>Layer 1: the contract
<ul>
<li>This must never be lost.</li>
<li>This includes goals and non-goals, constraints, acceptance criteria, invariants that must hold.</li>
</ul>
</li>
<li>Layer 2: the working set
<ul>
<li>The minimum needed for the current step.</li>
<li>This includes the files you are editing, the interfaces it depends on.</li>
</ul>
</li>
<li>Layer 3: the noise
<ul>
<li>This is everything else.</li>
<li>This includes old attempts, long logs, unrelated files added just in case.</li>
</ul>
</li>
</ul>
<p>I will try to optimize the context to reduce the noise as much as I can, while keeping the contract as simple as I can.</p>
<h3 id="start-with-a-written-contract"><a class="heading-link" href="#start-with-a-written-contract">Start with a written contract</a></h3>
<p>I do it even if it feels redundant. A contract is not for the model. It is for stabilizing attention.</p>
<p>My hypothesis is that attention is relative. The model weighs tokens against each other. A concise contract placed at the top of the prompt anchors the distribution of attention. It keeps global intent visible even when local diffs and logs get noisy.</p>
<p>When drift happens, do not argue inside a polluted context. I will restate the contract cleanly and continue. If needed, restart with the contract at the top.</p>
<h3 id="chunk-by-intent-not-by-files"><a class="heading-link" href="#chunk-by-intent-not-by-files">Chunk by intent, not by files</a></h3>
<p>I do not run one giant session until done.</p>
<p>I will split by intent, this concept is <a href="https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/">applied exactly</a> in how I built <a href="https://ai-devkit.com/">AI DevKit</a>, with phase splitting.</p>
<ul>
<li>understand requirement</li>
<li>design approach and plan</li>
<li>implement minimal slice</li>
<li>add tests</li>
<li>polish</li>
</ul>
<p>Each chunk gets its own small working set and its own definition of done.</p>
<p>Large sessions fail because they mix planning, execution, debugging, and optimization in one attention pool. By chunking by intent, you reduce cognitive load for the model. Each phase has a narrower objective and fewer competing signals.</p>
<h3 id="convert-history-into-state"><a class="heading-link" href="#convert-history-into-state">Convert history into state</a></h3>
<p>When a session gets long, I will pause and ask the agent to produce a state snapshot:</p>
<ul>
<li>what changed</li>
<li>what is broken</li>
<li>what is next</li>
<li>what constraints still apply</li>
</ul>
<p>Then, I will start a new thread with:</p>
<ul>
<li>the minimal files for the next step</li>
<li>the contract</li>
<li>the snapshot</li>
</ul>
<p>Context compaction is an item in <a href="https://ai-devkit.com/roadmap/">AI DevKit roadmap</a>, and I hope that I can release it soon.</p>
<h3 id="treat-rules-as-context-compression"><a class="heading-link" href="#treat-rules-as-context-compression">Treat rules as context compression</a></h3>
<p>Rules exist so you do not repeat yourself.</p>
<p>They should encode stable invariants:</p>
<ul>
<li>naming conventions</li>
<li>architectural boundaries</li>
<li>error handling patterns</li>
<li>preferred libraries</li>
<li>etc</li>
</ul>
<p>Good rules are small, stable, encode conventions and invariants.</p>
<p>Bad rules are long, include task-specific details, change every session.</p>
<p>You can also leverage files like AGENTS.md or CLAUDE.md as stable instruction anchors.</p>
<p>Instead of repeating architectural principles, coding conventions, and workflow rules in every session, move them into a dedicated, version-controlled instruction file. These files act as persistent context contracts that the agent can load explicitly when needed.</p>
<h3 id="design-tool-outputs-for-context-efficiency"><a class="heading-link" href="#design-tool-outputs-for-context-efficiency">Design tool outputs for context efficiency</a></h3>
<p>Tool outputs still consume tokens, so I will prefer:</p>
<ul>
<li>structured data over text</li>
<li>summaries over raw logs</li>
</ul>
<p>This is also why I personally prefer CLI-style integrations over heavy MCP-style preload patterns for many workflows.</p>
<p>When you preload large tool responses into the conversation, you are paying the context cost upfront. The agent must carry that entire payload in its active window, even if it only needs a small portion of it.</p>
<p>A CLI model is different. The agent calls a command when it needs something. The command returns structured output to stdout. The agent consumes exactly what is required for the current step.</p>
<p>This is the spirit behind how I designed <a href="https://ai-devkit.com/docs/6-memory/">AI DevKit memory</a>. Memory is not preloaded into every session. It is a standalone CLI. The agent calls it when needed, gets structured, minimal output, and continues reasoning.</p>
<h3 id="use-multi-agent-workflows-deliberately"><a class="heading-link" href="#use-multi-agent-workflows-deliberately">Use multi-agent workflows deliberately</a></h3>
<p>Multi-agent setups multiply context consumption and coordination cost. You are not just paying in tokens. You are paying for reconciliation complexity.</p>
<p>Use parallel agents when uncertainty is high, and you want alternative approaches. For deterministic execution, a single agent with a stable context stack is usually more reliable.</p>
<p>For example, once the plan is stable and the acceptance criteria are clear, I prefer one agent to execute end-to-end.</p>
<p>I do not split separate agents for backend, frontend, etc that stage. That kind of split increases coordination overhead and context duplication. Each agent needs a slightly different slice of the same contract, the same files, the same invariants. You end up paying the context cost multiple times and then reconciling their outputs.</p>
<p>Instead, I keep a single focused agent with a clean contract and a tight working set. It holds the intent consistently from implementation to tests to small refactors.</p>
<p>I only split agents when the problem space itself is separable. For example, different features that do not share tight coupling, or alternative solution explorations where diversity of approach is valuable.</p>
<h2 id="closing-thought"><a class="heading-link" href="#closing-thought">Closing thought</a></h2>
<p>AI tools made generation cheap.</p>
<p>They made information selection expensive.</p>
<p>When the agent feels dumb, do this</p>
<ol>
<li>Pause and assume context loss first.</li>
<li>Restate the contract.</li>
<li>Reduce the working set.</li>
<li>Ask for a state snapshot.</li>
<li>Continue in a fresh thread.</li>
</ol>
<p>If you learn to manage context deliberately, your agent will feel stable and consistent. Not because the model changed, but because your workflow did.</p>
<p>If you want to read more about software engineering in the AI era, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I will keep sharing what I learn while building systems with AI in the loop.</p>]]></content:encoded>
    </item>
    <item>
      <title>AI Coding Agents: Skills vs Commands vs Plugins vs Hooks Explained</title>
      <link>https://codeaholicguy.com/2026/01/31/ai-coding-agents-explained-rules-commands-skills-mcp-hooks/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/01/31/ai-coding-agents-explained-rules-commands-skills-mcp-hooks/</guid>
      <pubDate>Sat, 31 Jan 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>A clear mental model for AI coding agent building blocks: rules, commands, skills, sub-agents, MCP, hooks, and plugins, with examples and pitfalls.</description>
      <content:encoded><![CDATA[<p>If you are an engineer today, the challenge is no longer <a href="https://ownthecraftbook.com/chapters/5-why-this-matters-for-engineers-and-teams/">whether AI can help you write code</a>. The challenge is the flood of tools and concepts.</p>
<p>Every few weeks, there is something new. Agents, rules, skills, sub-agents, MCP, hooks. Each introduces its own vocabulary, and most explanations jump straight into features without explaining why any of these concepts exist in the first place.</p>
<p>So you try them. Some things feel powerful. Some feel magical for a moment. Then the setup starts to feel fragile and hard to reason about.</p>
<p>The problem is not that these concepts are bad. The problem is that they are rarely taught as parts of a system.</p>
<p>I went through the same confusion. The goal of this post is to give you a mental model you can apply regardless of the tool, so you know when a concept matters and when it is just noise.</p>
<h2 id="the-core-mental-model"><a class="heading-link" href="#the-core-mental-model">The core mental model</a></h2>
<p>An AI coding agent <a href="https://ownthecraftbook.com/chapters/9-the-new-engineering-workflow/">repeatedly does this</a>:</p>
<ol>
<li>Read persistent context</li>
<li>Interpret intent</li>
<li>Plan an approach</li>
<li>Generate or modify code</li>
<li>Optionally observe results</li>
<li>Iterate</li>
</ol>
<p>Every concept exists to control one specific step in this loop.</p>
<ul>
<li>Rules provide constraints.</li>
<li>Commands initiate execution.</li>
<li>Skills guide methodology selection.</li>
<li>Sub-agents limit responsibility.</li>
<li>MCP enables external interaction.</li>
<li>Hooks enforce guarantees.</li>
</ul>
<p>If two concepts try to control the same step, confusion follows.</p>
<h2 id="rules"><a class="heading-link" href="#rules">Rules</a></h2>
<p><a href="https://cursor.com/docs/context/rules">Rules</a> exist to separate constraints from execution. Without this separation, engineers keep encoding constraints inside prompts, and the agent’s behavior becomes unstable as soon as tasks change.</p>
<p>They are consulted by the model whenever it approaches a task, regardless of what the task is.</p>
<h3 id="what-it-is-in-practice"><a class="heading-link" href="#what-it-is-in-practice">What it is in practice</a></h3>
<p>Rules are persistent constraints that apply across all agent interactions, usually written as Markdown text.</p>
<p>They describe what must always be true, regardless of the task being executed. Rules live outside individual requests and do not change from task to task.</p>
<p>In practice, rules define boundaries rather than actions.</p>
<h3 id="what-it-is-good-for"><a class="heading-link" href="#what-it-is-good-for">What it is good for</a></h3>
<p>Rules are good for enforcing invariants.</p>
<p>They protect things you do not want the agent to renegotiate every time:</p>
<ul>
<li>architectural boundaries</li>
<li>dependency choices</li>
<li>folder structure</li>
<li>coding/testing conventions</li>
<li>backward compatibility expectations</li>
</ul>
<p>Once something is an invariant, it belongs in rules.</p>
<h3 id="example"><a class="heading-link" href="#example">Example</a></h3>
<p>To keep this grounded, we will use one example throughout.</p>
<p>You already have a Task API in production. Yesterday, you used an AI agent to add a new endpoint. Today, you want to add more features.</p>
<p>Two constraints matter:</p>
<ul>
<li>Existing behavior must not change.</li>
<li>Tests must keep passing.</li>
</ul>
<p>For the Task API, useful rules look like this:</p>
<ul>
<li>Validation logic must live in the domain layer</li>
<li>No new dependencies may be introduced</li>
<li>Existing test helpers must be reused</li>
<li>Public API behavior must not change without explicit instruction</li>
</ul>
<p>With these rules in place, prompts can stay short and focused on intent because constraints are no longer embedded inside them.</p>
<h3 id="common-misunderstanding"><a class="heading-link" href="#common-misunderstanding">Common misunderstanding</a></h3>
<p>The most common mistake is treating rules as behavioral coaching.</p>
<p>Rules like:</p>
<ul>
<li>Write clean code</li>
<li>Think carefully before coding</li>
<li>Refactor when appropriate</li>
</ul>
<p>do not define constraints. They force interpretation.</p>
<p>If a rule requires judgment to apply, it does not belong in rules. It belongs in a skill or in human review.</p>
<h2 id="commands"><a class="heading-link" href="#commands">Commands</a></h2>
<p><a href="https://cursor.com/docs/context/commands">Commands</a> exist to express execution intent. Unlike skills, which define how a class of problems should be solved, commands describe what specific action the agent should perform right now. They answer the question: what should the agent do now?</p>
<p>They are invoked explicitly by you and tell the model what task to execute now.</p>
<h3 id="what-it-is-in-practice-1"><a class="heading-link" href="#what-it-is-in-practice-1">What it is in practice</a></h3>
<p>A command is a named or explicit instruction, typically stored as Markdown or plain text, sometimes with parameters.</p>
<p>Commands are scoped to a single action and executed in the current context.</p>
<h3 id="what-it-is-good-for-1"><a class="heading-link" href="#what-it-is-good-for-1">What it is good for</a></h3>
<p>Commands are good for:</p>
<ul>
<li>executing a specific change</li>
<li>triggering a known workflow</li>
<li>keeping interaction fast and direct</li>
</ul>
<p>They work best when the intent is clear.</p>
<h3 id="example-1"><a class="heading-link" href="#example-1">Example</a></h3>
<p>Examples of commands are</p>
<ul>
<li>add input validation</li>
<li>update tests</li>
<li>create a new entity</li>
</ul>
<p>Commands should not describe architecture, style, or process. That belongs elsewhere.</p>
<h3 id="common-misunderstanding-1"><a class="heading-link" href="#common-misunderstanding-1">Common misunderstanding</a></h3>
<p>Commands are often overloaded with policy.</p>
<p>Long commands that explain how to structure code, where logic lives, and what tradeoffs to consider are compensating for missing rules or skills.</p>
<p>When commands get longer over time, it is a signal that execution is no longer the only problem.</p>
<h2 id="skills"><a class="heading-link" href="#skills">Skills</a></h2>
<p><a href="https://github.com/anthropics/skills">Skills</a> exist to separate methodology from execution. They capture how you solve a recurring class of problems.</p>
<p>They are consulted when the model recognizes a task as belonging to a known category.</p>
<h3 id="what-it-is-in-practice-2"><a class="heading-link" href="#what-it-is-in-practice-2">What it is in practice</a></h3>
<p>A skill is a reusable instruction set that teaches the agent a specific way of working. It is a bundle of instructions plus optional tools, usually defined as Markdown plus scripts or tool bindings.</p>
<p>It is not a one-off request. It is a repeatable method.</p>
<h3 id="what-it-is-good-for-2"><a class="heading-link" href="#what-it-is-good-for-2">What it is good for</a></h3>
<p>Skills are good for:</p>
<ul>
<li>capturing repeatable engineering patterns</li>
<li>reducing prompt repetition</li>
<li>improving consistency across tasks</li>
<li>encoding domain-specific practices once and reusing them everywhere</li>
<li>executing tools or scripts as part of a known method</li>
</ul>
<p>They remove methodology from commands and keep commands focused on intent.</p>
<h3 id="example-2"><a class="heading-link" href="#example-2">Example</a></h3>
<p>A validation skill might encode:</p>
<ul>
<li>how schemas are structured</li>
<li>how errors are returned</li>
<li>which edge cases must be tested</li>
</ul>
<p>When adding validation, the model applies this skill during execution.</p>
<h3 id="common-misunderstanding-2"><a class="heading-link" href="#common-misunderstanding-2">Common misunderstanding</a></h3>
<p>Skills are often created too early.</p>
<p>If something does not repeat, it does not deserve a skill.</p>
<h2 id="sub-agents"><a class="heading-link" href="#sub-agents">Sub-agents</a></h2>
<p><a href="https://code.claude.com/docs/en/sub-agents">Sub-agents</a> limit who is allowed to act on a task by explicitly constraining scope, context, and responsibility.</p>
<p>They exist to prevent a single agent from making broad, cross-cutting changes when the task itself only requires a narrow, well-defined area of action.</p>
<p>They are invoked when work is delegated instead of executed directly.</p>
<h3 id="what-it-is-in-practice-3"><a class="heading-link" href="#what-it-is-in-practice-3">What it is in practice</a></h3>
<p>A sub-agent is a scoped agent with a specific responsibility and limited context, usually expressed as Markdown with structured metadata.</p>
<p>The main agent decides when to delegate and what authority the sub-agent has.</p>
<h3 id="what-it-is-good-for-3"><a class="heading-link" href="#what-it-is-good-for-3">What it is good for</a></h3>
<p>Sub-agents are good for:</p>
<ul>
<li>controlling blast radius</li>
<li>separating concerns</li>
<li>preventing unintended cross-changes</li>
</ul>
<p>They reduce risk by narrowing responsibility.</p>
<h3 id="example-3"><a class="heading-link" href="#example-3">Example</a></h3>
<ul>
<li>Sub-agent A modifies validation logic only</li>
<li>Sub-agent B updates tests only</li>
</ul>
<p>Each sub-agent executes within its scope.</p>
<h3 id="common-misunderstanding-3"><a class="heading-link" href="#common-misunderstanding-3">Common misunderstanding</a></h3>
<p>Sub-agents are often framed as parallelism.</p>
<p>You rarely introduce sub-agents because you want things to run faster. You introduce them because a single agent handling too many responsibilities becomes unpredictable.</p>
<p>Their real value is containment: limiting what can change, where it can change, and why. Speed is a side effect at best, not the goal.</p>
<h2 id="mcp"><a class="heading-link" href="#mcp">MCP</a></h2>
<p>I have a separate, <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">longer post about MCP. Please check it out</a>.</p>
<p>Basically, it is invoked when the model needs real-world feedback instead of guessing. It also allows the agent to interact with systems.</p>
<h3 id="what-it-is-in-practice-4"><a class="heading-link" href="#what-it-is-in-practice-4">What it is in practice</a></h3>
<p><a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a> connects the agent to tools, environments, or data sources.</p>
<p>The model calls MCP tools to observe outcomes it cannot infer from text alone.</p>
<h3 id="what-it-is-good-for-4"><a class="heading-link" href="#what-it-is-good-for-4">What it is good for</a></h3>
<p>MCP is useful for:</p>
<ul>
<li>validating assumptions</li>
<li>running tests</li>
<li>reading failure output</li>
<li>querying external systems</li>
</ul>
<h3 id="example-4"><a class="heading-link" href="#example-4">Example</a></h3>
<ul>
<li>running the external test suite after changes</li>
<li>inspecting failing test output</li>
<li>interact with the browser for testing</li>
<li>query data from the database</li>
</ul>
<h3 id="common-misunderstanding-4"><a class="heading-link" href="#common-misunderstanding-4">Common misunderstanding</a></h3>
<p>MCP is often treated as intelligence.</p>
<p>It does not improve reasoning. It improves feedback.</p>
<h2 id="hooks"><a class="heading-link" href="#hooks">Hooks</a></h2>
<p><a href="https://code.claude.com/docs/en/hooks-guide">Hooks</a> exist to separate enforcement from reasoning. They run outside the LLM reasoning loop and are triggered regardless of what the model decides.</p>
<p>They guarantee that something always happens.</p>
<h3 id="what-it-is-in-practice-5"><a class="heading-link" href="#what-it-is-in-practice-5">What it is in practice</a></h3>
<p>A hook is a deterministic action triggered at a specific point in the workflow.</p>
<h3 id="what-it-is-good-for-5"><a class="heading-link" href="#what-it-is-good-for-5">What it is good for</a></h3>
<p>Hooks are good for:</p>
<ul>
<li>enforcing policies</li>
<li>running tests</li>
<li>formatting code</li>
<li>blocking unsafe changes</li>
</ul>
<h3 id="example-5"><a class="heading-link" href="#example-5">Example</a></h3>
<ul>
<li>run unit tests after file changes</li>
<li>block edits to protected directories</li>
</ul>
<h3 id="common-misunderstanding-5"><a class="heading-link" href="#common-misunderstanding-5">Common misunderstanding</a></h3>
<p>Hooks are sometimes used to encode decision logic.</p>
<p>If a hook needs reasoning, it belongs in a skill or sub-agent instead.</p>
<h2 id="how-this-fits-together"><a class="heading-link" href="#how-this-fits-together">How this fits together</a></h2>
<p>A stable setup looks like this:</p>
<ul>
<li>rules define constraints</li>
<li>commands express intent</li>
<li>skills capture repeated methodology</li>
<li>sub-agents control scope</li>
<li>MCP enables observation</li>
<li>hooks enforce guarantees</li>
</ul>
<p>You do not start with all of this.</p>
<p>Each layer exists because a specific problem appears.</p>
<h2 id="a-starter-kit-for-working-with-ai-coding-agents"><a class="heading-link" href="#a-starter-kit-for-working-with-ai-coding-agents">A starter kit for working with AI Coding Agents</a></h2>
<p>Once you understand the concepts above, a new problem usually appears.</p>
<p>You now know what rules, commands, skills, sub-agents, MCP, and hooks are for, but setting them up across different tools still takes time and cognitive energy. Each tool such as <a href="https://cursor.com/docs/get-started/quickstart">Cursor</a>, <a href="https://code.claude.com/docs/en/quickstart">Claude Code</a>, <a href="https://codelabs.developers.google.com/getting-started-google-antigravity">Antigavity</a>, <a href="https://developers.openai.com/codex/quickstart/">Codex</a>, <a href="https://opencode.ai/docs/">OpenCode</a>, etc has its own conventions, file locations, and wiring. The concepts are the same, but the setup friction is real.</p>
<p>This is the gap <a href="https://ai-devkit.com/">AI DevKit</a> is designed to close.</p>
<p>AI DevKit is a starter kit for engineers who want to work with any AI coding agent in a structured way, without rebuilding the same scaffolding every time. It gives you a ready-to-run foundation so you can focus on building software, not on repeatedly re-learning how to wire agent workflows.</p>
<p>You still decide the rules. You still design the skills. You still own the system. AI DevKit simply removes the accidental complexity so you can start from a sane baseline instead of a blank slate.</p>
<p><a href="https://github.com/codeaholicguy/ai-devkit">AI DevKit is open source</a>, and contributions are welcome from engineers who want to shape how practical AI agent workflows should look.</p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>Coding AI agents are not assistants. They are execution systems with explicit control layers.</p>
<p>They do not decide what is important, what is safe, or what should change. They execute within the structure you give them, and they fail in predictable ways when that structure is missing.</p>
<p>Once you understand when each layer is consulted and what form it takes, new tools stop feeling overwhelming and start feeling composable. The agent ultimately reflects your system design decisions, not your intent, and responsibility for correctness, safety, and clarity still sits with <a href="https://ownthecraftbook.com/chapters/15-you-still-own-the-craft/">you as the engineer</a>.</p>
<p>If you want to read more about software engineering in the AI era, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I will keep sharing what I learn while building systems with AI in the loop.</p>]]></content:encoded>
    </item>
    <item>
      <title>I use AI DevKit to develop AI DevKit features</title>
      <link>https://codeaholicguy.com/2026/01/24/i-use-ai-devkit-to-develop-ai-devkit-features/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/01/24/i-use-ai-devkit-to-develop-ai-devkit-features/</guid>
      <pubDate>Sat, 24 Jan 2026 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>What building AI DevKit&apos;s memory feature with AI DevKit taught me: why simple storage and smarter retrieval fall short, and why real usage matters.</description>
      <content:encoded><![CDATA[<p>I built <a href="https://ai-devkit.com/vision/">AI DevKit</a> because I wanted a workflow that makes AI coding feel less random and more efficient. But I also know from experience that AI looks great when you demo it. The problem only surfaces when you rely on them to ship something non-trivial.</p>
<p>I made a rule for myself. I would use <a href="https://ai-devkit.com/">AI DevKit</a> to build the features inside AI DevKit. If the workflow breaks, the product is not ready.</p>
<blockquote>
<p>About AI DevKit:</p>
<p>If you have not seen AI DevKit before, think of it as a way to work with AI without giving up control. Instead of treating AI as a black box that generate code, AI DevKit forces clear intent, explicit constraints, and tight feedback loops, so AI can execute well-defined work while humans stay responsible for direction and decisions. It can work well with any AI Coding Agents that you like, such as Cursor, Claude Code, Codex, Antigavity, OpenCode, etc.</p>
</blockquote>
<p>This post is a build log of that experience, and what it taught me about where AI helps and where humans still need to steer.</p>
<p>My goal is to turn <a href="https://ai-devkit.com/roadmap/">AI DevKit</a> into a tool that makes building with AI more effective through clearer intent and sharper inputs. By reducing the amount of steering engineers need to do, we can progressively hand off execution to AI, while humans stay focused on deciding what to build and defining the right constraints.</p>
<h2 id="the-problem-i-was-trying-to-solve"><a class="heading-link" href="#the-problem-i-was-trying-to-solve">The problem I was trying to solve</a></h2>
<p>Every time I asked an agent to implement something, I retyped the same rules:</p>
<ul>
<li>Always return Response DTOs for APIs.</li>
<li>Validate input at the boundary.</li>
<li>Follow our folder structure.</li>
<li>Avoid introducing new libraries without a reason.</li>
</ul>
<p>I could paste these into prompts, or even add them as rules, but neither felt right.</p>
<p>Rules tend to trigger only in very specific contexts, often tied to certain files or patterns. You cannot realistically cover every case that way. Adding more rules also does not scale well. It increases complexity and still leaves gaps.</p>
<p>Some of these rules are also personal preferences. They make sense for how I work, but I would not want to enforce them at a project level. Others are not really rules at all. They are knowledge about the product, its constraints, or the tradeoffs we have already made.</p>
<p>Prompts and rules both fall short here. Prompts are ephemeral and disappear after each task. Rules are rigid and incomplete. Neither is a good place to store evolving engineering knowledge that needs judgment and context.</p>
<p>What I wanted was much more mechanical:</p>
<ul>
<li>store small, precise rules once</li>
<li>retrieve them automatically when relevant</li>
<li>apply them consistently across tasks</li>
</ul>
<p>Not memory as chat history. Memory as engineering guidelines that actually get used.</p>
<p>That framing did not come from AI. It came from me being annoyed at repeating myself.</p>
<h2 id="first-assumption-memory-is-just-storage-and-retrieval"><a class="heading-link" href="#first-assumption-memory-is-just-storage-and-retrieval">First assumption: memory is just storage and retrieval</a></h2>
<p>At the beginning, I thought the feature would be straightforward.</p>
<ol>
<li>store something</li>
<li>search it later</li>
<li>inject it into context</li>
</ol>
<p>But once I started writing the requirements, I hit the first hard question.</p>
<p>What exactly are we storing?</p>
<p>If I store everything, retrieval becomes noisy. If I store long explanations, context gets polluted. If I store vague advice, the agent behaves vaguely.</p>
<p>This is where human steering mattered.</p>
<p>The agent was very good at expanding the scope. Chat history, flexible memory, richer schemas, future extensibility. All of it sounded reasonable.</p>
<p>But it did not answer the real question I had while working: What do I actually want the agent to remember while I am coding?</p>
<p>So I forced an explicit model:</p>
<ul>
<li>each memory item is one actionable rule</li>
<li>short title</li>
<li>content with rationale and optional examples</li>
<li>tags and scope to guide retrieval</li>
</ul>
<p>That was the moment where dogfooding mattered. I was not designing for a theoretical “memory feature”. I was designing for what I personally wanted the agent to recall in the middle of real work.</p>
<h2 id="second-assumption-smarter-retrieval-is-better"><a class="heading-link" href="#second-assumption-smarter-retrieval-is-better">Second assumption: smarter retrieval is better</a></h2>
<p>Once the memory model was clear, retrieval was the next decision.</p>
<p>The obvious modern answer these days is embeddings. The agent suggested it. It made sense technically.</p>
<p>But I kept asking a different question.</p>
<p>When a rule is retrieved, how do I know why it appeared?</p>
<p>If the system cannot explain itself, I cannot trust it. And if I cannot trust it, I will stop using it.</p>
<p>So I deliberately chose determinism over cleverness:</p>
<ul>
<li>SQLite with FTS5</li>
<li>BM25 as the primary ranking signal</li>
<li>title weighted higher than content</li>
<li>predictable boosts from tags and scope</li>
</ul>
<p>This was a tradeoff.</p>
<p>I accepted that lexical search is not as “smart” as embeddings. But I gained something more important for this product stage: repeatability. If I run the same query tomorrow, I want similar results. That is how you build trust in a tool that runs inside your development loop.</p>
<p>That judgment does not come from prompts. It comes from knowing how it feels to work inside a tool every day.</p>
<h2 id="using-the-tool-exposed-quality-problems"><a class="heading-link" href="#using-the-tool-exposed-quality-problems">Using the tool exposed quality problems</a></h2>
<p>Once I started “using the memory service while building the memory service”, problems showed up.</p>
<p>The biggest one was quality.</p>
<p>If you let vague knowledge in, the agent pulls it out later and behaves vaguely. If you let duplicate rules in, ranking gets worse, and context gets messy.</p>
<p>So I moved quality checks to write time:</p>
<ul>
<li>knowledge must be specific and actionable</li>
<li>generic advice is rejected</li>
<li>deduplication by normalized title and scope</li>
<li>deduplication by content hash</li>
<li>strict content length limits</li>
</ul>
<p>This is boring engineering work. But it is the difference between “a memory feature exists” and “memory actually helps”.</p>
<p>AI will happily generate more. Humans need to decide what is allowed.</p>
<h2 id="some-problems-only-appear-when-a-human-is-in-the-loop"><a class="heading-link" href="#some-problems-only-appear-when-a-human-is-in-the-loop">Some problems only appear when a human is in the loop</a></h2>
<p>A good example is the MCP inspector breaking because dev scripts printed logs to stdout.</p>
<p>From a technical perspective, nothing was wrong. The code worked. The server ran. From a usage perspective, the tool was broken.</p>
<p>The agent could not tell me this mattered. It could help debug it once I pointed it out, but it did not experience the failure.</p>
<p>Another example was integration tests failing because the SQLite connection was shared globally. The agent helped write the tests, but it did not anticipate how the global state would behave once everything was wired together.</p>
<p>Fixing this required stepping back, understanding how the system would actually be used, and redesigning test isolation. This was not about making the code more correct. It was about making the system safe to depend on. And it is the human responsibility.</p>
<p>This is a recurring pattern with AI-assisted development. AI helps you move faster once you see the problem. But a human still needs to notice what actually feels broken.</p>
<h2 id="what-this-build-changed-in-how-i-think-about-ai-tools"><a class="heading-link" href="#what-this-build-changed-in-how-i-think-about-ai-tools">What this build changed in how I think about AI tools</a></h2>
<p>My thoughts about AI tools stay the same, since the day I wrote “<a href="https://ownthecraftbook.com/"><strong>AI changes the Tools, you still own the Craft</strong></a>“:</p>
<blockquote>
<p>The best engineers of the future won’t just know how to use AI, they’ll know when not to.</p>
<p>So, as this era unfolds, don’t fear the tools. Learn them. Use them. Stretch them. Break them. But never forget: It’s your thinking, your judgment, your care that turns software into something worth shipping.</p>
<p><strong>The craft is still yours</strong>. Own it.</p>
</blockquote>
<p>By the end, the <a href="https://ai-devkit.com/docs/6-memory/">memory feature</a> looked very different from my initial idea.</p>
<p>It was smaller, stricter, and more boring. There is no fancy tech that was applied.</p>
<p>This build reinforced something I strongly believe.</p>
<p>AI can accelerate implementation. It cannot define intent, constraints, or quality on its own.</p>
<p>Humans still need to steer:</p>
<ul>
<li>what should exist</li>
<li>what should be rejected</li>
<li>which tradeoffs matter</li>
<li>when predictability beats capability</li>
</ul>
<p>Using AI DevKit to build AI DevKit features forced that discipline. It exposed assumptions I did not know I had. It turned fuzzy ideas into concrete constraints.</p>
<p>That is why I like dogfooding. It does not validate your ideas. It challenges them until they become real.</p>
<p><a href="https://github.com/codeaholicguy/ai-devkit">AI DevKit</a> is open source. If this way of working with AI resonates with you, <a href="https://github.com/codeaholicguy/ai-devkit/discussions">contributions</a>, experiments, and critical feedback are very welcome.</p>
<p>If you want to read more about software engineering in the AI era, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I will keep sharing what I learn while building systems with AI in the loop.</p>]]></content:encoded>
    </item>
    <item>
      <title>Claude Code vs Cursor</title>
      <link>https://codeaholicguy.com/2026/01/10/claude-code-vs-cursor/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2026/01/10/claude-code-vs-cursor/</guid>
      <pubDate>Sat, 10 Jan 2026 06:13:13 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Claude Code vs Cursor isn&apos;t about code quality but workflow shape: parallel CLI agents vs focused editor control, and why I built AI DevKit to use both.</description>
      <content:encoded><![CDATA[<p>I’ve seen a lot of <a href="https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/">Cursor</a> vs <a href="https://claude.com/product/claude-code">Claude Code</a> comparisons recently. Most of them try to answer a simple question: which one writes better code?</p>
<blockquote>
<p>As an engineer using both Cursor and Claude Code daily, I see no significant difference in the quality of code produced, the model output quality is mostly determined by how clearly and structured you plan + describe the task.</p>
<p>The one clear, practical advantage I observe with…</p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://twitter.com/codeaholicguy/status/2006021401212256626?ref_src=twsrc%5Etfw">December 30, 2025</a></p>
</blockquote>
<p>After using both tools daily, I think that question misses the point.</p>
<p>I don’t see a meaningful difference in code quality between Cursor and Claude Code anymore. Once you reach a certain baseline, the output quality is mostly determined by how clearly and structurally you plan the task, not which tool you use.</p>
<p>This post focuses more on my real-world experience. No benchmarks. No feature matrix. Just how these tools actually feel in real engineering work, as of today.</p>
<h2 id="the-real-difference-is-the-workflow-shape"><a class="heading-link" href="#the-real-difference-is-the-workflow-shape">The real difference is the workflow shape</a></h2>
<p>Here is the first thing I noticed over time.</p>
<ul>
<li>When the task is vague, both tools produce vague solutions.</li>
<li>When the task is structured, scoped, and explicit about constraints, both produce solid, reviewable code.</li>
</ul>
<p>I used to believe one tool was “smarter”; I thought Claude Code may be better because Anthropic owns it, and they know how to unlock the full potential of the model. That belief did not survive long.</p>
<p>Planning, decomposition, and clarity dominate everything else. The tool mostly amplifies whatever state you are already in.</p>
<p>Where the difference starts to show is not intelligence, but workflow shape.</p>
<ul>
<li>Claude Code nudges me toward exploration.</li>
<li>Cursor nudges me toward convergence.</li>
</ul>
<p>That difference sounds subtle, but it becomes very obvious once you feel it day after day.</p>
<h3 id="when-many-things-need-to-move-at-once"><a class="heading-link" href="#when-many-things-need-to-move-at-once">When many things need to move at once</a></h3>
<p>There is one mode where Claude Code consistently feels more natural to me: when I want to run multiple things in parallel.</p>
<p>This includes:</p>
<ul>
<li>Spawning several agents to explore different approaches</li>
<li>Running independent sub-tasks at the same time</li>
<li>Delegating background work that does not need constant supervision</li>
<li>Letting work progress asynchronously while I focus elsewhere</li>
</ul>
<p>This happens early in a project, but it also happens later. The key is not timing, but parallelism.</p>
<p>Claude Code shines when work can be decomposed and executed concurrently. The CLI and agent-first model makes it easy to treat AI as background workers, not just an interactive assistant. Exploration, testing, refactors, and well-scoped bug fixes all fit naturally here.</p>
<h3 id="when-understanding-and-control-matter"><a class="heading-link" href="#when-understanding-and-control-matter">When understanding and control matter</a></h3>
<p>There is another mode where Cursor consistently feels more friendly: when I need to slow down and focus.</p>
<p>If you ask me whether I would still choose Cursor without its AI features, the answer is no. If I do not need AI at all, I would go back to my favorite editor, Neovim, and review or edit the code myself. In this discussion, though, we are specifically talking about AI-powered editors. In that context, for this stage of the workflow, my preference is Cursor.</p>
<p>Whenever I need:</p>
<ul>
<li>Deep understanding of the codebase</li>
<li>Reviewing plans or generated changes</li>
<li>Diffing between versions</li>
<li>Explaining context to myself</li>
<li>Reading code carefully</li>
<li>Navigating files, symbols, and relationships</li>
</ul>
<p>Cursor feels better.</p>
<p>This can happen early or late in the lifecycle. Planning, design, and core implementation often fall into this category, even though they happen before implementation is complete. I want tight feedback loops, strong context, and deep visibility into the codebase.</p>
<p>Cursor helps me stay grounded. It is better suited for careful inspection, sense-making, and deliberate change.</p>
<h3 id="it-is-parallel-vs-focus"><a class="heading-link" href="#it-is-parallel-vs-focus">It is parallel vs focus</a></h3>
<p>With this framing, the pattern becomes clearer.</p>
<p>Some work benefits from parallelism. Some work benefits from focus.</p>
<p>Claude Code fits naturally when many things need to move at once. Cursor fits naturally when I need to slow down and understand.</p>
<p>Neither is objectively better. They are optimized for different modes of work, not different points on a timeline.</p>
<h2 id="why-i-built-ai-devkit"><a class="heading-link" href="#why-i-built-ai-devkit">Why I built AI DevKit</a></h2>
<p>This way of thinking is also the motivation behind why I built <a href="https://ai-devkit.com/">AI DevKit</a>.</p>
<p>As I started using both Cursor and Claude Code across different phases, I realized the real problem was not switching tools. The problem was losing structure and context when switching.</p>
<p>Planning lived in one place. Implementation lived in another. Testing and bug fixes lived somewhere else. Every pause or context switch added friction, not because of the tools, but because the work itself was not structured clearly enough.</p>
<p>AI DevKit is my attempt to solve that at a workflow level.</p>
<p>I treat software work as a set of explicit phases: planning, design, implementation, review, testing, and bug fixing. Each phase benefits from different levels of human attention and different tools.</p>
<p>Planning, design, and core implementation still need a lot of deliberate thinking from me. This is where I want strong context, tight feedback loops, and deep visibility into the codebase.</p>
<p>Testing, validation, and many bug fixes are different. Once intent is clear, these tasks are more mechanical. In those phases, I am often comfortable delegating almost fully to background AI execution. Claude Code feels stronger there today, especially when I can spin up focused tasks in your terminal and let them run asynchronously while I work on something else (I will talk more about how I set up my terminal to orchestrate different Claude Code processes in another post). This is where velocity really improves. With a clear plan, we can let multiple tasks run in parallel instead of doing everything sequentially. The harder part is not execution, but managing context switching. As engineers, we need to deliberately design our workflows, documentation, and handoffs so we can move between tasks and tools without losing intent or quality.</p>
<p>This is only my perspective. Different engineers will draw the line differently. Some prefer to stay hands-on everywhere. Some trust AI much earlier. There is no single correct split.</p>
<p>What AI DevKit gives me is a structured, portable context. Plans, constraints, assumptions, and decisions are written down in a way that can travel across tools. I can pause a task in one environment, pick it up in another, and still know exactly which phase I am in and what I am trying to achieve.</p>
<p>Once that structure exists, tool choice becomes simple. I just ask one question: which phase am I in right now? The answer usually tells me which tool fits best.</p>
<h2 id="why-cli-first-tools-matter-for-the-future"><a class="heading-link" href="#why-cli-first-tools-matter-for-the-future">Why CLI-first tools matter for the future</a></h2>
<p>There is one more angle that is worth calling out explicitly.</p>
<p>Personally, I think many top AI companies are pushing CLI-first experiences for a reason. It is not about power users or nostalgia. It is about integration.</p>
<blockquote>
<p>Personally, I think top AI companies are pushing for CLI because they want to push for more integration where coding agents are integrated in CI/CD pipeline so some of the engineering workflows can be delegated to AI in background.</p>
<p>— Hoang Nguyen (@codeaholicguy) <a href="https://twitter.com/codeaholicguy/status/1956863220268110164?ref_src=twsrc%5Etfw">August 16, 2025</a></p>
</blockquote>
<p>CLI makes it much easier to integrate coding agents directly into CI/CD pipelines. Once you cross that boundary, AI is no longer just an interactive assistant sitting next to you. It becomes something that can run in the background, triggered by events, delegated work, and operate without blocking a human.</p>
<p>That shift changes how engineering workflows scale.</p>
<p>This is already happening for us at ShopBack. We are pushing very hard on running Claude Code inside our CI/CD pipelines to do real work for us. It is effective in specific areas.</p>
<p>For example:</p>
<ul>
<li>Running targeted code changes with clear constraints</li>
<li>Delegating the inquiry support or issue tracing</li>
<li>Fixing well-scoped bugs with reproducible steps</li>
</ul>
<p>These are workflows where intent is already clear and the work is largely mechanical. Claude Code fits naturally here because it can run asynchronously, without UI friction, and without waiting for a human to sit in front of an editor.</p>
<p>This is a real advantage compared to Cursor.</p>
<p>Cursor shines when a human is actively involved. It is excellent for thinking, reviewing, navigating diffs, and shaping changes interactively. But it is not designed to own an execution environment in the cloud, check out code, read internal documents, and run as part of a pipeline without a human in the loop.</p>
<p>With Claude Code, we can own the environment. The agent can check out the repository, read documentation, apply changes, run validations, and report results, all inside infrastructure we control. There is no need to expose the codebase to another hosted editor or hook it into a separate cloud service just to make that possible.</p>
<p>That distinction matters once you start delegating work fully to AI in the background.</p>
<p>This does not mean everything should be automated. It does not mean engineers disappear. It means some parts of the workflow move out of the interactive loop entirely.</p>
<p>And that brings us back to phases again.</p>
<p>Early thinking, planning, and core design still demand human focus and tight feedback loops. Later, well-defined execution steps can increasingly be pushed into background agents running in CI/CD. Claude Code’s CLI-first nature makes that transition much easier today.</p>
<p>Again, this is not a universal truth. It is based on what we are seeing work in practice right now. Different teams and constraints will lead to different choices.</p>
<h2 id="stop-asking-which-tool-is-better"><a class="heading-link" href="#stop-asking-which-tool-is-better">Stop asking which tool is better</a></h2>
<p>I also see many engineers trying to standardize on a single “best” tool.</p>
<p>For individual engineers, I think that instinct is often wrong.</p>
<p>The more useful question is not which tool is better, but what you are doing right now.</p>
<ul>
<li>If you are thinking, exploring, and decomposing, tools that support parallel reasoning will feel faster.</li>
<li>If you are reviewing, validating, and refining, tools that keep you grounded in the codebase will feel safer.</li>
</ul>
<p>Strong engineers adapt tools to the phase instead of forcing the phase to fit the tool.</p>
<h2 id="a-note-on-time-context"><a class="heading-link" href="#a-note-on-time-context">A note on time context</a></h2>
<p>One last note on time context.</p>
<p>This is a snapshot of the current state. Both tools are evolving fast. Agent orchestration, context handling, and review workflows will keep changing. Some of these differences may narrow or even flip.</p>
<p>The durable insight is not which tool wins today. It is understanding why certain workflows feel better in certain environments.</p>
<h2 id="final-takeaway"><a class="heading-link" href="#final-takeaway">Final takeaway</a></h2>
<p>My personal takeaway is simple.</p>
<p>I do not force a full switch. I do not pick sides. I use the tool that fits the moment.</p>
<p>As long as I plan clearly and stay deliberate, both tools deliver more than enough. The real leverage comes from how I think, not which button I press.</p>
<p>If you feel torn between Cursor and Claude Code, you are probably asking the wrong question.</p>
<p>If you want to read more about software engineering in the AI era, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I will keep sharing what I learn while building systems with AI in the loop.</p>]]></content:encoded>
    </item>
    <item>
      <title>Prompts are becoming Code, but we still treat them like Strings</title>
      <link>https://codeaholicguy.com/2025/12/27/prompts-are-becoming-code-but-we-still-treat-them-like-strings/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/12/27/prompts-are-becoming-code-but-we-still-treat-them-like-strings/</guid>
      <pubDate>Sat, 27 Dec 2025 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Why prompts are becoming code that needs reuse, logic, and structured outputs, and how composable prompts with my promptfmt library keep them maintainable.</description>
      <content:encoded><![CDATA[<p>Prompts used to be just text.</p>
<p>You wrote a few sentences, pasted them into a chat box, tweaked the wording, and moved on. If the output was not good, you tried again. Nothing else depended on it. The cost of getting it wrong was close to zero.</p>
<p>That phase did not last long. In <a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">The Turning Point of AI</a>, I shared that:</p>
<blockquote>
<p>AI isn’t just a tool for completing your sentences or suggesting the next line of code. We need to see it as a new way of building software.</p>
</blockquote>
<p>The moment prompts moved out of chat windows and into those systems, their role changed. They were no longer throwaway text. They were reused across flows. They carried logic. They returned structured data that other parts of the system depended on.</p>
<p>At that point, prompts quietly became part of the system.</p>
<p>But the way we wrote them did not change. They were still plain text strings. Easy to write. Easy to paste. Easy to grow in the wrong direction.</p>
<h2 id="prompts-as-plain-text-and-the-pain-points"><a class="heading-link" href="#prompts-as-plain-text-and-the-pain-points">Prompts as plain text, and the pain points</a></h2>
<p>At first, this felt fine. A prompt was just a template string. Some variables. Maybe a bit of interpolation. It looked simple enough, and it worked for small use cases.</p>
<h3 id="pressure-1-reuse"><a class="heading-link" href="#pressure-1-reuse">Pressure 1: reuse</a></h3>
<p>The first pressure came from reuse.</p>
<p>The same prompt started powering multiple features. Copy-paste became the default strategy. Someone copied it to make a small change. Someone else added another instruction for a different flow. Over time, the prompt logic drifted.</p>
<p>No one could confidently say which parts were shared, which parts were safe to change, and which parts would break something else. Ownership of the structure slowly disappeared.</p>
<p>Nothing broke immediately. That was the dangerous part.</p>
<h3 id="pressure-2-logic"><a class="heading-link" href="#pressure-2-logic">Pressure 2: logic</a></h3>
<p>The second pressure came from logic.</p>
<p>As soon as prompts needed to behave differently based on context, teams usually took one of two approaches.</p>
<p><strong>Approach 1: branching in code and concatenating strings</strong></p>
<p>This pattern is common. The system decides which instructions to include, and the prompt is assembled step by step.</p>
<p>This works. The behavior is explicit. You can see exactly which rules apply in which case.</p>
<p>But the cost shows up as the system grows. Testability is weak because individual rules are hard to exercise in isolation. Maintainability suffers as strings grow and conditions multiply. Reuse often means copying text. A small change in one place can affect multiple flows.</p>
<p>Over time, reading this kind of prompt feels more like debugging than designing.</p>
<p>Example:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">buildPrompt</span>(<span class="hljs-params"><span class="hljs-attr">options</span>: {
  userType: <span class="hljs-string">"enterprise"</span> | <span class="hljs-string">"standard"</span>;
  environment: <span class="hljs-string">"prod"</span> | <span class="hljs-string">"staging"</span>;
}</span>) {
  <span class="hljs-keyword">let</span> prompt = <span class="hljs-string">`
Role
You are a senior QA engineer designing API test plans for production services.
`</span>;

  <span class="hljs-comment">// persona</span>
  prompt += <span class="hljs-string">`
Persona
<span class="hljs-subst">${
  options.userType === <span class="hljs-string">"enterprise"</span>
    ? <span class="hljs-string">"You are thorough and risk-aware. You prioritize reliability and compliance."</span>
    : <span class="hljs-string">"You are pragmatic and efficient. You prioritize high-signal coverage."</span>
}</span>
`</span>;

  <span class="hljs-comment">// input contract</span>
  prompt += <span class="hljs-string">`
Input
- userType: <span class="hljs-subst">${options.userType}</span>
- environment: <span class="hljs-subst">${options.environment}</span>
`</span>;

  <span class="hljs-comment">// steps</span>
  prompt += <span class="hljs-string">`
Steps
Step 1: Read the provided input and treat it as the contract.
Step 2: Derive test categories: happy path, validation, error handling, auth, rate limiting, idempotency.
Step 3: Generate test cases with realistic inputs and expected responses.
Step 4: Validate that every field in output strictly matches output schema.
`</span>;

  <span class="hljs-comment">// tasks</span>
  prompt += <span class="hljs-string">`
Tasks
1. Generate a test plan for the given API.
2. Return only JSON that matches output schema.
`</span>;

  <span class="hljs-comment">// constraints baseline</span>
  prompt += <span class="hljs-string">`
Constraints
- Do not include explanations or markdown.
- Do not output extra keys beyond output schema.
- Keep test cases safe for the given environment.
`</span>;

  <span class="hljs-comment">// guardrails</span>
  prompt += <span class="hljs-string">`
Guardrails
- Do not invent endpoints that are not implied by the API name and context.
- Do not include secrets or real tokens.
- If information is missing, leave a placeholder value and continue.
`</span>;

  <span class="hljs-comment">// conditional constraints</span>
  <span class="hljs-keyword">if</span> (options.<span class="hljs-property">environment</span> === <span class="hljs-string">"prod"</span>) {
    prompt += <span class="hljs-string">`
Constraints
- Avoid destructive test cases. Prefer read-only or safely reversible operations.
`</span>;
  }

  <span class="hljs-keyword">if</span> (options.<span class="hljs-property">userType</span> === <span class="hljs-string">"enterprise"</span>) {
    prompt += <span class="hljs-string">`
Constraints
- Include edge cases, rate limiting, and failure scenarios.
`</span>;
  }

  <span class="hljs-comment">// output</span>
  prompt += <span class="hljs-string">`
Output
- Return a single JSON object.
- JSON must match output schema exactly.
- Each test case must follow the schema: { name, description, request, expectedResponse }.
- Output only valid JSON.
`</span>;

  <span class="hljs-keyword">return</span> prompt;
}
</code></pre>
<p><strong>Approach 2: delegating logic to the LLM</strong></p>
<p>To avoid string concatenation, some teams push the branching logic into the prompt, describe the rules inside the prompt, and ask the model to apply them.</p>
<p>This looks cleaner at first. There is less code. Everything lives in one place.</p>
<p>But the trade-offs move elsewhere. Logic becomes implicit. Behavior depends on how the model interprets the rules. Testability drops. Debugging becomes guesswork. A small wording change can alter behavior in ways that are hard to predict or reproduce.</p>
<p>When this fails in production, it is often unclear why. Was a rule ignored? Was it interpreted differently? Did a small phrasing change shift the model’s behavior?</p>
<p>Example:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">buildPrompt</span>(<span class="hljs-params"><span class="hljs-attr">options</span>: {
  userType: <span class="hljs-string">"enterprise"</span> | <span class="hljs-string">"standard"</span>;
  environment: <span class="hljs-string">"prod"</span> | <span class="hljs-string">"staging"</span>;
}</span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-string">`
Role
You are a senior QA engineer designing API test plans for production services.

Persona
Adjust your behavior based on the following rules:
- If userType is enterprise, be thorough and risk-aware. Prioritize reliability and compliance.
- If userType is standard, be pragmatic and efficient. Prioritize high-signal coverage.

Input
You will receive:
- userType: <span class="hljs-subst">${options.userType}</span>
- environment: <span class="hljs-subst">${options.environment}</span>

Steps
Step 1: Read the provided input and treat it as the contract.
Step 2: Derive test categories: happy path, validation, error handling, auth, rate limiting, idempotency.
Step 3: Generate test cases with realistic inputs and expected responses.
Step 4: Validate that every field in output strictly matches the output schema.

Tasks
1. Generate a test plan for the given API.
2. Return only JSON that matches the output schema.

Constraints
- Do not include explanations or markdown.
- Do not output extra keys beyond the output schema.
- Keep test cases safe for the given environment.

Apply additional rules:
- If environment is prod, avoid destructive test cases. Prefer read-only or safely reversible operations.
- If userType is enterprise, include edge cases, rate limiting, and failure scenarios.

Guardrails
- Do not invent endpoints that are not implied by the API name and context.
- Do not include secrets or real tokens.
- If information is missing, leave a placeholder value and continue.

Output
- Return a single JSON object.
- JSON must match the output schema exactly.
- Each test case must follow the schema: { name, description, request, expectedResponse }.
- Output only valid JSON.
`</span>;
}
</code></pre>
<p>Some teams mix both approaches, but the underlying problems remain.</p>
<h3 id="pressure-3-structured-outputs"><a class="heading-link" href="#pressure-3-structured-outputs">Pressure 3: structured outputs</a></h3>
<p>The third pressure came from structured outputs.</p>
<p>Once prompts were expected to return structured data (ex: JSON) that fed directly into the next step of the system, failures became more visible. Parsing errors. Schema mismatches. Downstream crashes.</p>
<p>OpenAI introduced <a href="https://platform.openai.com/docs/guides/structured-outputs">structured outputs</a> and schema-based responses. Frameworks like the Vercel AI SDK added <a href="https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data">output validation and explicit error handling</a>, so invalid responses fail fast before reaching the next step.</p>
<p>These tools solved an important part of the problem. They made failures explicit and protected downstream systems.</p>
<p>But they focus on validating the output. They do not address how prompts themselves are structured or evolved over time.</p>
<h2 id="introducing-composable-prompts-with-promptfmt"><a class="heading-link" href="#introducing-composable-prompts-with-promptfmt">Introducing composable prompts with promptfmt</a></h2>
<p>Composable prompts are my attempt to address this gap.</p>
<p>Instead of treating a prompt as one growing string, you treat it as a composition of parts. Each part has a clear responsibility. Logic is explicit. Reuse is intentional.</p>
<p>This idea is not new.</p>
<p>SQL moved from raw strings to query builders so queries could be composed safely. HTML moved from templates to components so structure and logic could scale.</p>
<p>I believe prompts can follow a similar path.</p>
<p>I built <a href="https://github.com/codeaholicguy/promptfmt">prompfmt for experimenting with this idea</a>, the earlier prompt can be refactored without changing its behavior. What changes is the shape.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">PromptBuilder</span>, createCondition } <span class="hljs-keyword">from</span> <span class="hljs-string">"promptfmt"</span>;

<span class="hljs-keyword">function</span> <span class="hljs-title function_">buildPrompt</span>(<span class="hljs-params"><span class="hljs-attr">options</span>: {
  userType: <span class="hljs-string">"enterprise"</span> | <span class="hljs-string">"standard"</span>;
  environment: <span class="hljs-string">"prod"</span> | <span class="hljs-string">"staging"</span>;
}</span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PromptBuilder</span>()
    .<span class="hljs-title function_">role</span>(<span class="hljs-string">"You are a senior QA engineer designing API test plans for production services"</span>)
    .<span class="hljs-title function_">persona</span>(<span class="hljs-function">(<span class="hljs-params">params</span>) =></span> {
      <span class="hljs-keyword">if</span> (params.<span class="hljs-property">userType</span> === <span class="hljs-string">"enterprise"</span>) <span class="hljs-keyword">return</span> <span class="hljs-string">"You are thorough and risk-aware. You prioritize reliability and compliance."</span>;
      <span class="hljs-keyword">return</span> <span class="hljs-string">"You are pragmatic and efficient. You prioritize high-signal coverage."</span>;
    })
    .<span class="hljs-title function_">steps</span>([
      <span class="hljs-string">"Read the provided input and treat it as the contract"</span>,
      <span class="hljs-string">"Derive test categories: happy path, validation, error handling, auth, rate limiting, idempotency"</span>,
      <span class="hljs-string">"Generate test cases with realistic inputs and expected responses"</span>,
      <span class="hljs-string">"Validate that every field in output strictly matches output schema"</span>,
    ])
    .<span class="hljs-title function_">tasks</span>([
      <span class="hljs-string">"Generate a test plan for the given API"</span>,
      <span class="hljs-string">"Return only JSON that matches output schema"</span>,
    ])
    .<span class="hljs-title function_">constraints</span>([
      <span class="hljs-string">"Do not include explanations or markdown"</span>,
      <span class="hljs-string">"Do not output extra keys beyond output schema"</span>,
      <span class="hljs-string">"Keep test cases safe for the given environment"</span>,
    ])
    .<span class="hljs-title function_">guardrails</span>([
      <span class="hljs-string">"Do not invent endpoints that are not implied by the API name and context"</span>,
      <span class="hljs-string">"Do not include secrets or real tokens"</span>,
      <span class="hljs-string">"If information is missing, leave a placeholder value and continue"</span>,
    ])
    .<span class="hljs-title function_">constraints</span>({
      <span class="hljs-attr">condition</span>: <span class="hljs-title function_">createCondition</span>(
        <span class="hljs-function">(<span class="hljs-params">params</span>) =></span> params.<span class="hljs-property">environment</span> === <span class="hljs-string">"prod"</span>,
        <span class="hljs-string">"Avoid destructive test cases. Prefer read-only or safely reversible operations."</span>
      ),
    })
    .<span class="hljs-title function_">constraints</span>({
      <span class="hljs-attr">condition</span>: <span class="hljs-title function_">createCondition</span>(
        <span class="hljs-function">(<span class="hljs-params">params</span>) =></span> params.<span class="hljs-property">userType</span> === <span class="hljs-string">"enterprise"</span>,
        <span class="hljs-string">"Include edge cases, rate limiting, and failure scenarios."</span>
      ),
    })
    .<span class="hljs-title function_">output</span>([
      <span class="hljs-string">"Return a single JSON object"</span>,
      <span class="hljs-string">"Each test case must include: name, description, request, expectedResponse"</span>,
    ])
    .<span class="hljs-title function_">build</span>(options);
}
</code></pre>
<p>And these are the expected outputs.</p>
<pre><code class="language-text">// console.log(buildPrompt({userType: "enterprise", environment: "prod"));
Role
You are a senior QA engineer designing API test plans for production services

Persona
You are thorough and risk-aware. You prioritize reliability and compliance.

Steps
Step 1: Read the provided input and treat it as the contract
Step 2: Derive test categories: happy path, validation, error handling, auth, rate limiting, idempotency
Step 3: Generate test cases with realistic inputs and expected responses
Step 4: Validate that every field in output strictly matches output schema

Tasks
1. Generate a test plan for the given API
2. Return only JSON that matches output schema

Constraints
- Do not include explanations or markdown
- Do not output extra keys beyond output schema
- Keep test cases safe for the given environment

Guardrails
- Do not invent endpoints that are not implied by the API name and context
- Do not include secrets or real tokens
- If information is missing, leave a placeholder value and continue

Environment Constraints
Avoid destructive test cases. Prefer read-only or safely reversible operations.

Constraints
Include edge cases, rate limiting, and failure scenarios.

Output
Return a single JSON object
Each test case must include: name, description, request, expectedResponse

// console.log(buildPrompt({userType: "standard", environment: "staging")));
Role
You are a senior QA engineer designing API test plans for production services

Persona
You are pragmatic and efficient. You prioritize high-signal coverage.

Steps
Step 1: Read the provided input and treat it as the contract
Step 2: Derive test categories: happy path, validation, error handling, auth, rate limiting, idempotency
Step 3: Generate test cases with realistic inputs and expected responses
Step 4: Validate that every field in output strictly matches output schema

Tasks
1. Generate a test plan for the given API
2. Return only JSON that matches output schema

Constraints
- Do not include explanations or markdown
- Do not output extra keys beyond output schema
- Keep test cases safe for the given environment

Guardrails
- Do not invent endpoints that are not implied by the API name and context
- Do not include secrets or real tokens
- If information is missing, leave a placeholder value and continue

Output
Return a single JSON object
Each test case must include: name, description, request, expectedResponse
</code></pre>
<p>Structure becomes visible. Logic is explicit. Each rule exists as a first-class piece instead of being buried inside text. That makes prompts easier to change and safer to evolve.</p>
<p>Most importantly, prompts become more testable, maintainable, and predictable.</p>
<ul>
<li>Individual rules can be validated in isolation instead of only through end-to-end runs.</li>
<li>Adding a new condition does not require rewriting or copying large blocks of text.</li>
<li>Changes have clearer boundaries and fewer unintended side effects.</li>
</ul>
<p>These qualities are not optional in software engineering. They are the baseline that allows systems to scale.</p>
<p>But there are trade-offs.</p>
<p>Composable prompts add abstraction. They introduce a learning curve. They do not magically solve prompt quality.</p>
<p>There are also cases where this approach is unnecessary. One-off prompts. Prototypes. Low-risk flows that do not return structured data.</p>
<p>But for prompts that sit in the middle of real systems, composability aligns better with how the rest of the codebase evolves.</p>
<h2 id="closing"><a class="heading-link" href="#closing">Closing</a></h2>
<p>Prompts are no longer just text.</p>
<p>They are reused. They carry logic. They return structured data that other systems depend on. That makes testing, maintainability, and clarity important.</p>
<p>As prompts become a stable part of systems, tooling will follow.</p>
<p><a href="https://github.com/codeaholicguy/promptfmt">promptfmt</a> is one small experiment in that direction. It is early, and it should improve and evolve with real usage and feedback.</p>
<p>If this perspective resonates with you, <a href="https://github.com/codeaholicguy/promptfmt">check it out and contribute ideas or improvements</a>.</p>
<p>If you want to read more about software engineering in the AI era, <a href="https://codeaholicguy.substack.com/">subscribe to my blog</a>. I will keep sharing what I learn while building systems with AI in the loop.</p>]]></content:encoded>
    </item>
    <item>
      <title>The Illusion of Output</title>
      <link>https://codeaholicguy.com/2025/11/29/the-illusion-of-output/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/11/29/the-illusion-of-output/</guid>
      <pubDate>Fri, 28 Nov 2025 20:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>AI makes output cheap, but output isn&apos;t productivity. Why domain expertise, solving the right problem, and reviewing AI-generated code still matter.</description>
      <content:encoded><![CDATA[<p>AI makes it easy to produce output. You can <a href="https://codeaholicguy.com/2025/05/31/the-turning-point-in-ai/">generate code, designs, marketing ideas, user flows, or even a prototype in a few minutes</a>. It looks impressive on the surface. It also creates a trap. People start to believe output means productivity.</p>
<p>It does not.</p>
<p>Output is cheap now. Anyone can create something. The real question is whether that something solves the right problem. And this is where the gap becomes obvious.</p>
<p>I see this a lot in the way people talk about AI. You can find countless examples of “I built this in 10 minutes”. Some look exciting. But if you check the details, most of them do <a href="https://codeaholicguy.com/2025/06/07/great-ideas-arent-enough/">not solve a real user problem</a>. They are not reliable. They cannot run in real conditions. They <a href="https://codeaholicguy.com/2025/01/04/why-ai-wont-replace-human-engineers-and-why-it-shouldnt/">fall apart the moment you try to scale or integrate them</a>.</p>
<p>The same pattern shows up at work. People generate things quickly, but the output does not always move the problem forward. It creates noise rather than progress. People ask AI to generate code they cannot debug. Others create a dozen screens without understanding the flow. Some write long documents that do not clarify anything. It feels productive because something appears on the screen, but the output becomes a distraction.</p>
<p>Productivity has never been about the amount of output. Productivity is about solving the right problem with the most efficient solution. To do that, you need domain knowledge. You need context. You need experience making trade-offs. AI cannot do this part for you.</p>
<p><a href="https://codeaholicguy.com/2025/03/13/how-much-faster-can-ai-actually-make-your-team/">AI can help you move faster</a>. But only if you know where you are going. Without expertise, AI just accelerates confusion. You get more things, not better things. This is why strong practitioners get a clear advantage. They use AI to amplify their judgment. They ask better questions. They evaluate solutions. They know what good looks like. <a href="https://ownthecraftbook.com/chapters/8-where-ai-helps-and-where-it-doesnt/">AI becomes the leverage</a>.</p>
<p>Weak practitioners do not get the same effect. AI gives them more surface area to cover, more decisions to make, and more ways to make mistakes. Their lack of fundamentals becomes more visible. They ship faster, but also break things faster.</p>
<p>For non-engineers, this is important to understand. AI will not replace the need for expertise. It will not turn you into an engineer or a designer, or a product builder overnight. It reflects how much clarity you already have. If you do not understand the problem deeply, AI cannot fix that. It can only generate more output that you cannot evaluate.</p>
<p>There is also a common argument online comparing AI-generated code with compiled code from a compiler. They argue that if we do not review compiler output, we should not need to review AI output. They miss the key point. A compiler follows a deterministic algorithm. It transforms your code into machine code in a predictable and verifiable way. AI does not work like that. You cannot predict what AI will generate, and you cannot assume correctness from an LLM model.</p>
<p>Some people say, if the code can run, why do I need to check the code? While it’s true, running code only proves the happy path. It does not prove correctness, reliability, security, or long-term cost. It only shows that one specific scenario worked once. And in real systems, that is not enough.</p>
<p>The real multiplier is <a href="https://ownthecraftbook.com/chapters/15-you-still-own-the-craft/">still the same</a>. AI plus domain expertise. AI takes your strengths and scales them. It also takes your weaknesses and scales them. This makes the fundamentals even more important. Focus on your strength, don’t scale your weaknesses. Thinking clearly. Understanding problems. Making good decisions.</p>
<p>If you enjoy thoughts like this, follow me. I share what I learn as we navigate the AI era together.</p>]]></content:encoded>
    </item>
    <item>
      <title>Cursor 2.0 and Composer Model</title>
      <link>https://codeaholicguy.com/2025/11/01/cursor-2-0-and-composer-model-review/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/11/01/cursor-2-0-and-composer-model-review/</guid>
      <pubDate>Sat, 01 Nov 2025 01:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Hands-on review of Cursor 2.0: Composer model speed and code quality with and without AI DevKit, the multi-agent UI, code review, and the in-app browser.</description>
      <content:encoded><![CDATA[<p>Released on October 29, 2025, <a href="https://cursor.com/blog/2-0"><strong>Cursor 2.0</strong></a> is a major update to the AI-driven code editor that promises a new multi-agent interface and the debut of Cursor’s own coding model, <strong><a href="https://cursor.com/blog/composer">Composer</a></strong>.</p>
<p>As someone who uses Cursor every day, I couldn’t wait to try out the new 2.0 release. I spent some time exploring what’s new. Here’s my experience with the 2.0 release.</p>
<h2 id="composer-model"><a class="heading-link" href="#composer-model">Composer model</a></h2>
<p>One of the biggest highlights in Cursor 2.0 is <strong>Composer</strong>, the team’s first in-house AI coding model. Cursor describes it as a “frontier model” that delivers the same level of capability as others, but 4x faster. From my own testing and what others have shared, it definitely feels fast, making quick iterations smoother.</p>
<p>To see how well Composer performs, I tried the same test as <a href="https://codeaholicguy.com/2025/10/04/testing-claude-sonnet-4-5-gpt-5-codex-and-grok-code-fast-1-in-cursor-which-model-fits-best-for-development/">I did before with the frontier models</a>. But in this case, I will do it a bit differently, once is with a simple prompt of the requirement and once using <a href="https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/">my structured AI workflow</a> (<strong><a href="https://ai-devkit.com/">AI DevKit</a>,</strong> which adds planning and testing steps around the agent).</p>
<h3 id="without-ai-devkit"><a class="heading-link" href="#without-ai-devkit">Without AI DevKit</a></h3>
<p>Composer jumped straight into coding and finished in about <strong>2 minutes</strong>. The speed was amazing. I had working code almost immediately. However, the solution felt like a quick prototype.</p>
<p>The code was functional but bare-bones, lacking polish. I noticed I’d still need to tweak things (refactor some parts, add error handling, etc.) before considering it production-ready. It reminded me of the kind of output I’d get from <strong>Grok Code Fast 1</strong> from <a href="https://codeaholicguy.com/2025/10/04/testing-claude-sonnet-4-5-gpt-5-codex-and-grok-code-fast-1-in-cursor-which-model-fits-best-for-development/">the previous test</a>.</p>
<p>About the speed, it’s fast, but still a bit behind what <strong>Claude Sonnet 4.5</strong> performed in the previous test.</p>
<h3 id="with-ai-devkit"><a class="heading-link" href="#with-ai-devkit">With AI DevKit</a></h3>
<p>The process took longer overall (around <strong>8 minutes</strong> total: ~2 min planning, 4 min coding, 2 min testing), but the outcome was much more robust. Composer first spent time generating a plan (breaking down the task, clarifying requirements), then wrote the code, and finally ran tests/verification.</p>
<p>The resulting code quality was night-and-day compared to the quick run. It implemented features like lazy-loading of data, navigating between images via keyboard, included proper error handling.</p>
<p>The code structure was cleaner, and the agent’s own test run. In short, this more thorough run produced output on par with what <strong>GPT-5 Codex</strong> produced in the previous test, much closer to production-ready.</p>
<p>In summary, Composer is quite amazing in the speed, but to compare with other frontier models such as Claude Sonnet 4.5 or GPT-5 Codex, it still needs a bit more guidance. If you just need a quick prototype, letting it run freely gets you results in no time. But if you spend a bit more time, either manually or with a workflow such as <a href="https://github.com/codeaholicguy/ai-devkit">AI DevKit</a>, the quality jumps significantly, making the output solid enough for real development.</p>
<p>Since the <a href="https://cursor.com/docs/models#model-pricing">cost</a> of <strong>Composer</strong> is similar to <strong>GPT-5 Codex</strong>, I still think Codex is a better choice if putting them side by side. But if you prefer the speed, you can consider <strong>Composer</strong> over <strong>GPT-5 Codex</strong>.</p>
<p><strong>Output:</strong></p>
<p><strong>Without AI DevKit</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.24.48.png" alt="Gallery app generated by Composer without AI DevKit: a basic grid of photos with a zoom icon on hover" decoding="async" width="1023" height="655"></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.24.42.png" alt="Composer without AI DevKit: plain image modal showing one photo with a close button and image number" decoding="async" loading="lazy" width="1023" height="667"></p>
<p><strong>With AI DevKit</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-10.18.21.png" alt="Gallery app generated by Composer with AI DevKit: responsive photo grid with a focused, keyboard-selected image" decoding="async" loading="lazy" width="1023" height="644"></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-10.18.36.png" alt="Composer with AI DevKit: full-screen image viewer with previous/next arrows, photographer name, and resolution" decoding="async" loading="lazy" width="1023" height="655"></p>
<p><strong>Line of code</strong>:</p>
<p><strong>Without AI DevKit</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.51.23.png" alt="cloc output for the Composer run without AI DevKit: 7 files and 202 lines of code" decoding="async" loading="lazy" width="1023" height="417"></p>
<p><strong>With AI DevKit</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.52.02-1.png" alt="cloc output for the Composer run with AI DevKit: 12 files and 386 lines of code" decoding="async" loading="lazy" width="1022" height="480"></p>
<h2 id="multi-agents"><a class="heading-link" href="#multi-agents">Multi-Agents</a></h2>
<p>Personally, I think the new UI in Cursor 2.0 isn’t a big change. The <strong>agent view</strong> and <strong>editor view</strong> still look familiar, the main difference is the layout and where the panels are placed. We could already run multiple chats at the same time before this update, so the multi-agent interface isn’t entirely new, it’s just presented more clearly.</p>
<p><strong>Agent UI</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.44.41.png" alt="Cursor 2.0 Agent view with the agent list, a Composer 1 chat running /execute-plan, and a review panel" decoding="async" loading="lazy" width="1024" height="640"></p>
<p><strong>Editor UI</strong></p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.46.26.png" alt="Cursor 2.0 Editor view with the file explorer, a review tab of changes, and the agent chat on the right" decoding="async" loading="lazy" width="1024" height="640"></p>
<p>Under the hood, it uses git worktrees to give each agent its own isolated workspace so they don’t step on each other’s changes. In practice, this meant I could spin up a couple of agents to tackle different features (or the same feature with different approaches) without worrying about merge conflicts. The next problem is that “Does your laptop have enough resources to run multiple agents at the same time?”.</p>
<p>Functionally, running parallel agents worked smoothly for me. The UI allowed me to switch between each agent’s conversation and see their progress. One capability the Cursor team highlighted is that you can even have multiple models attempt the exact same prompt in parallel, then compare the results and pick the best solution. For example, you might run Composer alongside an OpenAI GPT-5 Codex agent, maybe even a Claude model, all tackling your feature request simultaneously. The idea is that for tough problems, this increases the chance that at least one agent comes up with a great solution. It is pretty cool to watch different AIs coding in parallel and then cherry-pick the winner or even combine insights.</p>
<p>That said, I’m not sure how often I’d use multi-model parallelism in everyday development. It definitely consumes more tokens/compute (since you’re effectively paying for two or three models to do the job of one), which might not be justified for routine tasks. I see it as a powerful feature for benchmarking model quality or tackling a really difficult problem where you want extra assurance. For most day-to-day feature builds, a single capable model usually suffices.</p>
<p>Overall, the new UI is cleaner and a bit more convenient, but it doesn’t fundamentally change how you work with Cursor.</p>
<h2 id="other-highlights"><a class="heading-link" href="#other-highlights">Other highlights</a></h2>
<h3 id="code-review"><a class="heading-link" href="#code-review">Code review</a></h3>
<p>One improvement I did notice is that all file changes are now grouped into a single review tab. In earlier versions, you had to open each file separately to see what the agent changed. Now, you can review everything in one place, similar to how we check diffs on GitHub or GitLab. It’s a small change, but it makes reviewing easier and more natural.</p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-09.44.41-2.png" alt="Cursor 2.0 Agent view with the single Review tab highlighted, grouping all of the agent&#x27;s file changes" decoding="async" loading="lazy" width="1024" height="640"></p>
<h3 id="in-app-browser"><a class="heading-link" href="#in-app-browser">In-app Browser</a></h3>
<p>I was really excited about the new built-in browser/DOM inspector tool.</p>
<p><img src="/images/2025/10/screenshot-2025-10-30-at-10.27.46.png" alt="Cursor 2.0 in-app browser previewing the gallery app beside an agent chat adding hover info to image cards" decoding="async" loading="lazy" width="1024" height="640"></p>
<p>Cursor 2.0 now has a native browser panel that the agent can use to run, test the app, and also forward DOM information to the agent within the editor. In earlier versions, I had to set up a separate <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">Model Context Protocol (MCP)</a> server to let the AI agent interface with a browser for similar thing. This feature will significantly help front-end developers.</p>
<p>Other than that, Cursor 2.0 introduced a Voice Mode, which lets you control the coding agent with speech commands. Using built-in speech-to-text, you can speak your prompt or instructions, and even use custom trigger words to tell the agent when to start coding. I gave this a brief try, and to be honest, I don’t see how it is useful. Personally, I prefer the silence of typing and thinking through the problem. In my workflow, I often need to pause and consider output, tweak a prompt, etc., and doing that out loud felt a bit awkward.</p>
<h2 id="final-take"><a class="heading-link" href="#final-take">Final take</a></h2>
<p>After a few days of playing with Cursor 2.0, my overall impression is positive. Cursor 2.0 feels like a solid improvement rather than a big leap.</p>
<p>Composer is a good model, but it will need some time to catch up with other frontier models. Composer is fast, and when used with a structured workflow, it can produce reliable, production-ready code.</p>
<p>The review flow is easier to follow, but most of the core experience remains familiar. Multi-agent mode and the in-app browser are interesting additions.</p>
<p>Overall, it’s a nice upgrade that makes Cursor more polished and practical for everyday development.</p>]]></content:encoded>
    </item>
    <item>
      <title>A retrospective on AWS outage 2025</title>
      <link>https://codeaholicguy.com/2025/10/25/a-retrospective-on-aws-outage-2025/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/10/25/a-retrospective-on-aws-outage-2025/</guid>
      <pubDate>Sat, 25 Oct 2025 02:00:00 GMT</pubDate>
      <category>Software Engineering</category>
      <description>A retrospective on the 2025 AWS outage and resilience strategies: multi-AZ and multi-region deploys, graceful degradation, backups, and DR drills.</description>
      <content:encoded><![CDATA[<p>It started when we discovered that some of our build processes were unable to run due to failures in <a href="https://www.docker.com/">Docker</a>.</p>
<p>My first thought was the usual: <em>“Is it us?”.</em> I checked <a href="https://www.dockerstatus.com/">Docker health</a> and found an incident from their side. Two minutes later, Twitter/X confirmed it wasn’t just us. <a href="https://www.cnbc.com/2025/10/20/amazon-web-services-outage-takes-down-major-websites.html">AWS was having a bad day</a>, and a big chunk of the internet came along for the ride.</p>
<p>This post is a short reflection on that incident. We were lucky, our systems run in a few different regions and were not directly connected to the broken services. But there were many lessons to learn.</p>
<h2 id="what-actually-went-down"><a class="heading-link" href="#what-actually-went-down">What actually went down</a></h2>
<p>On the <a href="https://health.aws.amazon.com/health/status?eventID=arn:aws:health:us-east-1::event/MULTIPLE_SERVICES/AWS_MULTIPLE_SERVICES_OPERATIONAL_ISSUE/AWS_MULTIPLE_SERVICES_OPERATIONAL_ISSUE_BA540_514A652BE1A">AWS website</a>, they shared that the outage started with a DNS failure in DynamoDB, triggered a chain reaction across key AWS systems (EC2, Lambda, SQS), and later exposed a deeper problem in AWS’s internal network monitoring. It impacted 141 AWS services and lasted about 15 hours before everything stabilized. The detailed post-event report can be found <a href="https://aws.amazon.com/message/101925/">here</a>. If you want to learn more about how the incident happened, Gergely Orosz has a <a href="https://newsletter.pragmaticengineer.com/p/what-caused-the-large-aws-outage">good deep dive in his newsletter, The Pragmatic Engineer</a>.</p>
<p>The incident affected thousands of companies. When AWS went down, it wasn’t just a tech issue, it stopped businesses in their tracks. A few hours offline can mean lost sales, missed deadlines, and frustrated customers. Every major outage reminds teams to take a hard look at how fragile their setup might be.</p>
<p>The incident also highlighted the trade-off we’ve made with the cloud. Using <a href="https://aws.amazon.com/">AWS</a>, <a href="https://azure.microsoft.com/">Azure</a>, or <a href="https://cloud.google.com/">Google Cloud</a> is fast, simple, and cheaper upfront compared to running your own data center. You don’t need to buy servers, manage power, or hire people to maintain racks. But the convenience comes with a cost: less control and higher dependency. When one cloud region fails, you wait for them to fix it. Running your own data center costs more in hardware and staff, yet you can decide how it’s built, tested, and recovered. Most teams choose the cloud because it speeds up development, removes the overhead of operation, but the AWS outage reminded us that saving time can mean giving up some control.</p>
<p>We also experienced several major incidents due to the Cloud provider in the past. I remember the Cloudflare outage we experienced. Our system was down because our DNS and CDN were tied to <a href="https://www.cloudflare.com/">Cloudflare</a>’s network, and when Cloudflare sneezed, everyone caught a cold. We realized we had no quick fallback; we just had to wait it out. Similarly, the AWS outage made companies large and small realize that “wait it out” shouldn’t be the only plan.</p>
<p>If your application, backend, databases, etc, are all tied to one provider and one region, that’s a single point of failure. And you can do it at your own risk. Being “production-ready” nowadays means being failure-tolerant by default.</p>
<p>So, what do we do about it? No cloud provider is perfect, AWS, Google, Microsoft, Cloudflare, you name it, they all have bad days. We can’t prevent outages, but we can prepare for them. Below are key strategies and techniques that we learned and implemented in our team to mitigate the impact of outages.</p>
<h2 id="key-strategies-for-resilience"><a class="heading-link" href="#key-strategies-for-resilience">Key Strategies for Resilience</a></h2>
<p>Here are approaches we’ve used and seen others use to stay online and recover quickly when cloud problems happen:</p>
<h3 id="deploy-across-multiple-availability-zones-and-regions"><a class="heading-link" href="#deploy-across-multiple-availability-zones-and-regions">Deploy across multiple Availability Zones and Regions</a></h3>
<ul>
<li>At a minimum, deploy your critical workloads in a multi-AZ setup (multiple data centers in one region) so an isolated data center failure won’t take you down. But given regional outages like this one, consider multi-region redundancy for truly critical systems.</li>
<li>Whether you do active-active (serving traffic from two regions at once) or active-passive (warm standby in a second region), having a Plan B region can turn a regional outage into a minor performance problem rather than a full shutdown.</li>
<li>Multi-region adds complexity (data replication, syncing configs, etc.) and cost, of course, but it increases resilience.</li>
</ul>
<h3 id="design-for-graceful-degradation"><a class="heading-link" href="#design-for-graceful-degradation">Design for Graceful Degradation</a></h3>
<ul>
<li>Not every part of your application needs to work 100% for the core experience to survive. Identify which features can fail gracefully. Decouple your architecture so that if one service or dependency goes down, it doesn’t drag everything with it.</li>
<li>In practice, this might mean using queues and async processing. If your database is having issues, perhaps you queue up writes for later, serve cached data, or switch to a read-only mode. The goal is to fail softly, users get a lighter version of your app instead of a blank error screen. We follow this approach in our app at ShopBack so that if our API gateway ever fails to respond for any reason, users can still access cached read-only data.</li>
</ul>
<h3 id="regular-backups-and-cross-cloud-backups"><a class="heading-link" href="#regular-backups-and-cross-cloud-backups">Regular Backups and Cross-Cloud Backups</a></h3>
<ul>
<li>Outages often remind us of the value of backups. Make sure you regularly back up your data and store those backups in a different region or even outside your current Cloud provider.</li>
<li>For example, you might periodically export critical data from your AWS database to an on-premises server or another cloud’s storage. That way, if AWS is down hard, you haven’t lost data, and you could even spin up a read-only instance elsewhere in a pinch. Define your RTO (Recovery Time Objective). Basically, how long you can afford to be down and how much data you can lose, and architect the infrastructure and system to meet those targets.</li>
<li>A case study of this is the cyberattack on one of the biggest stock exchanges in Vietnam in 2024. In that case, the company kept all its backups in the same cloud environment. When attackers gained access, they froze both the main system and the backups, making recovery impossible. It took them the whole week to recover. If those backups had been stored elsewhere, the outcome could have been very different.</li>
<li>The main downside of cross-cloud backups is cost and the extra effort needed to manage them.</li>
</ul>
<h3 id="disaster-recovery-drills"><a class="heading-link" href="#disaster-recovery-drills">Disaster Recovery Drills</a></h3>
<ul>
<li>When an outage hits, every minute counts. You don’t want to be clicking around the console, spinning up resources in a panic. Use <a href="https://learn.microsoft.com/en-us/devops/deliver/what-is-infrastructure-as-code">Infrastructure as Code</a> and automated scripts for your failover plans. Set up processes that can quickly spin up your systems in another region or on a different provider when something goes wrong.</li>
<li>Run Disaster Recovery Drills to practice this failover. For example, occasionally simulate “What if us-east-1 dies?” and see if your team can restore service from backups or switch DNS to your secondary site within your RTO window. Practice not only finds the gaps in your plan but also makes the real thing much less chaotic.</li>
<li>Don’t forget to test your communications during such drills, ensure the team can communicate even if your primary systems (or Slack/email) are down.</li>
<li>At ShopBack, we run this exercise every year and set tight goals for how fast we can rebuild our environment from scratch.</li>
</ul>
<h3 id="monitoring-and-observability-of-dependencies"><a class="heading-link" href="#monitoring-and-observability-of-dependencies">Monitoring and Observability of Dependencies</a></h3>
<ul>
<li>One sneaky lesson from outages is that sometimes services you didn’t think were critical turn out to be. In this case, many teams were surprised to find that even services running in other AWS regions or seemingly independent systems failed because they secretly depended on something in us-east-1.</li>
<li>We need to map out the dependencies. What happens if Service A can’t talk to Service B? Do you know all the external APIs, auth services, or resources that your stack relies on? Use monitoring tools to catch these hidden dependencies and set up alerts for any unusual latencies or failures in them. During normal times, this helps you catch issues early; during an outage, it can pinpoint which dependency is the weak link causing cascading failures.</li>
<li>Also, keep an eye on your external and third-party services. After several incidents involving them, at ShopBack, we set up a dedicated Slack alert channel to monitor the health of these integrations separately.</li>
</ul>
<h3 id="consider-hybrid-and-multi-cloud-approaches"><a class="heading-link" href="#consider-hybrid-and-multi-cloud-approaches">Consider Hybrid and Multi-Cloud Approaches</a></h3>
<ul>
<li>This is the big guns. Not every company can afford to do it. It’s expensive and requires extra effort and maintenance to manage well. “Multi-cloud” means using more than one cloud provider, while hybrid setups combine cloud services with your own on-premises servers. Both are often seen as ways to reduce reliance on a single provider.</li>
<li>They’re not trivial to implement, but for some businesses, they can be worth it. The goal is to run some of your systems on another provider or your own servers so that if one fails, the others keep working.</li>
</ul>
<p>Systems will have bugs, and incidents will happen. We can’t control when AWS or any provider has issues, but we can control our preparedness and response. The latest AWS outage reminded us of that. By learning from it and from our own war stories (yes, Cloudflare, I’m looking at you), we can keep our applications not just running on sunny days, but resilient through the storms.</p>
<p>Stay safe out there in the cloud! And next time AWS goes dark, may your systems stay comfortably in the light.</p>
<p>If you love my blogs, please subscribe for more engineering lessons on building reliable systems.</p>]]></content:encoded>
    </item>
    <item>
      <title>My Engineering Workflow in CursorAI</title>
      <link>https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/10/18/my-engineering-workflow-in-cursorai/</guid>
      <pubDate>Sat, 18 Oct 2025 02:00:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>My AI-accelerated engineering workflow in Cursor, from requirements and design to review and testing, when to use MCP, and why I built AI DevKit.</description>
      <content:encoded><![CDATA[<p>Every Software Engineer follows a similar rhythm:</p>
<blockquote>
<p>Understand requirement → Design → Plan → Implement → Review → Test → Deploy → Monitor.</p>
</blockquote>
<p>That hasn’t changed for a long time. When AI is involved in the workflow, what’s changed is the speed. I also <a href="https://ownthecraftbook.com/chapters/9-the-new-engineering-workflow/">mentioned this in my FREE eBook about adapting AI in Software Engineering</a>.</p>
<p>Before AI, a typical cycle for mid-sized features often stretched over several weeks. Each step involved back-and-forth reviews, context switching, and waiting for feedback. But with tools like Cursor (and yes, even Claude Code), the same workflow can now run faster. I mentioned this in the post <a href="https://codeaholicguy.com/2025/03/13/how-much-faster-can-ai-actually-make-your-team/">How much faster can AI actually make your team?</a>.</p>
<p>I’ve been experimenting with this new way of working for a while, the last time I talked about this was in my post about <a href="https://codeaholicguy.com/2025/04/12/what-i-learned-using-cursorai-every-day-as-an-engineer/">my learning after using CursorAI for 9 months</a>. Eventually, I decided to turn my setup into a reusable toolkit, I called it <a href="https://github.com/codeaholicguy/ai-devkit">ai-devkit</a>, so that I can share it with others and make AI-assisted workflows consistent across projects.</p>
<p>Let’s walk through how my engineering workflow actually looks today, step by step.</p>
<h2 id="engineering-workflow-before-ai"><a class="heading-link" href="#engineering-workflow-before-ai">Engineering Workflow (Before AI)</a></h2>
<p>Whether you’re at a startup or a big tech company, the core process looks like this:</p>
<ol>
<li><strong>Understand Requirement</strong>: Read the PRD or tickets, clarify what’s being asked.</li>
<li><strong>Review Requirement</strong>: Identify gaps, edge cases, or unclear assumptions.</li>
<li><strong>Design</strong>: Draft your architecture, data model, or API contract.</li>
<li><strong>Review Design</strong>: Discuss with peers or leads to validate approach.</li>
<li><strong>Plan</strong>: Break it into tasks, estimate effort, and define milestones.</li>
<li><strong>Implement</strong>: Write the code, debug, and document.</li>
<li><strong>Review Implementation</strong>: Peer review for quality and maintainability.</li>
<li><strong>Testing</strong>: Unit, integration, or E2E coverage before shipping.</li>
<li><strong>Deploy &#x26; Monitor</strong>: Release safely and track metrics or logs.</li>
</ol>
<p>Each phase produces something tangible: a document, a design, a Pull Request (PR) or Merge Request (MR), or a deployment. Every phase waited for human feedback or context setup, so it could take weeks or months for even a mid-sized feature.</p>
<h2 id="ai-accelerated-workflow"><a class="heading-link" href="#ai-accelerated-workflow">AI-Accelerated Workflow</a></h2>
<p>To make this section easier to navigate, I’ve broken it down into two parts: Feature development and Understanding existing code.</p>
<h3 id="feature-development"><a class="heading-link" href="#feature-development">Feature development</a></h3>
<p>The workflow itself didn’t change, we just execute it differently now. I personally use Cursor as the default editor and tried to tighten the workflow with it, although I was a Neovim user for many years.</p>
<p>I use <a href="https://cursor.com/docs/agent/chat/commands">Cursor Commands</a> to create reusable workflows so that I don’t need to repeat myself.</p>
<p>You can create commands yourself. Or you also can easily scaffold the working environment with <code>npx ai-devkit init</code>. You can choose your environment, such as Cursor or Claude Code, so that it will set up the files accordingly.</p>
<p>Here’s how it looks with <code>ai-devkit</code>:</p>
<ol>
<li><strong>Understand Requirement</strong>
<ul>
<li>In the Cursor AI Chat (or Claude Code), use the <code>/new-requirement</code> command to summarize goals, constraints, and success metrics.</li>
<li>This prompt will try to highlight what’s unclear and what assumptions you’re making.</li>
</ul>
</li>
<li><strong>Review Requirement</strong>
<ul>
<li>After finishing running <code>/new-requirement</code> commands, you now have all your required documents in <code>docs/ai/</code>. At this stage, we will review the files so that we won’t miss anything. I usually manually review this so that I will stay on top of the work.</li>
<li>Ask Cursor to review the requirement by using the command <code>/review-requirement</code>.</li>
<li>The output helps spot what the original specs might have missed.</li>
</ul>
</li>
<li><strong>Design</strong>
<ul>
<li>When running <code>/new-requirement</code> it will also propose a design in <code>docs/ai/design</code>.</li>
<li>I often generate multiple options, compare trade-offs, and refine manually.</li>
</ul>
</li>
<li><strong>Review Design</strong>
<ul>
<li>To support this, I have a command <code>/review-design</code> so that Cursor can act like a simulated peer reviewer, which challenges design decisions and reminds me about different things that I overlooked.</li>
</ul>
</li>
<li><strong>Plan</strong>
<ul>
<li>Convert the design into concrete steps and a checklist.</li>
</ul>
</li>
<li><strong>Implement</strong>
<ul>
<li>This is where AI tools such as Cursor or Claude Code shine.</li>
<li>Whenever we finish reviewing all the design and plan documents, we can run <code>/execute-plan</code> the tool will pick up the tasks one by one and execute them.</li>
<li>Whenever the implementation is done, you can run <code>check-implementation</code> to make sure the code follows the requirements.</li>
<li>You will need to manually review the code with the support of <code>/code-review</code>, so that you still own the quality of the code.</li>
</ul>
</li>
<li><strong>Testing</strong>
<ul>
<li>Run <code>/writing-test</code> to create unit and integration tests targeting high coverage.</li>
</ul>
</li>
<li><strong>Deploy &#x26; Monitor</strong>
<ul>
<li>Cursor helps generate release notes, deployment steps, monitoring metrics, and alerts.</li>
</ul>
</li>
</ol>
<p>I still think through the same steps, but AI keeps the flow unbroken. It helps me stay in problem-solving mode longer.</p>
<h3 id="understanding-existing-code"><a class="heading-link" href="#understanding-existing-code">Understanding existing code</a></h3>
<p>A big part of coding is understanding the code that’s already there. For years, reading and making sense of existing code has been one of the hardest things, especially for newer engineers.</p>
<p>Now, AI can help speed up that process. It can describe what a piece of code is doing, show you where it’s being used, and help you understand how different parts fit together.</p>
<p>With <code>ai-devkit</code>, you can use <code>/capture-knowledge</code> command helps you understand how existing code works by analyzing it from any entry point and generating comprehensive documentation with visual diagrams.</p>
<h2 id="know-when-to-use-mcp"><a class="heading-link" href="#know-when-to-use-mcp">Know when to use MCP</a></h2>
<p>When working inside Cursor or Claude Code, not everything needs to run through a <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">Model Context Protocol (MCP)</a> server. MCP is extremely useful when you need live integration between tools, but many workflows are still more practical using existing CLIs.</p>
<p>For example, I use the <a href="https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server"><code>Figma MCP</code> server</a> to fetch design details directly into Cursor. It saves time when I need to reference a component or color spec while coding. Similarly, I rely on the <a href="https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/"><code>Atlassian MCP</code> server</a> to fetch Jira ticket or Confluence information automatically when running <code>/new-requirement</code>, which keeps requirements in sync.</p>
<p>However, not every integration benefits from MCP. For GitLab, I prefer to use the <a href="https://gitlab.com/gitlab-org/cli">GitLab CLI</a> instead of setting up an MCP server. It’s faster, simpler, and perfectly fine for tasks like creating MR. Sometimes, adding MCP just for the sake of it adds unnecessary complexity.</p>
<p>Use MCP when context-sharing between AI and external tools makes your workflow smoother, but don’t force everything into the MCP pattern. Command-line tools and direct editor integrations are often more efficient for straightforward tasks.</p>
<h2 id="why-i-built-ai-devkit"><a class="heading-link" href="#why-i-built-ai-devkit">Why I Built ai-devkit</a></h2>
<p>Before <code>ai-devkit</code>, I used to keep a folder of prompts. Every time I needed to start something new, I copied one prompt into Cursor, adjusted the wording, and ran it. It worked, but it was manual, inconsistent, and error-prone. Sometimes the context is lost in the middle.</p>
<p>So I bundled everything into a single, reusable setup <a href="https://github.com/codeaholicguy/ai-devkit"><code>ai-devkit</code></a>. It gives me consistency across projects and lets others benefit too.</p>
<p>You can use it directly in Cursor, or even Claude Code, and that is your choice. The structure is universal, it just depends on how you integrate prompts into your workspace.</p>
<h2 id="real-example-better-output-lower-cost"><a class="heading-link" href="#real-example-better-output-lower-cost">Real Example: Better Output, Lower Cost</a></h2>
<p>In my previous post, I <a href="https://codeaholicguy.com/2025/10/04/testing-claude-sonnet-4-5-gpt-5-codex-and-grok-code-fast-1-in-cursor-which-model-fits-best-for-development/">compared Claude Sonnet 4.5, GPT-5 Codex, and Grok Code Fast 1</a>. Grok Code Fast 1 was fast, but produced incomplete code.</p>
<p>When I tested the same task again using <code>ai-devkit</code> with the <code>/new-requirement</code> command in Cursor with Grok Code Fast 1, it was fast, the result was good on the first try, and the cost was much lower compared to the other models.</p>
<p><a href="/images/2025/10/screenshot-2025-10-16-at-15.29.27.png"><img src="/images/2025/10/screenshot-2025-10-16-at-15.29.27.png" alt="Image gallery app built with AI DevKit and Grok Code Fast 1, showing a responsive grid of photos" decoding="async" width="1023" height="500"></a></p>
<p><a href="/images/2025/10/screenshot-2025-10-16-at-15.29.39.png"><img src="/images/2025/10/screenshot-2025-10-16-at-15.29.39.png" alt="Same gallery app with an image opened in a modal showing the photographer, resolution, and a close button" decoding="async" loading="lazy" width="1022" height="501"></a></p>
<p>Model matters, but what we give to the model matters more.</p>
<h2 id="the-future-of-ai-assisted-engineering"><a class="heading-link" href="#the-future-of-ai-assisted-engineering">The future of AI-assisted Engineering</a></h2>
<p>AI is now part of the engineering workflow, it amplifies, not replaces, human engineers in the workflow. We are augmented by it.</p>
<p>The phases remain the same, but the cycle time is reduced. You still need to think clearly, design responsibly, and test thoroughly. AI just helps you get there faster, with less friction between steps.</p>
<p>I see this as the new normal: Engineers still <a href="https://ownthecraftbook.com/">own the craft</a>, but they now have powerful copilots to accelerate it.</p>
<p>If you’re curious to explore this kind of workflow, try <a href="https://github.com/codeaholicguy/ai-devkit"><code>ai-devkit</code></a>. Use it, experiment with your own commands, or even extend it with better prompts. All contributions are welcome.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to Create a ChatGPT App Using MCP (Model Context Protocol) Step by Step</title>
      <link>https://codeaholicguy.com/2025/10/11/how-to-create-a-chatgpt-app-using-mcp-model-context-protocol-step-by-step/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/10/11/how-to-create-a-chatgpt-app-using-mcp-model-context-protocol-step-by-step/</guid>
      <pubDate>Sat, 11 Oct 2025 16:30:42 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Build a ChatGPT app with the OpenAI Apps SDK step by step: a React to-do widget, an Express MCP server with resources and tools, and Developer Mode.</description>
      <content:encoded><![CDATA[<p>OpenAI just announced the <a href="https://developers.openai.com/apps-sdk">Apps SDK</a> during OpenAI DevDay 2025. It quietly introduced something important, transforming <a href="https://chatgpt.com/">ChatGPT</a> from a Chat app to an App Platform.</p>
<p>I wanted to understand how this works in practice, so I decided to build something small: a to-do app (because, well, this is something you build when you learn something new).</p>
<p>Here’s the plan:</p>
<ul>
<li>We’ll build a <a href="https://openai.com/index/introducing-apps-in-chatgpt/">ChatGPT App</a> that can read, write, and sync a to-do list in real time.</li>
<li>We’ll keep things simple: no database, no cloud setup, no fancy frameworks.</li>
<li>We’ll use JavaScript, Express, React, and <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP (Model Context Protocol)</a>.</li>
</ul>
<p>It’s the easiest way to see how to build a simple ChatGPT App in real life. This tutorial walks through that process from scratch: how to build, connect, and finally, make ChatGPT aware when your app updates.</p>
<p>Here’s the flow we’re going for:</p>
<ol>
<li>You ask ChatGPT: “Add create tutorial to my to-do list.”</li>
<li>ChatGPT calls your MCP tool <code>addTask</code>.</li>
<li>The server stores the task in memory.</li>
<li>The React UI updates instantly to show it.</li>
<li>Later, when you check a task off in the UI, ChatGPT knows it happened.</li>
</ol>
<p>That last step is the interesting one, your app notifying ChatGPT when something changes. We’ll get there.</p>
<p>You’ll only need a few things to follow along. Make sure you install them before going to the next steps.</p>
<ul>
<li><a href="https://nodejs.org/en/download">Node.js</a></li>
<li>npm (this is included when you install Node.js)</li>
<li><a href="https://ngrok.com/download/mac-os">ngrok</a> (for exposing your local machine to the internet)</li>
</ul>
<p>Let’s start clean.</p>
<p>Here is the folder structure that we will set up:</p>
<pre><code class="language-text">chatgpt-app-todo/
├── server/ # Express + MCP server
│ ├── index.js # Entry point for backend
│ └── package.json
├── client/ # React frontend
│ ├── src/
│ │ └── App.jsx # Main UI component
│ ├── index.html
│ └── package.json
</code></pre>
<p>In your terminal, just run these commands:</p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">mkdir</span> chatgpt-app-todo
<span class="hljs-built_in">cd</span> chatgpt-app-todo
<span class="hljs-built_in">mkdir</span> server client
</code></pre>
<h2 id="build-the-server"><a class="heading-link" href="#build-the-server">Build the Server</a></h2>
<p>The server will do two things:</p>
<ol>
<li>Provide a basic REST API that the React app can talk to.</li>
<li>Register MCP tools and resources so ChatGPT can call them.</li>
</ol>
<p>Let’s start with the basics.</p>
<p>Inside <code>/server</code>, we will install some simple dependencies before coding.</p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">cd</span> server
npm install --save express cors body-parser
<span class="hljs-built_in">touch</span> index.js
</code></pre>
<p>In <code>index.js</code>, we will create a simple server to handle some basic endpoint to create tasks, list tasks, and mark task as completed.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-title function_">express</span>();
app.<span class="hljs-title function_">use</span>(<span class="hljs-title function_">cors</span>());
app.<span class="hljs-title function_">use</span>(express.<span class="hljs-title function_">json</span>());

<span class="hljs-keyword">let</span> tasks = [];

app.<span class="hljs-title function_">get</span>(<span class="hljs-string">'/tasks'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =></span> res.<span class="hljs-title function_">json</span>(tasks));
app.<span class="hljs-title function_">post</span>(<span class="hljs-string">'/tasks'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =></span> {
  <span class="hljs-keyword">const</span> newTask = { <span class="hljs-attr">id</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(), <span class="hljs-attr">text</span>: req.<span class="hljs-property">body</span>.<span class="hljs-property">text</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> };
  tasks.<span class="hljs-title function_">push</span>(newTask);
  res.<span class="hljs-title function_">json</span>(newTask);
});

app.<span class="hljs-title function_">post</span>(<span class="hljs-string">'/tasks/:id/complete'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =></span> {
  <span class="hljs-keyword">const</span> task = tasks.<span class="hljs-title function_">find</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> t.<span class="hljs-property">id</span> === +req.<span class="hljs-property">params</span>.<span class="hljs-property">id</span>);
  <span class="hljs-keyword">if</span> (task) task.<span class="hljs-property">completed</span> = <span class="hljs-literal">true</span>;
  res.<span class="hljs-title function_">json</span>(task);
});

app.<span class="hljs-title function_">listen</span>(<span class="hljs-number">3000</span>, <span class="hljs-function">() =></span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Server running on http://localhost:3000'</span>));
</code></pre>
<p>Now you can run your server with <code>node index.js</code>.</p>
<p>You should see <code>Server running on http://localhost:3000</code>. When you visit <code>http://localhost:3000/tasks</code> you will receive an empty array <code>[]</code>.</p>
<p>That’s the simplest version of the API and perfect for now. If you are a software engineer, this only takes a few minutes.</p>
<h2 id="build-the-client"><a class="heading-link" href="#build-the-client">Build the Client</a></h2>
<p>We’ll keep this simple too. We will spin up a new React app with JavaScript using <a href="https://vite.dev/guide/">Vite</a>. In <code>client</code>, run</p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">cd</span> client
npm create vite@latest
</code></pre>
<p>Answer some questions to create the app, remember to choose React and JavaScript.</p>
<p>Update <code>src/App.tsx</code> with this, to create a quick Todo interface.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [tasks, setTasks] = <span class="hljs-title function_">useState</span>([]);
  <span class="hljs-keyword">const</span> [text, setText] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">fetchTasks</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">"http://localhost:3000/tasks"</span>);
    <span class="hljs-title function_">setTasks</span>(<span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>());
  };

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">addTask</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {
    <span class="hljs-keyword">if</span> (!text.<span class="hljs-title function_">trim</span>()) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">"http://localhost:3000/tasks"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> },
      <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ text }),
    });
    <span class="hljs-title function_">setText</span>(<span class="hljs-string">""</span>);
    <span class="hljs-title function_">fetchTasks</span>();
  };

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">completeTask</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params">id</span>) => {
    <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">`http://localhost:3000/tasks/<span class="hljs-subst">${id}</span>/complete`</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
    });
    <span class="hljs-title function_">fetchTasks</span>();
  };

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">handleKeyPress</span> = (<span class="hljs-params">e</span>) => {
    <span class="hljs-keyword">if</span> (e.<span class="hljs-property">key</span> === <span class="hljs-string">"Enter"</span>) {
      <span class="hljs-title function_">addTask</span>();
    }
  };

  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
    <span class="hljs-title function_">fetchTasks</span>();
  }, []);

  <span class="hljs-keyword">const</span> completedCount = tasks.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">t</span>) =></span> t.<span class="hljs-property">completed</span>).<span class="hljs-property">length</span>;
  <span class="hljs-keyword">const</span> totalCount = tasks.<span class="hljs-property">length</span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">h1</span>></span>Task Tracker<span class="hljs-tag">&#x3C;/<span class="hljs-name">h1</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">p</span>></span>Stay organized and productive<span class="hljs-tag">&#x3C;/<span class="hljs-name">p</span>></span>

      {totalCount > 0 &#x26;&#x26; (
        <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">span</span>></span>Progress: {completedCount} of {totalCount} completed<span class="hljs-tag">&#x3C;/<span class="hljs-name">span</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> `${<span class="hljs-attr">progress</span>}%`, <span class="hljs-attr">background:</span> "<span class="hljs-attr">blue</span>", <span class="hljs-attr">height:</span> "<span class="hljs-attr">5px</span>" }}></span><span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
      )}

      <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{text}</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =></span> setText(e.target.value)}
          onKeyPress={(e) => e.key === "Enter" &#x26;&#x26; addTask()}
          placeholder="What needs to be done?"
        />
        <span class="hljs-tag">&#x3C;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{addTask}</span>></span>Add Task<span class="hljs-tag">&#x3C;/<span class="hljs-name">button</span>></span>
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>

      <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span>></span>
        {tasks.length === 0 ? (
          <span class="hljs-tag">&#x3C;<span class="hljs-name">p</span>></span>No tasks yet. Add one to get started!<span class="hljs-tag">&#x3C;/<span class="hljs-name">p</span>></span>
        ) : (
          tasks.map((t) => (
            <span class="hljs-tag">&#x3C;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{t.id}</span>></span>
              <span class="hljs-tag">&#x3C;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                <span class="hljs-attr">checked</span>=<span class="hljs-string">{t.completed}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{()</span> =></span> completeTask(t.id)}
              />
              <span class="hljs-tag">&#x3C;<span class="hljs-name">span</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">textDecoration:</span> <span class="hljs-attr">t.completed</span> ? "<span class="hljs-attr">line-through</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">none</span>" }}></span>
                {t.text}
              <span class="hljs-tag">&#x3C;/<span class="hljs-name">span</span>></span>
            <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
          ))
        )}
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">div</span>></span></span>
  );
}
</code></pre>
<p>I also added Tailwind to make it not boring. For setting up Tailwind, you can refer to <a href="https://tailwindcss.com/docs/installation/using-vite">the instructions here</a>. It is pretty straightforward.</p>
<p>Now, when you run the app with <code>npm run dev</code>, then visit <code>http://localhost:5173/ you now have a fully functional</code> Todo app.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-11.56.15.png"><img src="/images/2025/10/screenshot-2025-10-10-at-11.56.15.png" alt="Task Tracker to-do app with a progress bar, an Add Task input, and two tasks, one completed" decoding="async" width="1023" height="659"></a></p>
<p>When you run <code>npm run build</code>, you will see your app artifact in <code>client/dist</code> folder.</p>
<p>That’s all the easy part, you might take a few minutes on this, the next part we will focus on building MCP (Model Context Protocol) server and connect the app with ChatGPT.</p>
<h2 id="building-the-mcp-server"><a class="heading-link" href="#building-the-mcp-server">Building the MCP server</a></h2>
<p>By now, you have a working local app:</p>
<ul>
<li>A small Express server that stores your tasks in memory.</li>
<li>A React frontend that lets you add or complete tasks.</li>
</ul>
<p>Now it’s time to make ChatGPT part of the loop.</p>
<p>Here is the high-level of the architecture when building ChatGPT app.</p>
<p><a href="/images/2025/10/mermaid-chart-create-complex-visual-diagrams-with-text.-2025-10-11-160947.png"><img src="/images/2025/10/mermaid-chart-create-complex-visual-diagrams-with-text.-2025-10-11-160947.png" alt="ChatGPT app architecture: ChatGPT sends tool requests to your MCP server and renders the returned UI component" decoding="async" loading="lazy" width="1022" height="228"></a></p>
<p>If you want to learn more about MCP, you can visit <a href="https://modelcontextprotocol.io/docs/getting-started/intro">here</a>. I also had another post to <a href="https://codeaholicguy.com/2025/06/14/why-does-mcp-matter-a-deep-dive-for-engineers/">explain about MCP and why it matters</a>.</p>
<p>For this tutorial, you can understand MCP as a bridge between ChatGPT and external systems. With it, ChatGPT can:</p>
<ul>
<li>Call your functions (called “tools”): for example, add a new task or get a list.</li>
<li>Render custom UIs inside ChatGPT: using small HTML widgets (iframes).</li>
<li>Exchange structured data between your app and ChatGPT.</li>
</ul>
<p>In order to add MCP to your server, you need to install 2 dependencies</p>
<pre><code class="hljs language-bash">npm i --save @modelcontextprotocol/sdk zod
</code></pre>
<h3 id="register-resource"><a class="heading-link" href="#register-resource">Register resource</a></h3>
<p>Back to your <code>server/index.js</code>, add this code:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> <span class="hljs-title function_">createMcpServer</span> = (<span class="hljs-params"></span>) => {
  <span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> <span class="hljs-title class_">McpServer</span>({
    <span class="hljs-attr">name</span>: <span class="hljs-string">'Todo List'</span>,
    <span class="hljs-attr">version</span>: <span class="hljs-string">'1.0.0'</span>,
  });

  <span class="hljs-keyword">const</span> html = fs.<span class="hljs-title function_">readFileSync</span>(path.<span class="hljs-title function_">join</span>(<span class="hljs-string">'../client/dist'</span>, <span class="hljs-string">'index.html'</span>), <span class="hljs-string">'utf8'</span>).<span class="hljs-title function_">trim</span>();
  server.<span class="hljs-title function_">registerResource</span>(
    <span class="hljs-string">"chatgpt-app-todo-widget"</span>,
    <span class="hljs-string">"ui://widget/chatgpt-app-todo.html"</span>,
    {},
    <span class="hljs-function">() =></span> ({
      <span class="hljs-attr">contents</span>: [{
        <span class="hljs-attr">uri</span>: <span class="hljs-string">"ui://widget/chatgpt-app-todo.html"</span>,
        <span class="hljs-attr">mimeType</span>: <span class="hljs-string">"text/html+skybridge"</span>,
        <span class="hljs-attr">text</span>: html
      }]
    })
  );

  <span class="hljs-keyword">return</span> server;
};
</code></pre>
<p>The <code>createMcpServer</code> function is for creating your MCP server. This reads the final built HTML file from your React app. We’ll send this HTML to ChatGPT so it can display your UI directly inside its own window through an iframe.</p>
<p>There are something to note:</p>
<ul>
<li><code>registerResource()</code> lets you expose files or data that ChatGPT can use as visual components or structured resources.</li>
<li>The <code>ui://widget/...html</code> URI is just a convention so that ChatGPT understands this is a UI.</li>
<li><code>mimeType: "text/html+skybridge"</code> tells ChatGPT it’s not a normal webpage, it’s an embedded widget that should be rendered inside ChatGPT.</li>
<li>You could make the return contents dynamic if needed, but here we just return your static HTML.</li>
</ul>
<p>After this, ChatGPT knows that your MCP server offers a resource called <code>chatgpt-app-todo-widget</code> that can be displayed visually.</p>
<p>Then, you need to provide a way for ChatGPT to connect with your MCP server. In order to do this, you need to expose an endpoint for ChatGPT to call. In your <code>server/index.js</code>, expose an endpoint like this:</p>
<pre><code class="hljs language-javascript">app.<span class="hljs-title function_">post</span>(<span class="hljs-string">'/mcp'</span>, <span class="hljs-title function_">async</span> (req, res) => {
  <span class="hljs-keyword">const</span> server = <span class="hljs-title function_">createMcpServer</span>();

  <span class="hljs-keyword">const</span> transport = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StreamableHTTPServerTransport</span>({
    <span class="hljs-attr">sessionIdGenerator</span>: <span class="hljs-literal">undefined</span>,
    <span class="hljs-attr">enableJsonResponse</span>: <span class="hljs-literal">true</span>
  });

  res.<span class="hljs-title function_">on</span>(<span class="hljs-string">'close'</span>, <span class="hljs-function">() =></span> {
    transport.<span class="hljs-title function_">close</span>();
  });

  <span class="hljs-keyword">await</span> server.<span class="hljs-title function_">connect</span>(transport);
  <span class="hljs-keyword">await</span> transport.<span class="hljs-title function_">handleRequest</span>(req, res, req.<span class="hljs-property">body</span>);
});
</code></pre>
<p>So, when ChatGPT sends an MCP message to <code>POST /mcp</code>, this function makes sure it’s handled end-to-end according to the <a href="https://modelcontextprotocol.io/specification/2025-06-18">MCP specifications</a>. In production environment, you will need to handle authentication and authorization, but within this tutorial, we won’t do it.</p>
<p>Now, you have your MCP server. To test the server, you can use <a href="https://modelcontextprotocol.io/docs/tools/inspector">MCP Inspector</a>, you can run the inspector with this command.</p>
<pre><code class="hljs language-bash">npx @modelcontextprotocol/inspector
</code></pre>
<p>Then, go to <code>http://localhost:6274/</code>, you will get the inspector UI. You can connect to your MCP server by inputting your URL. Click on <code>List Resources</code>, you will see your <code>chatgpt-app-todo-widget</code>.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.06.52.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.06.52.png" alt="MCP Inspector connected to localhost:3000/mcp, listing the chatgpt-app-todo-widget resource and its HTML contents" decoding="async" loading="lazy" width="1024" height="489"></a></p>
<p>Please note that whenever you change your MCP server code, you need to reconnect to the MCP server in Inspector to get the latest changes.</p>
<h3 id="register-tool"><a class="heading-link" href="#register-tool">Register tool</a></h3>
<p>Now, let’s register a tool that ChatGPT can call when a user says something like “Create a new task: finish reading the MCP tutorial.”</p>
<p>In your <code>createMcpServer</code> function, add this:</p>
<pre><code class="hljs language-javascript">  server.<span class="hljs-title function_">registerTool</span>(<span class="hljs-string">'createTask'</span>, {
    <span class="hljs-attr">title</span>: <span class="hljs-string">'Create a new task'</span>,
    <span class="hljs-attr">description</span>: <span class="hljs-string">'Create a new task'</span>,
    <span class="hljs-attr">inputSchema</span>: {
      <span class="hljs-attr">text</span>: z.<span class="hljs-title function_">string</span>(),
    },
    <span class="hljs-attr">outputSchema</span>: {
      <span class="hljs-attr">id</span>: z.<span class="hljs-title function_">number</span>(),
      <span class="hljs-attr">text</span>: z.<span class="hljs-title function_">string</span>(),
      <span class="hljs-attr">completed</span>: z.<span class="hljs-title function_">boolean</span>(),
    },
    <span class="hljs-attr">_meta</span>: {
      <span class="hljs-string">"openai/outputTemplate"</span>: <span class="hljs-string">"ui://widget/chatgpt-app-todo.html"</span>,
      <span class="hljs-string">"openai/toolInvocation/invoking"</span>: <span class="hljs-string">"Creating task..."</span>,
      <span class="hljs-string">"openai/toolInvocation/invoked"</span>: <span class="hljs-string">"Task created"</span>
    },
  }, <span class="hljs-title function_">async</span> ({ text }) => {
    <span class="hljs-keyword">const</span> newTask = { <span class="hljs-attr">id</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(), text, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> };
    tasks.<span class="hljs-title function_">push</span>(newTask);
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">structuredContent</span>: newTask,
      <span class="hljs-attr">content</span>: [
        { <span class="hljs-attr">type</span>: <span class="hljs-string">'text'</span>, <span class="hljs-attr">text</span>: newTask.<span class="hljs-property">text</span> },
      ],
    }
  });
</code></pre>
<p>There are 3 parts when you register a tool:</p>
<ul>
<li>The tool name.</li>
<li>The configuration defines what the tool is and how it should behave so that ChatGPT can understand.</li>
<li>The handler function that runs when ChatGPT calls this tool.</li>
</ul>
<p>In configuration, you will have:</p>
<ul>
<li>The title and description in plain text, so that it can describe the meaning of the tool to ChatGPT.</li>
<li>The <code>inputSchema</code> and <code>outputSchema</code> tells ChatGPT the shape of data the tool expects and returns. We’re using <code>zod</code> to define and validate types.</li>
<li>The <code>_meta</code> to explain the user experience when ChatGPT triggers the tool
<ul>
<li><code>openai/outputTemplate</code> tells ChatGPT which UI widget to show when this tool is used. In our case, it’s the same HTML widget we registered above.</li>
<li><code>openai/toolInvocation/invoking</code> is the message ChatGPT displays while the tool runs.</li>
<li><code>openai/toolInvocation/invoked</code> is what it says when the tool finishes.</li>
</ul>
</li>
</ul>
<p>In the handler function, there are things you need to note:</p>
<ul>
<li><code>content</code> is the required field; this is for ChatGPT to show to the user.</li>
<li><code>structuredContent</code> is for the app; this will be validated with the <code>outputSchema</code>.</li>
</ul>
<p>We can test this by MCP Inspector, go to List Tools, then select the tool and choose Run tool.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.26.36.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.26.36.png" alt="MCP Inspector Tools tab listing createTask, getTasks, and completeTask, with createTask&#x27;s input and output schema" decoding="async" loading="lazy" width="1024" height="485"></a></p>
<p>When you can run the tool successfully, you will see the result like this.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.27.33.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.27.33.png" alt="MCP Inspector showing a successful createTask result with structured content valid against the output schema" decoding="async" loading="lazy" width="1024" height="479"></a></p>
<p>You can create other tools, such as get tasks, or mark a task as completed, similar to the example above.</p>
<p>At this point, you already have the MCP server that provides information to ChatGPT about:</p>
<ul>
<li>What your app looks like (via the registered HTML resource).</li>
<li>What actions it can perform (via the registered tools).</li>
</ul>
<p>Once you start your server and register it with ChatGPT, you’ll be able to chat: “Create a new task: finish reading the MCP tutorial.” ChatGPT will call your tool, update the shared data, and render your widget.</p>
<h2 id="connect-your-app-with-chatgpt"><a class="heading-link" href="#connect-your-app-with-chatgpt">Connect your app with ChatGPT</a></h2>
<p>First, please double-check if your server is running.</p>
<p>Now we will <a href="https://ngrok.com/docs/getting-started">use ngrok to expose your server to the internet</a> so that ChatGPT can connect.</p>
<pre><code class="hljs language-bash">ngrok http 3000
</code></pre>
<p>If you have already deployed your server and exposed it to the internet, you won’t need this step.</p>
<p>Now, you need to enable Developer Mode in ChatGPT.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.41.12.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.41.12.png" alt="ChatGPT settings under Apps &#x26; Connectors with the Developer mode (beta) toggle switched on" decoding="async" loading="lazy" width="1024" height="882"></a></p>
<p>After enabling Developer mode, you will see a red border around the chat box. Then, we can create new Apps by clicking the Create button.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.44.36.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.44.36.png" alt="ChatGPT Connectors settings listing GitHub, Gmail, and Google connectors, with the Create button highlighted" decoding="async" loading="lazy" width="1023" height="872"></a></p>
<p>Input your information for the App. Please note that the <code>MCP Server URL</code> must be the one that you exposed through ngrok or your host if you have already deployed the server somewhere. In this tutorial, we will disable authentication for this App.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.45.46.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.45.46.png" alt="New Connector form with name ChatGPT App Todo, an ngrok MCP server URL, and No authentication selected" decoding="async" loading="lazy" width="844" height="1023"></a></p>
<p>Click Create to create your App. After creating the App successfully, you will see something like this. Remember that if you change your MCP server, you need to click Refresh to make sure your App syncs with the changes from the MCP server.</p>
<p><a href="/images/2025/10/screenshot-2025-10-10-at-15.51.15.png"><img src="/images/2025/10/screenshot-2025-10-10-at-15.51.15.png" alt="ChatGPT App Todo connector details showing its MCP URL, the createTask action, and the Refresh button highlighted" decoding="async" loading="lazy" width="1024" height="884"></a></p>
<p>Now we can test if our App can show in ChatGPT when we type “@ChatGPT App Todo Create a new task: finish reading the MCP tutorial.”.</p>
<p><a href="/images/2025/10/screenrecording2025-10-10at15.58.07-ezgif.com-optimize.gif"><img src="/images/2025/10/screenrecording2025-10-10at15.58.07-ezgif.com-optimize.gif" alt="Screen recording of ChatGPT in developer mode calling the ChatGPT App Todo app to create a new task" decoding="async" loading="lazy" width="800" height="466"></a></p>
<p>Hold on, there is something not right, after it ran the MCP tool to create new task, there was no item in the App UI. What was wrong?</p>
<p>Back to the <code>client/src/App.jsx</code>, we found the issue that there is no communication between ChatGPT and our App. But how can we fix that?</p>
<p>When your App UI (the widget) is rendered inside ChatGPT, it runs in a sandboxed iframe. Within that iframe, the OpenAI Apps SDK injects a global object: <code>window.openai</code>. This gives you safe access to ChatGPT context including:</p>
<ul>
<li>Functions to interact with ChatGPT’s runtime (tool calling, sending follow-ups, etc.)</li>
<li>Global data describing the current widget state (theme, locale, layout, tool input, tool output, etc.)</li>
</ul>
<p>Now, when the ChatGPT call MCP tool, as mentioned in Building the MCP section, we need to return the <code>structuredContent</code> for the App to use, this value will be included in the global data of ChatGPT context, named <code>toolOutput</code>. So, in this case, we want to listen if the value of <code>toolOutput</code> is changed, then re-render the app to reflect the correct state.</p>
<p>In order to listen to the <code>toolOutput</code> value, let’s create a React Hook for listening the <code>window.openai.toolOutput</code> changes. We will call this <code>useOpenAiGlobal</code>, so that we can listen other value if we need to.</p>
<p>Create <code>client/src/useOpenAiGlobal.js</code>.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { useSyncExternalStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-variable constant_">SET_GLOBALS_EVENT_TYPE</span> = <span class="hljs-string">"openai:set_globals"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">useOpenAiGlobal</span>(<span class="hljs-params">key</span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-title function_">useSyncExternalStore</span>(
    <span class="hljs-function">(<span class="hljs-params">onChange</span>) =></span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-variable language_">window</span> === <span class="hljs-string">"undefined"</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-function">() =></span> {};
      }

      <span class="hljs-keyword">const</span> <span class="hljs-title function_">handleSetGlobal</span> = (<span class="hljs-params">event</span>) => {
        <span class="hljs-keyword">const</span> value = event.<span class="hljs-property">detail</span>.<span class="hljs-property">globals</span>[key];
        <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span>) {
          <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-title function_">onChange</span>();
      };

      <span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-variable constant_">SET_GLOBALS_EVENT_TYPE</span>, handleSetGlobal, {
        <span class="hljs-attr">passive</span>: <span class="hljs-literal">true</span>,
      });

      <span class="hljs-keyword">return</span> <span class="hljs-function">() =></span> {
        <span class="hljs-variable language_">window</span>.<span class="hljs-title function_">removeEventListener</span>(<span class="hljs-variable constant_">SET_GLOBALS_EVENT_TYPE</span>, handleSetGlobal);
      };
    },
    <span class="hljs-function">() =></span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>?.[key] ?? <span class="hljs-literal">null</span>,
    <span class="hljs-function">() =></span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>?.[key] ?? <span class="hljs-literal">null</span>
  );
}
</code></pre>
<p>Then, in your <code>client/src/App.jsx</code>, use the hook that we just defined.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { useOpenAiGlobal } <span class="hljs-keyword">from</span> <span class="hljs-string">"./useOpenAiGlobal"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> toolOutput = <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span> ? <span class="hljs-title function_">useOpenAiGlobal</span>(<span class="hljs-string">"toolOutput"</span>) : <span class="hljs-literal">null</span>;

  <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>) {
    <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
      <span class="hljs-title function_">fetchTasks</span>();
    }, [toolOutput]);
  }
  <span class="hljs-comment">// The existed code</span>
}
</code></pre>
<p>What’s happening inside this code</p>
<ul>
<li>It first checks if <code>window.openai</code> exists (meaning the app is running inside ChatGPT’s environment).</li>
<li>If yes,
<ul>
<li>It calls a custom React hook <code>useOpenAiGlobal("toolOutput")</code>.</li>
<li>Every time <code>toolOutput</code> changes, we calls <code>fetchTasks()</code> to update the App state with new data. This is not the effective solution but the simplest solution to demostrate the communication between ChatGPT and the App.</li>
</ul>
</li>
<li>If not (e.g., you’re running locally in a dev browser), it just sets <code>toolOutput = null</code>.</li>
</ul>
<p>Now test it again, you will see the App will reflect new state after the tool run successfully.</p>
<p><a href="/images/2025/10/screenrecording2025-10-11at16.58.21-ezgif.com-video-to-gif-converter-1.gif"><img src="/images/2025/10/screenrecording2025-10-11at16.58.21-ezgif.com-video-to-gif-converter-1.gif" alt="Screen recording of ChatGPT creating a task and the to-do widget updating to show the new state" decoding="async" loading="lazy" width="800" height="466"></a></p>
<p>If you want to send follow up message to ChatGPT when you do some action in your app, you can use <code>window.openai.sendFollowupMessage</code> to send a message into the conversation. For example, in your <code>client/src/App.jsx</code></p>
<pre><code class="hljs language-javascript">  <span class="hljs-keyword">const</span> <span class="hljs-title function_">addTask</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {
    <span class="hljs-comment">// The existed code</span>
    <span class="hljs-keyword">await</span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>.<span class="hljs-title function_">sendFollowUpMessage</span>({
      <span class="hljs-attr">prompt</span>: <span class="hljs-string">`I just added a task to the todo list: <span class="hljs-subst">${text}</span>.`</span>,
    });
  };
</code></pre>
<p>Other than that, you can also call tool from your app, for example:</p>
<pre><code class="hljs language-javascript">  <span class="hljs-keyword">const</span> <span class="hljs-title function_">addTask</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {
    <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>) {
      <span class="hljs-keyword">await</span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>.<span class="hljs-title function_">callTool</span>(<span class="hljs-string">"createTask"</span>, { text });
      <span class="hljs-keyword">await</span> <span class="hljs-variable language_">window</span>.<span class="hljs-property">openai</span>.<span class="hljs-title function_">sendFollowUpMessage</span>({
        <span class="hljs-attr">prompt</span>: <span class="hljs-string">`I just added a task to the todo list: <span class="hljs-subst">${text}</span>.`</span>,
      });
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">"http://localhost:3000/tasks"</span>, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
        <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> },
        <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ text }),
      });
      <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetchTasks</span>();
    }
    <span class="hljs-title function_">setText</span>(<span class="hljs-string">""</span>);
  };
</code></pre>
<p>In order to call a tool from your app, you need to tell ChatGPT about that through the MCP tool meta by declaring <code>openai/widgetAccessible: true</code> when registering the tool.</p>
<pre><code class="hljs language-javascript"> server.<span class="hljs-title function_">registerTool</span>(<span class="hljs-string">'createTask'</span>, {
    <span class="hljs-attr">_meta</span>: {
      <span class="hljs-string">"openai/outputTemplate"</span>: <span class="hljs-string">"ui://widget/chatgpt-app-todo.html"</span>,
      <span class="hljs-string">"openai/toolInvocation/invoking"</span>: <span class="hljs-string">"Creating task..."</span>,
      <span class="hljs-string">"openai/toolInvocation/invoked"</span>: <span class="hljs-string">"Task created"</span>,
      <span class="hljs-string">"openai/widgetAccessible"</span>: <span class="hljs-literal">true</span>
    },
}, <span class="hljs-function">(<span class="hljs-params">{ text }</span>) =></span> {
    <span class="hljs-comment">// Your logic</span>
})
</code></pre>
<h2 id="summary"><a class="heading-link" href="#summary">Summary</a></h2>
<p>The technology stack behind a ChatGPT App isn’t particularly new or mysterious. It builds on ideas that web developers have been using for years:</p>
<ul>
<li>When ChatGPT shows your app, it’s really just embedding an iframe. That means your UI can be built with any web frontend technology such as React, Vue, Svelte, or even plain HTML, CSS, and JavaScript. There’s no special rendering engine or magic framework. It’s just a secure sandboxed web page.</li>
<li>Inside that iframe, ChatGPT exposes a global object called <code>window.openai</code>. This is how your App and ChatGPT talk to each other:
<ul>
<li>ChatGPT injects tool output and widget state into your app.</li>
<li>Your app can send updates or trigger actions using APIs.</li>
</ul>
</li>
<li>On the backend, ChatGPT communicates with your server using the Model Context Protocol (MCP). MCP is an open protocol that defines how ChatGPT can discover, call, and receive data from your app. You register tools and resources on your server, and ChatGPT calls them over HTTP (or WebSocket) using JSON messages.</li>
</ul>
<p>All of this is still in beta and currently limited to developer access. OpenAI has said that app submissions will open later in the year, so the SDK and MCP specifications for ChatGPT App are still evolving. So we should expect changes in APIs, event naming, and configuration formats as development continues.</p>
<p>The sample code of this tutorial can be find on <a href="https://github.com/codeaholicguy/chatgpt-app-todo/tree/main">my Github</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Grok vs Claude vs Codex: Which is best for coding?</title>
      <link>https://codeaholicguy.com/2025/10/04/testing-claude-sonnet-4-5-gpt-5-codex-and-grok-code-fast-1-in-cursor-which-model-fits-best-for-development/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/10/04/testing-claude-sonnet-4-5-gpt-5-codex-and-grok-code-fast-1-in-cursor-which-model-fits-best-for-development/</guid>
      <pubDate>Sat, 04 Oct 2025 06:30:00 GMT</pubDate>
      <category>AI</category>
      <category>Software Engineering</category>
      <description>Claude Sonnet 4.5, GPT-5 Codex, and Grok Code Fast 1 compared in Cursor on building a React gallery app and writing Jest tests, with code quality and cost.</description>
      <content:encoded><![CDATA[<p>It has only been a few days since <strong><a href="https://www.anthropic.com/">Anthropic</a></strong> announced their new model <a href="https://www.anthropic.com/news/claude-sonnet-4-5"><strong>Claude Sonnet 4.5</strong></a>, and I wanted to see for myself if the hype matches reality. Instead of reading benchmarks, I opened <a href="https://cursor.com/">Cursor</a> and ran some coding tests to feel it as an end user.</p>
<p>My goal with this test wasn’t to cover every possible case or measure the model’s intelligence. I just wanted to try a few real-world tasks that I deal with in my day-to-day work, and see how the models do in terms of code quality, speed, and output. This is purely a personal experiment, not an academic or comprehensive benchmark. You might see it differently, and that’s fine, I just want to give you a quick snapshot before you run your own tests.</p>
<p>I decided to test two key aspects of a developer’s workflow:</p>
<ul>
<li>Generating code for a new feature from scratch</li>
<li>Generating a test suite for an existing function</li>
</ul>
<p>I put three of the top coding models to the test: Anthropic’s new <strong>Claude Sonnet 4.5</strong>, OpenAI’s <strong><a href="https://openai.com/index/introducing-upgrades-to-codex/">GPT-5 Codex</a></strong>, and xAI’s <strong><a href="https://x.ai/news/grok-code-fast-1">Grok Code Fast 1</a></strong>.</p>
<h2 id="test-1-generating-a-new-app"><a class="heading-link" href="#test-1-generating-a-new-app">Test 1: Generating a New App</a></h2>
<p>For the first test, I wanted to see how well each model could handle a greenfield project, a common starting point for any new idea.</p>
<h3 id="the-prompt"><a class="heading-link" href="#the-prompt">The Prompt</a></h3>
<blockquote>
<p>“Generate a complete, production-ready React application using Vite and Tailwind CSS. The application should be a responsive image gallery. It should fetch images from a placeholder API (like <code>picsum.photos</code>), display them in a grid, and open a larger view in a modal when an image is clicked. Include basic styling for a clean, modern look and feel. Structure the code into logical components and provide instructions for running the project.”</p>
</blockquote>
<h3 id="impression"><a class="heading-link" href="#impression"><strong>Impression</strong></a></h3>
<p><strong>Claude Sonnet 4.5</strong></p>
<ul>
<li>This was impressively fast, completing the entire task in about a minute. It presented a clean, clear to-do list that was easy for an engineer to follow.</li>
<li>It was built with JavaScript and JSX, ran perfectly on the first try, styling was ok.</li>
<li>The code structure and component splitting were clean, simple, and exactly what I was looking for.</li>
<li>It even included a dark mode, which I hadn’t requested.</li>
<li>This was my favorite output of the three.</li>
</ul>
<p><strong>GPT-5 Codex</strong></p>
<ul>
<li>This model took a more methodical, but much slower approach. The process required me to manually confirm a series of scaffolding CLI commands, and the whole task took about a few minutes.</li>
<li>The final app also use JavaScript and JSX, it was fully functional on the first try.</li>
<li>The styling was better than <strong>Claude Sonnet 4.5</strong>‘s.</li>
<li>It also added an infinity scrolling feature I didn’t ask for, which was great.</li>
<li>While the code structure was good, it felt a bit overcomplicated for my simple request.</li>
<li>The output was high-quality, but the process was a bit cumbersome.</li>
</ul>
<p><strong>Grok Code Fast 1</strong></p>
<ul>
<li>The experience here was similar to <strong>GPT-5 Codex</strong>, taking a few minutes to complete.</li>
<li>The app, which it generated using TypeScript, did start on the first try, but the functionality was broken and the CSS was messy.</li>
<li>The code structure and component splitting weren’t good, and I was not a fan of the code it generated.</li>
<li>The cost is significantly lower compared to the other models.</li>
</ul>
<h3 id="output"><a class="heading-link" href="#output"><strong>Output</strong></a></h3>
<p><strong>Claude Sonnet 4.5</strong></p>
<p><strong>GPT-5 Codex</strong></p>
<p><strong>Grok Code Fast 1</strong></p>
<p><strong>Output: Main screen</strong></p>
<p><img src="/images/2025/10/screenshot-2025-09-30-at-11.25.05-2.png" alt="Claude Sonnet 4.5 output: image gallery grid with dark mode toggle and a Load More Images button" decoding="async" width="1023" height="681"></p>
<p><img src="/images/2025/10/screenshot-2025-09-30-at-12.20.24-1.png" alt="GPT-5 Codex output: polished dark Aurora Grid gallery with photo cards, a refresh button, and photo count" decoding="async" loading="lazy" width="1023" height="781"></p>
<p><img src="/images/2025/10/screenshot-2025-10-02-at-17.45.50.png" alt="Grok Code Fast 1 output: broken gallery page showing one oversized unstyled image under the heading" decoding="async" loading="lazy" width="1024" height="520"></p>
<p><strong>Output: Image modal</strong></p>
<p><img src="/images/2025/10/sonet-detail.png" alt="Claude Sonnet 4.5 image modal showing a full-size photo with photographer name and previous/next arrows" decoding="async" loading="lazy" width="1023" height="513"></p>
<p><img src="/images/2025/10/gpt5-codex-detail.png" alt="GPT-5 Codex image modal with photographer, resolution, navigation arrows, and 600/800/1200px download options" decoding="async" loading="lazy" width="1023" height="760"></p>
<p>Could not work as expected</p>
<h3 id="detail-comparision"><a class="heading-link" href="#detail-comparision">Detail comparision</a></h3>
<p><strong>Generated Todo list</strong></p>
<p><strong>Claude Sonnet 4.5</strong></p>
<p><strong>GPT-5 Codex</strong></p>
<p><strong>Grok Code Fast 1</strong></p>
<p><img src="/images/2025/10/sonet-todo.png" alt="Claude Sonnet 4.5 to-do list in Cursor with 7 steps, from Vite setup to ImageCard, ImageModal, and ImageGallery components" decoding="async" loading="lazy" width="676" height="904"></p>
<p><img src="/images/2025/10/gpt5-codex-todo.png" alt="GPT-5 Codex to-do list with 8 steps, including an image fetch hook, keyboard-accessible modal, and Load More pagination" decoding="async" loading="lazy" width="910" height="476"></p>
<p><img src="/images/2025/10/grok-todo.png" alt="Grok Code Fast 1 to-do list in Cursor with 10 granular steps for a Vite, React, and TypeScript gallery app" decoding="async" loading="lazy" width="802" height="884"></p>
<p><strong>Claude Sonnet 4.5</strong> delivers the most concise and focused plan.</p>
<ul>
<li>Aligned tightly with the prompt, it sticks to the core requirements.</li>
<li>Structured around three main components: ImageCard, ImageModal, and ImageGallery.</li>
<li>Smartly groups related tasks (ex: building the gallery and API integration together).</li>
<li>No unnecessary features or extra technologies.</li>
<li>Easiest to read at a glance, with a straight path to the final output.</li>
</ul>
<p><strong>GPT-5 Codex</strong> offers the most feature-rich, production-oriented approach.</p>
<ul>
<li>Goes beyond the prompt by adding pagination, loading/empty states, and polished styling.</li>
<li>Considers usability with keyboard/escape support.</li>
<li>Separates logic via an image fetch hook and types, showing modern React best practices.</li>
<li>Treats the task more like building a complete webpage than just a component set.</li>
</ul>
<p><strong>Grok-code-fast-1</strong> is the most detailed and step-by-step.</p>
<ul>
<li>Breaks everything into 10 small tasks, covering setup and functionality separately.</li>
<li>Explicitly chooses Vite + React + TypeScript from the start.</li>
<li>Translate the prompt into coding actions.</li>
</ul>
<p>At this stage, <strong>GPT-5 Codex</strong> is the most thoughtful, though it also introduces more complexity in the implementation.</p>
<p>If I had to choose, <strong>Claude Sonnet 4.5</strong> best matches my preferred style of working. <strong>Grok Code Fast 1</strong> provides a clear step-by-step setup, but lacks an overall implementation structure.</p>
<p><strong>Folder structure &#x26; components breakdown</strong></p>
<p><img src="/images/2025/10/sonet-folder.png" alt="Claude Sonnet 4.5 project structure with ImageCard, ImageGallery, and ImageModal JSX components" decoding="async" loading="lazy" width="498" height="848"></p>
<p><img src="/images/2025/10/gpt5-codex-folder.png" alt="GPT-5 Codex project structure with five components and a hooks folder containing useImageFeed.js" decoding="async" loading="lazy" width="482" height="1023"></p>
<p><img src="/images/2025/10/grok-folder.png" alt="Grok Code Fast 1 project structure in TypeScript with only ImageCard.tsx and Modal.tsx components" decoding="async" loading="lazy" width="466" height="1023"></p>
<p><strong>Claude Sonnet 4.5</strong>‘s structure is the simplest and most direct of the three. It’s a classic JavaScript-based React project.</p>
<ul>
<li>It created three components: <code>ImageCard.jsx</code>, <code>ImageGallery.jsx</code>, and <code>ImageModal.jsx</code>. Simple and good enough structure.</li>
<li>It created <code>ImageGallery.jsx</code> as a “container” component to manage the layout of the image cards, which is a good structure.</li>
</ul>
<p><strong>GPT-5 Codex</strong>‘s structure is the most sophisticated and demonstrates an understanding of advanced React patterns for building larger, more complex applications.</p>
<ul>
<li>It created a <code>hooks</code> folder containing a custom hook: <code>useImageFeed.js</code>. It separates the application’s logic (like fetching data, managing state, etc.) from the UI components. This makes the logic and the components easier to test and reuse.</li>
<li>The <code>components</code> directory is much more detailed.</li>
</ul>
<p><strong>Grok Code Fast 1</strong>‘s structure is clean and modern for TypeScript &#x26; React development.</p>
<ul>
<li>The components folder contains two core UI pieces: ImageCard.tsx and Modal.tsx. This is a common way to organize UI elements.</li>
<li>The core logic is likely bundled within App.tsx, making it less modular.</li>
<li>The structure is less organized and clean compared to the others.</li>
</ul>
<p>After reviewing the code, I found that the structure from <strong>GPT-5 Codex</strong> is robust enough for a production environment.</p>
<p>In contrast, the code from <strong>Claude Sonnet 4.5</strong> is simple and clean, making it an excellent starting point for a developer. I wasn’t a fan of the structure from <strong>Grok Code Fast 1</strong> in this test.</p>
<p>I’d consider it a <strong>tie between GPT-5 Codex and Claude Sonnet 4.5</strong>, as they both excel in different areas.</p>
<p><strong>Line of Code</strong></p>
<p><img src="/images/2025/10/sonet-loc.png" alt="cloc output for Claude Sonnet 4.5: 14 files and 734 lines of code, including 422 JSX and 195 Markdown" decoding="async" loading="lazy" width="1021" height="322"></p>
<p><img src="/images/2025/10/gpt5-codex-loc.png" alt="cloc output for GPT-5 Codex: 19 files and 770 lines of code, mostly JSX and JavaScript" decoding="async" loading="lazy" width="1023" height="325"></p>
<p><img src="/images/2025/10/grok-loc.png" alt="cloc output for Grok Code Fast 1: 18 files and 514 lines of code, with 232 lines of TypeScript" decoding="async" loading="lazy" width="1022" height="330"></p>
<p><strong>Claude Sonnet 4.5</strong></p>
<ul>
<li>Implementation is leaner than <strong>GPT-5 Codex</strong> but heavier than <strong>Grok Code Fast 1</strong>.</li>
<li>Best documentation coverage.</li>
<li>It makes me feel a balance between implementation and documentation.</li>
</ul>
<p><strong>GPT-5 Codex</strong></p>
<ul>
<li>Heaviest on real application logic, so we can understand that it is the most feature-rich implementation.</li>
<li>Docs are light.</li>
</ul>
<p><strong>Grok Code Fast 1</strong></p>
<ul>
<li>Much lighter real app logic compared to GPT-5 Codex and Claude Sonnet 4.5, explain why the functionalities didn’t work.</li>
<li>Docs are better than Codex.</li>
</ul>
<p>In summary, both <strong>GPT-5 Codex and Claude Sonnet 4.5 performed well in this test</strong>.</p>
<p>Personally, I value speed and simplicity, so I’d choose <strong>Claude Sonnet 4.5 for execution</strong>. However, <strong>for the planning phase, I’d lean on GPT-5 Codex</strong> because of its thoroughness.</p>
<h2 id="test-2-writing-tests"><a class="heading-link" href="#test-2-writing-tests">Test 2: Writing Tests</a></h2>
<p>For the second test, I wanted to see how each model would handle writing tests for a function, the kind of everyday task engineers deal with regularly.</p>
<h3 id="the-prompt-1"><a class="heading-link" href="#the-prompt-1">The Prompt</a></h3>
<blockquote>
<p>Generate a Jest test suite with edge cases for this function. Put the file in a util folder.</p>
<p>function divide(a, b) { return a / b; }</p>
</blockquote>
<h3 id="results"><a class="heading-link" href="#results">Results</a></h3>
<p><strong>Claude Sonnet 4.5</strong></p>
<p><strong>GPT-5 Codex</strong></p>
<p><strong>Grok Code Fast 1</strong></p>
<p><img src="/images/2025/10/sonet-test.png" alt="Jest results for Claude Sonnet 4.5: 23 passing divide tests grouped by category, with full coverage of divide.js" decoding="async" loading="lazy" width="848" height="1023"></p>
<p><img src="/images/2025/10/gpt5-codex-test.png" alt="Jest results for GPT-5 Codex: 8 passing divide tests with 100% coverage" decoding="async" loading="lazy" width="1024" height="694"></p>
<p><img src="/images/2025/10/grok-test.png" alt="Jest results for Grok Code Fast 1: 12 passing divide tests in math.test.ts with 100% coverage" decoding="async" loading="lazy" width="1024" height="779"></p>
<p><strong>Claude Sonnet 4.5</strong></p>
<ul>
<li>Fast to generate the test script and asked about the test setup.</li>
<li>After completing the test setup, it auto ran and double-checked coverage.</li>
<li>Produced 23 test cases covering division by zero, floats, negatives, and large numbers. Very detailed and thorough.
<ul>
<li>Covers all from Codex + Grok</li>
<li>Adds more categories:
<ul>
<li>Floating point division</li>
<li>Edge cases with overflow</li>
<li>Type coercion edge cases (numeric strings, booleans, null, undefined)</li>
</ul>
</li>
</ul>
</li>
<li>Limitations:
<ul>
<li>More verbose, harder to maintain.</li>
<li>Some cases may be unrealistic for real-world usage.</li>
</ul>
</li>
</ul>
<p><strong>GPT-5 Codex</strong></p>
<ul>
<li>Quick and direct, produced 8 test cases.
<ul>
<li>Positive/negative integers</li>
<li>Fractional results</li>
<li>Division by zero (Infinity, -Infinity, NaN)</li>
<li>Special handling with Infinity numerator</li>
<li>Zero result with Infinity denominator</li>
</ul>
</li>
<li>Asked about setup but didn’t check coverage.</li>
<li>Got the basics right, but shallow.</li>
<li>Style is concise, direct, and structured, ensuring correctness without redundancy.</li>
<li>Coverage is ok, but scope is narrower compared to the other two models.</li>
<li>May miss some rare edge cases like non-numeric inputs or type coercion.</li>
</ul>
<p><strong><strong>Grok Code Fast 1</strong></strong>:</p>
<ul>
<li>Slower, required setup commands first, similar to the first test. I need to manually approve a lot of CLI commands to do the test setup.</li>
<li>The good thing is that it only needs 1 prompt to complete everything, from setup to generating test cases.</li>
<li>Ended with 12 test cases. Solid coverage but no coverage check.
<ul>
<li>Very large numbers</li>
<li>Very small numbers</li>
<li>Floating point precision issues</li>
<li>Boundary values</li>
<li>Non-numeric inputs</li>
<li>Special numeric values (NaN, Infinity, etc.)</li>
</ul>
</li>
<li>Organized in a clean scenario-driven approach, making it easy to reason about what’s tested, covering realistic as well as edge inputs.</li>
</ul>
<p>Personally, <strong>Grok Code Fast 1 offers the most practical suite for production use</strong>.</p>
<p><strong>GPT-5 Codex</strong> is ideal for a lean starting point, and <strong>Claude Sonnet 4.5</strong> is best when maximum safety and completeness are required.</p>
<h2 id="cost-comparison"><a class="heading-link" href="#cost-comparison">Cost Comparison</a></h2>
<p>Here’s how the three providers price their coding models (per million tokens):</p>






























<table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>Model</td><td>Input Price (per 1M token)</td><td>Output Price (per 1M token)</td></tr><tr><td>Claude-sonnet-4.5</td><td>$3.00</td><td>$15.00</td></tr><tr><td>GPT-5 Codex</td><td>$1.25</td><td>$10.00</td></tr><tr><td>Grok-code-fast-1</td><td>$0.20</td><td>$1.50</td></tr></tbody></table>
<p><em>(Prices are not 100% exact; they vary depending on provider updates. You can check the latest on <a href="https://openrouter.ai/models?fmt=table">OpenRouter</a>)</em></p>
<p>If cost is the biggest factor, I would consider Grok Code Fast 1, but the way I prompt will be significantly different and cost more effort.</p>
<h2 id="conclusion"><a class="heading-link" href="#conclusion">Conclusion</a></h2>
<p>After spending a good amount of time with these models, <strong>I have a clear favorite for my day-to-day coding work: Claude Sonnet 4.5. Honestly, it’s my top recommendation.</strong></p>
<p>Now, if you’re trying to find a <strong>balance between cost and quality, GPT-5 Codex is a good choice</strong>. The code it writes is really polished and high-quality, and it’s more affordable to run.</p>
<p>But here’s the biggest takeaway for me: you don’t have to choose just one. <strong>A hybrid workflow seems like the real power</strong>.</p>
<ul>
<li>You could use <strong>GPT-5 Codex</strong> for the initial planning phase, since it’s so thoughtful about architecture.</li>
<li>Then, when it’s time to actually write the code, switch over to <strong>Claude Sonnet 4.5</strong> to take advantage of its raw speed and incredible thoroughness.</li>
<li>And even though <strong>Claude Sonnet 4.5</strong> generates the most comprehensive test cases, <strong>Grok Code Fast 1</strong> felt more practical for production use, so when it comes to writing tests, Grok is not a bad choice.</li>
</ul>
<p>I’m glad we could dive into this together. I’m really interested to hear about your own experiences as you get to know these tools!</p>]]></content:encoded>
    </item>
    <item>
      <title>What “Next Level” actually looks like</title>
      <link>https://codeaholicguy.com/2025/07/26/what-next-level-actually-looks-like/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/07/26/what-next-level-actually-looks-like/</guid>
      <pubDate>Sat, 26 Jul 2025 07:00:00 GMT</pubDate>
      <category>Software Engineering</category>
      <description>What it really takes to reach the next engineering level beyond the career ladder doc: build the foundation, expand your scope, then scale your impact.</description>
      <content:encoded><![CDATA[<p>In one of my 1:1s, an engineer on my team asked, “I know we have the career ladder docs,” he said, “but what does it really take to get to the next level? What’s the actual difference?”</p>
<p>It’s a question I hear a lot, and I love it. It shows a desire to grow beyond just shipping tickets. The official documents are great for defining expectations, but they can feel like a checklist. The real journey isn’t about ticking boxes.</p>
<p>In the previous post, I shared about <a href="https://codeaholicguy.com/2025/07/19/the-real-stages-of-engineering-experience/">the real stages of experience in Software Engineering</a>, so that you know what you should focus on to gain more experience. In this post, I will follow up on another aspect of what type of problem you should focus on to grow.</p>
<p>After years of building teams and mentoring engineers, I’ve found that the entire arc of a technical career can be distilled into a single idea:</p>
<blockquote>
<p><strong>Your growth is a journey from solving clearly defined problems, to navigating highly ambiguous problems, to finally identifying the right problems to solve for the business.</strong></p>
</blockquote>
<p>That’s it. That’s the game. You move from being given a map, to being given a compass and a destination, to being the one who decides which mountains are worth climbing in the first place.</p>
<p>While every company has its own matrix, here’s how I see that journey break down in practice.</p>
<h2 id="stage-1-building-the-foundation"><a class="heading-link" href="#stage-1-building-the-foundation">Stage 1: Building the Foundation</a></h2>
<p>This is where it all starts. Your title will be Software Engineer. The focus is on execution, reliability, and learning the craft. In this stage, I will split it into 2 phases.</p>
<p><strong>Software Engineer (Phase 1)</strong>: You deliver well-defined tasks on time and with high quality. You’re handed a clear spec, and you can be trusted to build it with minimal guidance. You’re honing your technical skills and proving your dependability.</p>
<p><strong>Software Engineer (Phase 2)</strong>: You don’t just take tasks, you clarify them. You spot the missing edge cases in a ticket before you start coding. You collaborate with your peers to complete assignments, asking the right questions to make the work better. You’re moving from a pure doer to a participant in the solution.</p>
<h2 id="stage-2-expanding-your-scope"><a class="heading-link" href="#stage-2-expanding-your-scope">Stage 2: Expanding Your Scope</a></h2>
<p>This is the most significant transition for many. Your title will be Senior Software Engineer. You begin to shift from owning tickets to owning outcomes. In this stage, I will also split it into 2 phases.</p>
<p><strong>Senior Software Engineer (Phase 1)</strong>: You own an entire project. Instead of a series of tasks, you’re given a goal, like “Improve the checkout flow”. You are responsible for clarifying the scope, breaking down the work, and collaborating across the team to deliver it. You’re now a leader of execution.</p>
<p><strong>Senior Software Engineer (Phase 2)</strong>: You’ve moved beyond project-level problems to team-level problems. You see the friction in the deployment process and propose a solution. You notice the team is struggling with a legacy component, and you create a plan to address it. You guide the team to a better way of working.</p>
<h2 id="stage-3-scaling-your-impact"><a class="heading-link" href="#stage-3-scaling-your-impact">Stage 3: Scaling Your Impact</a></h2>
<p>At this stage, your primary role is no longer only writing code, it’s creating leverage. You enable everyone around you to be more effective. Your title will be Staff Software Engineer, to Senior Staff Software Engineer, to Principal Software Engineer, to whatever your organization has.</p>
<p><strong>Staff Software Engineer:</strong> You are the go-to expert in a specific domain. When a hard problem involving your specialty comes up, you’re the person everyone seeks out. You provide direction, mentor others, and multiply your impact by enabling the entire team to succeed within your specialty.</p>
<p><strong>Senior Staff Software Engineer:</strong> You take on fuzzy, organization-level problems. The challenge might be “Our web performance is degrading” or “We need a unified design system”. You coordinate across multiple teams, create the plan, and ensure the entire org can deliver on a complex, high-impact outcome.</p>
<p><strong>Principal Software Engineer:</strong> You operate at the highest level of technical strategy. You aren’t just solving problems; you are identifying the most important future challenges and opportunities for the entire engineering organization. You see what’s three years out, propose a clear vision, and lead efforts across the company to make it a reality.</p>
<h2 id="important-truths"><a class="heading-link" href="#important-truths">Important Truths</a></h2>
<p>First, each level builds directly on the skills of the one before it. You can’t be an effective Senior Software Engineer who owns projects if you haven’t first proven you can deliver tasks reliably. This growth has to be demonstrated consistently, not just on one showcase project. I also mentioned once in the post <a href="https://codeaholicguy.com/2025/07/12/mindset-title/">Mindset > Title</a>, in order to move to the next level, it is not about changing the title. Promotions aren’t about potential alone. They’re about demonstrating the consistent behavior, attitude, and mindset aligned with the next role.</p>
<p>Second, you don’t have to keep climbing**.** In my experience, and as far as I know from different organizations, from Senior Software Engineer at stage 2 and above, it is considered a “career level”. This means if you are happy and effective in that role, you can remain there for the rest of your career. The pressure to advance is primarily on the earlier levels. Finding a position where you can deliver high value and feel fulfilled is a win, not a failure.</p>
<p>I hope this perspective helps clarify what real growth looks like. Now, the question is back to you.</p>
<p>Are you consistently meeting the expectations of your current level? And ready to start operating at the next one?</p>]]></content:encoded>
    </item>
    <item>
      <title>The real stages of Engineering Experience</title>
      <link>https://codeaholicguy.com/2025/07/19/the-real-stages-of-engineering-experience/</link>
      <guid isPermaLink="true">https://codeaholicguy.com/2025/07/19/the-real-stages-of-engineering-experience/</guid>
      <pubDate>Sat, 19 Jul 2025 07:20:00 GMT</pubDate>
      <category>Software Engineering</category>
      <description>The three real stages of engineering experience, from learning through mistakes to anticipating and preventing them, and how to grow at each stage.</description>
      <content:encoded><![CDATA[<p>This question came up in a chat with my team: “How do you think about different levels of experience?”. It led to a great discussion, and I think this perspective is worth sharing more widely.</p>
<p>In software engineering, experience isn’t just about how many years you’ve been in the game. I talked a bit about this in my previous post on <a href="https://codeaholicguy.com/2025/06/28/the-quite-power-of-pattern-matching/">The Quiet Power of Pattern Matching</a>. Real experience shows in how you tackle problems, build solutions, and turn ideas into something that actually works.</p>
<p>Personally, I find it useful to think of experience in three broad stages: engineers with no experience, engineers with some experience, and engineers with extensive experience.</p>
<p>When you have a clear sense of which stage you’re in, it’s much easier to know what to work on next. It gives you direction and helps you grow faster by setting goals that actually match where you are right now.</p>
<h2 id="engineers-with-no-experience"><a class="heading-link" href="#engineers-with-no-experience">Engineers with no experience</a></h2>
<p>This is the stage where theory hits the real world.</p>
<p>Most beginners come in fresh, maybe from university, a coding bootcamp, or an online course. They’ve spent a lot of time absorbing ideas about how things should work, learning stuff like design patterns, clean code, best practices, and system architecture.</p>
<p>But knowing the theory is one thing. Putting it into practice is a whole different story.</p>
<p>And it’s totally normal to struggle here. Turning concepts into working code can feel overwhelming. You’ll make mistakes, probably a lot of them. That’s not the problem. The real problem is not learning from those mistakes.</p>
<p>At this stage, the goal is to learn fast. Figure out what went wrong, why it happened, and how to avoid doing it again next time.</p>
<blockquote>
<p><strong>Engineers with no experience will learn the paths that lead to mistakes and use this knowledge to prevent future errors.</strong></p>
</blockquote>
<h2 id="engineers-with-some-experience"><a class="heading-link" href="#engineers-with-some-experience"><strong>Engineers with some experience</strong></a></h2>
<p>This is the stage where you need to build your experience bank.</p>
<p>Engineers with some experience have moved beyond theory and into the hands-on world of real projects. They’ve dealt with actual problems, started to notice recurring patterns, and built confidence in getting things done.</p>
<p>At this point, they’re pretty good at solving problems, but they might not always see the curveballs coming.</p>
<p>Foresight is still a work in progress. This stage is all about stacking up more experience, trying things, breaking things, fixing them, and learning along the way. It’s not just about avoiding mistakes they’ve made before, but also starting to spot and prevent the ones they haven’t seen yet.</p>
<blockquote>
<p><strong>Engineers with some experience can identify not only the paths that led to their own mistakes but also predict other paths that might lead to similar issues, allowing them to prevent a broader range of mistakes.</strong></p>
</blockquote>
<h2 id="engineers-with-extensive-experience"><a class="heading-link" href="#engineers-with-extensive-experience">Engineers with extensive experience</a></h2>
<p>This is the stage where you will think more about anticipating and preventing mistakes from happening.</p>
<p>What really sets experienced engineers apart is their ability to see problems before they happen. They’ve been through enough projects, some smooth, some rough, to build a kind of mental model of what works and what doesn’t. That instinct helps them avoid a lot of unnecessary pain, not just for themselves, but for the whole team.</p>
<p>But it’s not just about being good at solving problems. Extensively experienced engineers tend to take a step further. They guide others, spot patterns early, and help the team stay out of common traps. They’re always looking for better ways to do things. They ask questions like “Is there a simpler way to do this?”, “How can we do this better?”, etc.</p>
<p>Eventually, they move from just reacting to the issues to building systems that prevent the issues in the first place. They have a deep understanding of best practices and help embed them into the team’s habits. They know that building great systems isn’t a solo effort. It takes trust, collaboration, and a shared mindset of learning and growing together. That <a href="https://codeaholicguy.com/2025/07/12/mindset-title/">mindset</a> doesn’t just improve the code, it helps to shift how the whole team thinks and solve problems.</p>
<blockquote>
<p><strong>Engineers with extensive experience can foresee potential issues from the starting point and implement strategies to prevent mistakes from occurring in the first place.</strong></p>
</blockquote>
<p>For leaders, this perspective allows you to better support your team. You can set the right expectations, give more targeted feedback, and offer opportunities that match each person’s level. It also helps you spot potential early and grow your people in the right direction.</p>
<p>For individual contributors, knowing where you are helps you focus. You can double down on the skills that matter most right now, instead of trying to learn everything at once. You’ll also be better equipped to ask for the right kind of support, take on suitable challenges, and track your own progress.</p>
<p>When everyone understands their stage and what comes next, the team grows together with clarity, purpose, and momentum.</p>]]></content:encoded>
    </item>
  </channel>
</rss>