<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Nick Tune on Medium]]></title>
        <description><![CDATA[Stories by Nick Tune on Medium]]></description>
        <link>https://medium.com/@nicktune?source=rss-d012fdeb34b0------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*XxNIv1Xof4HeNoTzgKvcXA.jpeg</url>
            <title>Stories by Nick Tune on Medium</title>
            <link>https://medium.com/@nicktune?source=rss-d012fdeb34b0------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 10 Nov 2026 05:40:23 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@nicktune/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Architecture and model diffs via code conventions]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/architecture-and-model-diffs-via-code-conventions-8610a7bcf3e6?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/8610a7bcf3e6</guid>
            <category><![CDATA[domain-driven-design]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Tue, 08 Sep 2026 11:14:21 GMT</pubDate>
            <atom:updated>2026-09-08T11:14:21.330Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8D5aCzlfatE5P6us.png" /></figure><p>As an engineer reviewing human code or AI code, the first thing I always scan a pull request for are architectural changes. Is the right code in the right place following codebase conventions? And is the domain well modelled?</p><p>I’ve never really enjoyed looking at the diff on GitHub and trying to extract this information which is buried in hundreds of lines of code. And now I don’t have to.</p><p>In <a href="https://nick-tune.me/blog/2026-08-13-enforced-application-architecture-for-agents-and-humans">recent posts</a> I talked about strong codebase to conventions to enforce application architecture and domain modelling concepts. Those same conventions make it easy to identify architecture, and architecture changes, in your codebase. It therefore becomes trivial to add an architecture/modell to your pull requests (all via code without any LLM involvement.)</p><p>This type of simple architecture diff gets added as a comment to every PR:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SGMkycVxDVU4qAuR.png" /></figure><p>Read the full article for free on my website (no sign in or anything required, no cookies, I just chose to host to own my content): <a href="https://nick-tune.me/blog/2026-09-08-architecture-and-model-diffs-via-conventions/">https://nick-tune.me/blog/2026-09-08-architecture-and-model-diffs-via-conventions/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8610a7bcf3e6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/architecture-and-model-diffs-via-code-conventions-8610a7bcf3e6">Architecture and model diffs via code conventions</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deterministic code conventions with Oxlint]]></title>
            <link>https://nicktune.medium.com/deterministic-code-conventions-with-oxlint-74dfc3fa3b70?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/74dfc3fa3b70</guid>
            <category><![CDATA[domain-driven-design]]></category>
            <category><![CDATA[code-quality]]></category>
            <category><![CDATA[application-architecture]]></category>
            <category><![CDATA[software-design]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Fri, 10 Apr 2026 18:30:25 GMT</pubDate>
            <atom:updated>2026-04-10T18:30:25.671Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Gw1pkOD6dL3gfpw1.png" /></figure><p>The latest adventure on my journey to automate software conventions (mostly for AI but also for humans) has involved <a href="https://oxc.rs/docs/guide/usage/linter.html">Oxlint</a>. A TypeScript linter written in Rust.</p><p>My colleagues at PayFit have been pretty impressed with its performance, so I used it to implement the first version of a concept I’ve been thinking about for a while.</p><p>The idea is very simple: every piece of code must have a label indicating what type of code it is. Is it a repository, a validator, an error, a formatter. Based on the label you can then apply rules about where the code lives and how it can be combined with other code.</p><p>For example, any code with the decorator <em>/** </em><a href="http://twitter.com/riviere"><em>@riviere</em></a><em>-role cli-entrypoint */</em> can only live in a folder that matches the following pattern: src/features/{feature}/entrypoint. If you put that code in a different folder, Oxlint will report an error. And if you put other code in that location Oxlint will also report an error.</p><p>In the full post I walk through the TypeScript DSL I use to configure these rules, show how to go beyond location-based checks into coupling and role-level constraints, explain how to help AI agents recover when they hit a guard rail, and share why I’m leaning into extreme standardisation as a strategy for AI-assisted codebases.</p><p>Read the full post for free on <a href="https://nick-tune.me/blog/2026-04-10-deterministic-code-conventions-oxlint">nick-tune.me</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=74dfc3fa3b70" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Event-sourced Claude Code workflows]]></title>
            <link>https://nicktune.medium.com/event-sourced-claude-code-workflows-3cd1c531e4e9?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/3cd1c531e4e9</guid>
            <category><![CDATA[domain-driven-design]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Wed, 04 Mar 2026 22:36:39 GMT</pubDate>
            <atom:updated>2026-03-04T22:36:39.882Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9bjNCwrjbDh3HIMH.png" /><figcaption>Analyzing a Claude Code workflow using event-sourced events</figcaption></figure><p>In the previous couple of posts I shared an approach I’ve started using, to design Claude Code workflows as state machines in real code using domain-driven design concepts. And then how to make them more<br> declarative with DSLs.</p><p>It was suggested by Yves Reynhout to make the workflows fully event-sourced, by not storing the current state and only storing the events. Then deriving the current state from the events.</p><p>So I gave it a try. The implementation was a pretty boring event-sourcing implementation using SQLite for storage. However, the possibilities unlocked are quite exciting — in-depth observability of how my agents performed and where they spent their time implementing a feature, and the potential for cross-session trends analysis.</p><p>In the full post I walk through the observability features this unlocks — session timelines, rejection and hook denial counts, AI-powered analysis suggestions, journal entries, event log search — plus the architecture, cross-session analysis possibilities, and when this approach is actually worth it.</p><p><a href="https://nick-tune.me/blog/2026-03-04-event-sourced-claude-code-workflows/">Read the full post on nick-tune.me</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3cd1c531e4e9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Claude Code workflows as DSL-driven domain models]]></title>
            <link>https://nicktune.medium.com/claude-code-workflows-as-dsl-driven-domain-models-fb28c5fde899?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/fb28c5fde899</guid>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Mon, 02 Mar 2026 20:53:27 GMT</pubDate>
            <atom:updated>2026-03-02T20:53:27.030Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bTlJOm4cfkgxwKVD_qdnfA.png" /></figure><p>In the <a href="https://nick-tune.me/blog/2026-02-28-hook-driven-dev-workflows-with-claude-code/">previous post</a> I showed a hook-driven workflow engine that coordinates Claude Code agent teams from GitHub issue through to PR. Defining workflows as code improves determinism and allows us to unit test the workflows so we can put strong guardrails around Claude and ensure they are working.</p><p>The original version I created was fully implemented in code. The code had some basic design, separation of concerns and layering, but I was curious if I could make it even easier to see what each step of the workflow does without looking at lots of other code. I wanted something more declarative yet still type safe.</p><p>So I started working on a redesign to decouple the declarative workflow steps from the underlying execution mechanics to get the best of both worlds.</p><p>Full post on my website: <a href="https://nick-tune.me/blog/2026-03-01-workflow-dsl-domain-driven-claude-code-workflows/">https://nick-tune.me/blog/2026-03-01-workflow-dsl-domain-driven-claude-code-workflows/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fb28c5fde899" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hook-driven dev workflows with Claude Code]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/hook-driven-dev-workflows-with-claude-code-ee2f961ad7ef?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/ee2f961ad7ef</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[domain-driven-design]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Sun, 01 Mar 2026 10:04:14 GMT</pubDate>
            <atom:updated>2026-03-01T10:04:54.900Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yd7ogCNFTvYa0NvF.png" /></figure><p>AI works best for me when it takes care of the workflow itself and allows me to focus on the business goals and architecture. I don’t want to be constantly prompting and guiding the agent to do the right thing, to run code reviews etc. It’s not enjoyable and it’s boring.</p><p>I’ve had a lot of success introducing deterministic workflows on repositories I fully control. I own the lint rules, the git hooks, the code review steps, and I can bake those into the process. I am consistently getting good results.</p><p>However, I am not getting good results when I need to work in legacy codebases, or codebases where I do not control all of those parameters. How can I get Claude to apply the same workflow and coding standards in those situations?</p><p>The solution I’ve arrived at is Claude Code hooks. I realised that they can do a lot more than I understood. They allow a highly enforced workflow with strong guardrails, written in a proper programming language, with 100% test coverage.</p><p>In the full post I walk through the architecture of a hook-driven workflow engine, show the state machine that enforces transitions between planning, developing, reviewing, and committing phases, and share code examples from a working GitHub repo with full test coverage.</p><p><strong>Read the full post on nick-tune.me</strong> → <a href="https://nick-tune.me/blog/2026-02-28-hook-driven-dev-workflows-with-claude-code">https://nick-tune.me/blog/2026-02-28-hook-driven-dev-workflows-with-claude-code</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ee2f961ad7ef" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/hook-driven-dev-workflows-with-claude-code-ee2f961ad7ef">Hook-driven dev workflows with Claude Code</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code quality feedback loops in AI dev workflows]]></title>
            <link>https://nicktune.medium.com/code-quality-feedback-loops-in-ai-dev-workflows-8b1924baefb4?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/8b1924baefb4</guid>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Sun, 01 Feb 2026 18:34:06 GMT</pubDate>
            <atom:updated>2026-02-01T18:34:37.674Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lrQH_6RPwVWFZhD_.png" /></figure><p>My journeys with AI-assisted development workflows have recently been<br>focused on feedback loops in the process. What I notice is that as more of<br>the mechanical process is automated, it frees up time to focus on improving the quality and building quality in.</p><p>The majority of my learnings have been shifting left on the code review<br>process. Typically this will be feedback raised by CodeRabbit on the PR that I try to incorporate into the process so it is caught earlier or doesn’t<br>happen at all.</p><p>There are various touch points in the process where feedback can be applied. Sometimes it’s updating CLAUDE.md, sometimes it’s a custom lint rule. But sometimes, it’s actually going much further upstream and improving the quality of the specifications.</p><p>The full post is available on my personal website (for free, no cookies or anything, I just prefer to own my content ).</p><p><a href="https://nick-tune.me/blog/2026-02-01-code-quality-feedback-loops/">https://nick-tune.me/blog/2026-02-01-code-quality-feedback-loops/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b1924baefb4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Workflows as Code]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/dev-workflows-as-code-fab70d44b6ab?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/fab70d44b6ab</guid>
            <category><![CDATA[claude-code]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Sat, 17 Jan 2026 12:49:58 GMT</pubDate>
            <atom:updated>2026-01-17T12:49:58.357Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/748/1*XYU0zwvkTIDD9awJPo6q1Q.png" /></figure><p>I’ve reached a point where I’ve automated the development process for a single feature. Claude Code will take a github issue, implement it, run code reviews locally, raise a PR, respond to the PR feedback, and then tell me when it’s mergeable and ready for my review.</p><p>I was very happy with the setup, but not fully satisfied. It was reliable but not bullet proof. And there were still some niggles to iron out to make it solid.</p><p>To get to that solution, the answer was to use real code. The current approach relies on Claude running steps in order, aka--please-work</p><p>I’d extracted some logic to bash scripts to make it partly deterministic and leveraged subagents to decouple the main agent, but there was still room for the main agent to improvise (that’s a negative in this context).</p><p>With real code, this problem is solved. And there are many other advantages too. I recommend it.</p><h3>Current challenges</h3><p>As I explained in the <a href="https://medium.com/nick-tune-tech-strategy-blog/coding-agent-development-workflows-af52e6f912aa?source=user_profile_page---------0-------------d012fdeb34b0----------------------">last post</a>, the current solution looks like this:</p><pre>/complete-task<br>    │<br>    ▼<br>Main agent runs pipeline directly<br>    │<br>    ├── [Bash] Verify gate → FAIL → return to main<br>    ├── [Subagent] code-review subagent → FAIL → return findings + next steps<br>    ├── [Subagent] Task-check subagent → FAIL → return issues<br>    ├── [Bash] submit-pr subagent → captures output<br>    ├── Check reviewDecision (CHANGES_REQUESTED = not mergeable)<br>    ├── Address ALL CodeRabbit feedback (comments + nitpicks)<br>    ├── [Bash] Re-run submit-pr if fixes made<br>    └── Return SUCCESS with PR ready to merge</pre><p>Most of the steps are bash or subagent, removing some orchestration and decision-making from the main agent. But the main agent still has to orchestrate the workflow (because subagents cannot spawn other subagents… I tried that).</p><p>The main agent has just implemented a feature so it’s context is window is occupied. It’s not fully focused on orchestrating this process and following the precise instructions, it’s a bit tipsy wobbling from side-to-side.</p><p>It sometimes handles responses from each step in different ways, like not responding to CodeRabbit feedback, or re-running the submit-pr step without starting the full pipeline again.</p><p>Rather than keep trying to write better prompts and pleading harder with Claude, I decided to use real code.</p><h3>Real Code</h3><p>With real code you get determinism. The orchestration logic is mechanical based on simple rules. You don’t need an LLM for that. The only part of the process that needs an LLM is the code review. So I put all of the orchestration in code and used Claude Code SDK for the code review steps.</p><p>What I love about the solution is that it’s largely type safe. The input and output of each step of the workflow is strongly typed. I’m using Zod schemas in TypeScript.</p><p>Now, the workflow looks like this…</p><h4>Claude Command</h4><p>Rather than being given a workflow to orchestrate, Claude is just told to run an nx command in the /.claude/commands/complete-task command.</p><pre># Complete Task<br><br>Run the complete-task pipeline to verify, review, and submit your work.<br><br>## Instructions<br><br>Run this command with a **10-minute timeout**:<br><br>```bash<br>pnpm nx run dev-workflow:complete-task<br>```<br><br>This is a long-running command that:<br>- Runs local verification (lint, typecheck, test)<br>- Executes code review agents<br>- Submits PR and waits for CI checks<br><br>Parse the JSON response and follow the `nextInstructions` field exactly.</pre><p>It’s now a black box. Claude doesn’t know about the mechanics, it just waits for a result indicating the task succeeded or failed. If it failed there will be a list of errors to deal with.</p><h4>Dev workflow as code</h4><p>Now the complete-task command is implemented like this:</p><pre>runWorkflow&lt;CompleteTaskContext&gt;(<br>  [<br>    verifyBuild,<br>    codeReview,<br>    submitPR,<br>    fetchPRFeedback,<br>  ],<br>  buildCompleteTaskContext,<br>  (result: WorkflowResult, ctx: CompleteTaskContext) =&gt; formatCompleteTaskResult(result, ctx.prUrl),<br>)</pre><ol><li>verifyBuild — runs build, lint, test</li><li>codeReview — spins up multiple Claude Code subagents to do the review</li><li>submitPr — creates PR on GitHub and waits for checks to complete</li><li>fetchPRFeedback — queries the PR for the status of all checks and all unresolved conversations (both bots like CodeRabbit and humans)</li></ol><p>I built a small library to extract away the mechanics so the workflows themselves are declarative (highly experimental don’t copy this!). And I use existing typescript SDKs for interacting with github.</p><h4>Composable workflow steps</h4><p>Each step in the worfklow conforms to the following types. This makes it easy to compose steps, and to decouple declarative steps from generic infrastructure for running steps.</p><pre>export function runWorkflow(steps: Step[]): void {<br>  executeWorkflow(steps).catch(handleWorkflowError)<br>}<br><br>export type Step = (ctx: WorkflowContext) =&gt; Promise&lt;StepResult&gt;<br><br>export type StepResult =<br>  | { type: &#39;success&#39; }<br>  | {<br>    type: &#39;failure&#39;<br>    nextAction: NextAction<br>    details: unknown<br>  }</pre><p>A step looks like this:</p><pre>export const verifyBuild: Step&lt;CompleteTaskContext&gt; = async () =&gt; {<br>  const result = await nx.runMany([&#39;lint&#39;, &#39;typecheck&#39;, &#39;test&#39;])<br><br>  if (result.failed) {<br>    return failure({<br>      type: &#39;fix_errors&#39;,<br>      details: result.output <br>    })<br>  }<br><br>  return success()<br>}</pre><p>I did look for existing libraries but didn’t find anything that seemed lightweight enough. Let me know if there is something lightweight and robust already available.</p><h3>Claude Code SDK</h3><p>We still need AI to perform the code review, and that can’t be done in pure code (you can invoke external services like CodeRabbit via CLI, but here we want to do a local code review with our own agent first).</p><p>Instead of the main Claude Code agent spinning up subagents. Now, TypeScript will orchestrate Claude Code agents like this:</p><pre>import { query } from &#39;@anthropic-ai/claude-agent-sdk&#39;<br><br>const result = await query({<br>                  prompt: opts.prompt,<br>                  options: {<br>                    model: opts.model,<br>                    maxTurns: 50,<br>                    outputFormat: {<br>                      type: &#39;json_schema&#39;,<br>                      schema: z.toJSONSchema(opts.outputSchema),<br>                    },<br>                  },<br>                })</pre><p>You pass in a prompt and your options and you get a result back. The outputFormat is extremely useful here. You can pass a json schema and the SDK will ensure the response from Claude is valid against the schema.</p><p>So my code review step looks like this:</p><pre>const reviewerNames = [&#39;code-review&#39;, &#39;bug-scanner&#39;]<br><br>const results = await runReviewers(reviewerNames, filesToReview, ctx.reviewDir, ctx.taskDetails)<br><br>async function runReviewers(<br>  names: readonly string[],<br>  filesToReview: string[],<br>  reviewDir: string,<br>  taskDetails?: {<br>    title: string<br>    body: string<br>  },<br>): Promise&lt;ReviewerResult[]&gt; {<br>  return Promise.all(<br>    names.map(async (name) =&gt; {<br>      const agentPath = `.claude/agents/${name}.md`<br>      const basePrompt = await readAgentPrompt(agentPath)<br>      const reportPath = `${reviewDir}/${name}.md`<br><br>      const promptParts = [basePrompt, &#39;\n\n## Files to Review\n\n&#39;, filesToReview.join(&#39;\n&#39;)]<br><br>      if (name === &#39;task-check&#39; &amp;&amp; taskDetails) {<br>        promptParts.push(<br>          `\n\n## Task Details\n\nTitle: ${taskDetails.title}\n\nBody:\n${taskDetails.body}`,<br>        )<br>      }<br><br>      const response = await claude.query({<br>        prompt: promptParts.join(&#39;&#39;),<br>        model: &#39;sonnet&#39;,<br>        outputSchema: agentResponseSchema,<br>        outputPath: reportPath,<br>      })</pre><p>The runReviewers method is based on a convention. It takes a list of reviewer names and for each one it looks in .claude/agents/ for a prompt matching the name which contains the review instructions.</p><p>Then it passes the prompt to the Claude SDK with the files to review and gets the output back in the valid JSON structure. You have to add instructions in the prompt to tell Claude to return the result in that format of course.</p><h3>Workflow enforcement</h3><p>One key point I think is worth mentioning is that when you automate your workflow, you don’t want Claude bypassing it and calling commands directly. That’s why hooks in Claude Code are important.</p><p>For example, I have these hooks that block Claude from directly running the gh cli tool and remind it of the correct dev workflow commands to run instead:</p><pre># Block direct git push - agents must use /complete-task workflow<br>if [[ &quot;$command&quot; =~ (^|[[:space:]])git[[:space:]]+push($|[[:space:]]) ]]; then<br>    jq -n &#39;{<br>      &quot;hookSpecificOutput&quot;: {<br>        &quot;hookEventName&quot;: &quot;PreToolUse&quot;,<br>        &quot;permissionDecision&quot;: &quot;deny&quot;,<br>        &quot;permissionDecisionReason&quot;: &quot;Blocked: Direct git push bypasses required workflow. Use /complete-task command instead, which runs the complete verification pipeline (lint, test, code review, PR submission) and prevents orphaned changes.&quot;<br>      }<br>    }&#39;<br>    exit 0<br>fi<br><br># Block ALL gh pr commands - must use dev-workflow tools<br>if [[ &quot;$command&quot; =~ (^|[[:space:]])gh[[:space:]]+pr($|[[:space:]]) ]]; then<br>    jq -n &#39;{<br>      &quot;hookSpecificOutput&quot;: {<br>        &quot;hookEventName&quot;: &quot;PreToolUse&quot;,<br>        &quot;permissionDecision&quot;: &quot;deny&quot;,<br>        &quot;permissionDecisionReason&quot;: &quot;Blocked: Do not use gh pr directly. Use:\n- /complete-task - Create/update PR, run reviews, submit, check CI\n- pnpm nx run dev-workflow:get-pr-feedback - Check PR feedback and status (mergeable?)&quot;<br>      }<br>    }&#39;<br>    exit 0<br>fi</pre><h3>Very rough</h3><p>The ideas in this post are the latest evolution of my software development workflow automation. I am learning and evolving the process every week.</p><p>I’m not telling you to do this, I’m just sharing that this is the best approach I have found so far. Conceptually, putting the orchestration in real code and shelling out to LLMs only where they are truly needed makes sense. And in practice it seems to be working.</p><p>Maybe there are tools that solve this in a better way or maybe I just need to learn how to write better prompts.</p><p>But regardless of the implementation, one thing seems inevitable to me: we’re going to see the SDLC being automated in 2026.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fab70d44b6ab" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/dev-workflows-as-code-fab70d44b6ab">Dev Workflows as Code</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Coding Agent Development Workflows]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/coding-agent-development-workflows-af52e6f912aa?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/af52e6f912aa</guid>
            <category><![CDATA[continuous-delivery]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 18:25:39 GMT</pubDate>
            <atom:updated>2026-01-12T11:53:33.667Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gvsyQiGv4QtPlZCk" /><figcaption>Credit <a href="https://unsplash.com/@beancurd">Choi sungwoo</a></figcaption></figure><p>I’ve been using Claude Code a lot for personal projects and work projects, and one of the most interesting aspects for me is shaping software development workflows. I think this is something we are all (or should be) thinking about and experimenting with right now.</p><p>It interests me because the workflows allow us to make AI behaviour more predictable and it allows us to bake quality and other personal and organizational conventions into our processes. I’m also seeing that it allows us to automate more so we can focus more of our efforts where we add the most value.</p><p>A few months ago <a href="http://nicktune.medium.com/minimalist-claude-code-task-management-workflow-7b7bdcbc4cc1">I wrote about this</a> but I’ve learned so much since then. I wanted to write another post to get all of this out of my head and structure my thoughts. And also to think about where this is heading and what will be different in 6 months and how to prepare for that now.</p><blockquote>In this post I focus on the workflow. In the <a href="https://medium.com/nick-tune-tech-strategy-blog/code-quality-foundations-for-ai-assisted-codebases-4880f5948394?postPublishedType=repub">last post</a> I focused on concrete techniques I’m using for baking quality into the process</blockquote><h3>Task Implementation</h3><p>Recently, I’ve been iterating a lot on the task implementation part of the workflow. I define this phase as from the moment where an agent/develoer starts working on a ticket (assigns it to themselves, marks it as in progress) until there is a pull request open ready to be merged (or requiring feedback).</p><p>On my personal project, I’ve been experimenting with the goal of 100% AI-generated code but with the highest code quality possible. I don’t know how it will turn out, but the lessons learned will be valuable.</p><p>In this context, my workflow defines the agent’s goal as:</p><blockquote><em>Work through the entire lifecycle autonomously until you have a mergeable PR or are blocked. Present the user with a completed pull request that is green and ready for review.</em></blockquote><p>And the <a href="https://github.com/NTCoding/living-architecture/blob/main/docs/workflow/task-workflow.md">workflow</a> looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JrZgDnVqQsoADAOimfmkag.png" /></figure><p>What I’m trying to achieve here is that the agent will start working on a ticket and notify me once the whole pull request is finished, meets all of our quality standards and conventions, and is ready for me to review.</p><blockquote>I think in 2026 we’ll see better tooling for orchestrating these types of workflows. Tooling that provides more determinism for gluing together different steps and processing structured inputs and outputs.</blockquote><h3>Shell scripts vs AI commands</h3><p>In the workflow above, there are a mix of Claude commands (i.e. prompts) and bash scripts. Where possible, I try to use bash scripts for deterministic behaviour. For example, starting a task involves creating a feature branch in a standard manner and marking the item as in progress.</p><p>For the /complete-task workflow, this is a prompt and uses subagents to handle different steps. It requires more interpretation and isn&#39;t so easy to automate with bash scripts. Although, there are certain parts that are automated with bash scripts like querying GitHub to get pull request feedback.</p><p>I use subagents for 2 reasons:</p><ul><li>when the main agent is working on a task, the context window will be full and it is less likely to follow instructions. A dedicated subagent with a fresh context is more likely to follow the instructions (it has a single clear focus)</li><li>it won’t pollute the main agent’s context window</li></ul><h3>Task Completion</h3><p>At the moment, task completion is the most complex part of my workflow. I wanted to use a subagent to orchestrate this process, but subagents can’t spawn other subagents.</p><pre>/complete-task<br>    │<br>    ▼<br>Main agent runs pipeline directly<br>    │<br>    ├── Verify gate → FAIL → return to main<br>    ├── code-review subagent → FAIL → return findings + next steps<br>    ├── Task-check subagent → FAIL → return issues<br>    ├── submit-pr subagent → captures output<br>    ├── Check reviewDecision (CHANGES_REQUESTED = not mergeable)<br>    ├── Address ALL CodeRabbit feedback (comments + nitpicks)<br>    ├── Re-run submit-pr if fixes made<br>    └── Return SUCCESS with PR ready to merge</pre><p>The first step is to run build, lint, test locally. Then it will run a local code review using the code review subagent and in parallel it does task-check. This is basically a QA - does the work that has been done match the requirements that are in the task (in this case the GitHub issue)</p><p>If both of those succeed, the next subagent will push the changes to GitHub and create a pull request. It will use the GitHub cli to wait for all of checks to complete:</p><pre>gh pr checks --watch --fail-fast -i 30</pre><p>Currently I have some general CI checks and CodeRabbit and SonarQube that automatically review all pull requests. So if any of these checks fail, or request changes, or leave any nitpicks, the process instructions the agent to automatically fetch them using the GitHub GraphQL API.</p><pre>gh api graphql -f query=&#39;<br>  query($pr: Int!) {<br>    repository(owner: &quot;owner&quot;, name: &quot;repo&quot;) {<br>      pullRequest(number: $pr) {<br>        reviewDecision<br>        reviewThreads(first: 100) {<br>          nodes {<br>            isResolved<br>            comments(first: 1) {<br>              nodes { body }<br>            }<br>          }<br>        }<br>      }<br>    }<br>  }&#39; -F pr=$PR_NUMBER</pre><p>At this point, if anything so far has failed, the agent is empowered to autonomously fix the issues. It is instructed to keep working and addressing feedback (from local checks or on the CI) until the PR is mergeable or it is stuck or needs some help.</p><p>It did take me a while to get this all setup, and I’m still improving the process, so there is a cost involved in setting all this up. But I’m at a point where it’s working and justifies the effort.</p><h3>Single source of truth for conventions</h3><p>Different AI agents and different tools are involved in this workflow. And it’s important that they are all aligned on the same conventions. Otherwise they might end up going round in circles and wasting time (local review says one thing and then CodeRabbit flags it as a problem for example).</p><p>So my recommendation is to have a single source of truth for your coding standards and conventions not coupled to any AI tool.</p><p>I put them in /docs/.... And then any agent or tool that is writing code or reviewing it can reference these documents — the agent writing the code, the local agent reviewing the code, and tools like CodeRabbit will all be aligned to your same conventions.</p><p>My coderabbit.yml looks like this:</p><pre>knowledge_base:<br>  code_guidelines:<br>    enabled: true<br>    filePatterns:<br>      - &quot;docs/conventions/software-design.md&quot;<br>      - &quot;docs/conventions/anti-patterns.md&quot;<br>      - &quot;docs/conventions/testing.md&quot;<br>      - &quot;docs/conventions/standard-patterns.md&quot;<br>      - &quot;docs/conventions/codebase-structure.md&quot;<br>      - &quot;docs/architecture/overview.md&quot;<br>      - &quot;docs/workflow/code-review.md&quot;</pre><p>One other recommendation is to point your code review tools and agents at your ADRs. I’ve actually seen an example of this where the code review bot provided a PR comment explaining that the implementation didn’t align with an ADR and referenced the ADR specifically. I don’t have ADRs in the example above (but I will add it).</p><h3>Build quality in, continuous improvement</h3><p>Once you have a workflow and you have your context (your conventions, architecture docs, ADRs, and coding standards that all agents reference), then things get really exciting. The process of continuous improvement begins. This is where I’ve been having a lot of fun.</p><p>The mindset I have is, “how can I improve the process so that any error or manual intervention that I’ve had to make can be avoided next time.” For example, when CodeRabbit or SonarQube identifies an issue, I try to research linting rules. Can I find a linting rule that will catch this problem locally before it gets to these tools?</p><p>If linting rules aren’t possible then I look at ways to add this to the coding conventions or architecture documentation so that either the main agent or the local code review will catch it first.</p><p>On the contrary, I also wonder if at some point I will remove some of these checks. Maybe I will skip the local checks and just let the agent build the feature and create the PR and let all of the review happen there. If I can optimize the local development cycle so that it produces high quality to spec, then we speed up the overall process by having fewer checks. I’m not there yet, I still like having the local checks and not relying on CodeRabbit 100%.</p><h3>Key challenges for teams and organizations</h3><p>Based on my learnings and trying to take all these ideas and see how they apply to modern organizations, there are a few intersting challenges that we’ll have to face.</p><h4>Defining standards</h4><p>The first challenge is around consistency and standards. I think AI-assisted workflows will work best when there are more standards and clearer guidelines to follow. Some teams already do this, but some teams have many implicity conventions and different team members prefer different principles and patterns.</p><p>I think teams that excel are going to be more explicit with their coding standards and conventions. I.e. the context.</p><h4>Continuous improvement mindset</h4><p>Another challenge I see is the continuous improvement aspect. For engineers who like writing code and building systems, it could be a big change in mindset. Now, a key part of the job is to automate software development and identify mistakes or improvement opportunities so that AI produces better code and architecture.</p><p>Rather than writing code, I think it might be more that we need to think about “how can we improve the workflow and context so that we don’t need to manually write this line of code”. Software engineering might be more workflow + context engineering.</p><h4>Organizational vs team conventions</h4><p>A challenge we always face is deciding at what level a convention applies. Where do we standardize on tech stacks and architecture patterns and where do teams have freedom to go in their own direction. And this will definitely be a big challenge for defining workflows.</p><p>Allowing teams to have freedom could be crucial, as they discover new tools and techniques that others can benefit from, or are more suited to their local context. Equally, having some standard choices around conventions and tooling can bake in a minimum level of good practices and improve efficiency.</p><p>As a starting point, I think a good middle ground is always to be principles-driven. So you might define an AI-development principle such as “Each team must have documented coding standards and ADRs that are accessible to all AI agents and code review tools”. Each team can then define their own standards and use their own tooling to satisfy this principle.</p><h3>Planning and Task Breakdown</h3><p>You might have noticed in the task workflow table earlier that I have steps for managing PRDs and creating tasks. This is an important part of the process that I didn’t dive into: the phase before implementation where you break down requirements into actionable tasks.</p><p>I work with a <a href="https://github.com/NTCoding/claude-skillz/blob/main/system-prompts/prd-expert.md">PRD specialist agent</a> to create the PRD first. This agent goes through a structured lifecycle (draft, planning, approved) and asks probing questions rather than just transcribing what I say. Once the PRD is approved, I activate it and use /create-tasks to break it down into specific implementation tasks.</p><p>This phase is actually really important because the quality of the task definition directly impacts the quality of the implementation. When you provide the AI with clear instructions, specific acceptance criteria, and edge cases to consider, the chances of getting the output you want go up significantly.</p><p>My /create-tasks command enforces a 10-section structure for every task including deliverable, context, traceability to PRD requirements, acceptance criteria, edge case scenarios, implementation guidelines, testing strategy, and verification commands. If any section can&#39;t be completed with specific details, the task gets rejected. This structure forces clarity upfront and ensures the implementing agent has everything it needs.</p><p>I built this myself because I wanted to learn from the ground up, but there are spec-driven frameworks out there that handle this phase. If you’re just getting started, it might be worth looking at what already exists rather than rolling your own.</p><h3>These are just my personal learnings</h3><p>I’m not saying these are the best practices. I don’t really know what the best practices are. And anyway, things are changing so quickly that all of this will be out of date soon anyway.</p><p>These are just my personal learnings from trial-and-error and also looking at what other people are doing. I hope you found some of this useful, and if you have any suggestions or ideas that are missing I would love to hear it. I’m in full learning mode.</p><p>Thanks to everyone who has already shared feedback with me. Every time I write a post or share an idea on LinkedIn I receive ideas and insights from other people which is really helping me.</p><p>I’ve learned a lot from many sources but <a href="https://www.linkedin.com/in/paul-hammond-bb5b78251/">Paul Hammond</a> and <a href="https://www.linkedin.com/in/simonmartinelli/overlay/about-this-profile/">Simon Martinelli</a> regularly provide feedback on LinkedIn and I’m always checking what they’re upto, so special mention to them and I definitely recommend following them.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af52e6f912aa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/coding-agent-development-workflows-af52e6f912aa">Coding Agent Development Workflows</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code Quality Foundations for AI-assisted Codebases]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/code-quality-foundations-for-ai-assisted-codebases-4880f5948394?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/4880f5948394</guid>
            <category><![CDATA[software-design]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Thu, 01 Jan 2026 10:32:28 GMT</pubDate>
            <atom:updated>2026-01-07T18:30:01.688Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zl6eY1eHr0kn6p-C2IMYPQ.png" /></figure><p>There are a few basic techniques you can use to increase the quality of code produced by AI coding assistants. These are easy techniques you can apply to any codebase so I highly encourage you to spend a little time setting them up.</p><p>The techniques fall into three broad groups</p><ul><li><strong>Rules</strong>: define coding quality and standards</li><li><strong>Reviews</strong>: feedback loops to verify produced output</li><li><strong>Blocks</strong>: hard checks to ensure rules and reviews aren’t bypassed</li></ul><p>In this post I’ll share the real examples from my open source side project <a href="https://github.com/NTCoding/living-architecture">living-architecture.</a></p><h3>Rules</h3><p>Rules define what code quality means in your codebases. I bundle standards, principles, and conventions all under this generic umbrella.</p><h4>Lint rules</h4><p>Lint rules are one of the most effective techniques for baking in code quality since they are easy to enforce and many rules already exist, plus it’s easy to create your own.</p><p>My <a href="https://github.com/NTCoding/living-architecture/blob/main/eslint.config.mjs">lint config</a> contains rules like the following…</p><p><strong>Type safety</strong> <br>I’ve banned any and as type assertions. Claude Code loves to use these a lot and can’t be trusted with them. I never use them myself anyway, there’s always a better alternative.</p><pre>// No any types<br>&#39;@typescript-eslint/no-explicit-any&#39;: &#39;error&#39;,<br>&#39;@typescript-eslint/no-unsafe-assignment&#39;: &#39;error&#39;,<br>&#39;@typescript-eslint/no-unsafe-member-access&#39;: &#39;error&#39;,<br>&#39;@typescript-eslint/no-unsafe-call&#39;: &#39;error&#39;,<br>&#39;@typescript-eslint/no-unsafe-return&#39;: &#39;error&#39;,<br><br>// No type assertions - fix the types instead<br>&#39;@typescript-eslint/consistent-type-assertions&#39;: [<br>    &#39;error&#39;,<br>     { assertionStyle: &#39;never&#39; },<br> ],<br><br>// No non-null assertions - handle errors properly<br>&#39;@typescript-eslint/no-non-null-assertion&#39;: &#39;error&#39;,</pre><p><strong>Code complexity<br></strong>I’ve set a maximum code complexity per-function as 12 , maximum level of indentation as 3, and a maximum file size of 400.</p><p>When Claude hits these limits, it forces it to think about how to make the code more modular and it usually does a good job.</p><p>Without these limits Claude will absolutely create highly nested code and files with 1k+ lines.</p><pre>// Complexity limits<br>&#39;max-lines&#39;: [<br>    &#39;error&#39;,<br>    { max: 400, skipBlankLines: true, skipComments: true },<br> ],<br> &#39;max-depth&#39;: [&#39;error&#39;, 3],<br> complexity: [&#39;error&#39;, 12],</pre><p><strong>Naming<br></strong>I observed that Claude would use a lot of generic naming like helper and utils . It seems so lazy and just defaults to these generic names even when finding more accurate names isn’t hard.</p><p>So I use lint rules to ban the worst offenders. And it works. Claude hits the lint rule and has to think of a better name, and it always finds something more appropriate.</p><pre>// Ban generic folder imports (not lib - that&#39;s NX convention)<br>&#39;no-restricted-imports&#39;: [<br>    &#39;error&#39;,<br>        {<br>          patterns: [<br>            {<br>              group: [&#39;*/utils/*&#39;, &#39;*/utils&#39;],<br>              message: &#39;No utils folders. Use domain-specific names.&#39;,<br>            },<br>            {<br>              group: [&#39;*/helpers/*&#39;, &#39;*/helpers&#39;],<br>              message: &#39;No helpers folders. Use domain-specific names.&#39;,<br>            },<br>            {<br>              group: [&#39;*/common/*&#39;, &#39;*/common&#39;],<br>              message: &#39;No common folders. Use domain-specific names.&#39;,<br>            },<br>            {<br>              group: [&#39;*/shared/*&#39;, &#39;*/shared&#39;],<br>              message: &#39;No shared folders. Use domain-specific names.&#39;,<br>            },<br>            {<br>              group: [&#39;*/core/*&#39;, &#39;*/core&#39;],<br>              message: &#39;No core folders. Use domain-specific names.&#39;,<br>            },<br>            {<br>              group: [&#39;*/src/lib/*&#39;, &#39;*/src/lib&#39;, &#39;./lib/*&#39;, &#39;./lib&#39;, &#39;../lib/*&#39;, &#39;../lib&#39;],<br>              message: &#39;No lib folders in projects. Use domain-specific names.&#39;,<br>            },<br>          ],<br>        },<br>      ],</pre><h4>Test coverage</h4><p>In my <a href="https://github.com/NTCoding/living-architecture/blob/main/packages/riviere-cli/vitest.config.mts">vitest configs</a> I set test coverage to 100%. Sometimes you might need to set more granular rules but I recommend starting out with 100 for everything and iterating from there.</p><pre>coverage: {<br>    reportsDirectory: &#39;./test-output/vitest/coverage&#39;,<br>    provider: &#39;v8&#39; as const,<br>    exclude: [&#39;**/*test-fixtures.ts&#39;],<br>    thresholds: {<br>      lines: 100,<br>      statements: 100,<br>      functions: 100,<br>      branches: 100,<br>    },<br>},</pre><p>Claude will often miss edge cases, even with detailed planning. 100% coverage requirements help to catch them. They don’t guarantee perfect tests but it’s better to have these thresholds than not having them.</p><h4>Standards and conventions</h4><p>For other coding standards, principles and conventions I have a /docs/conventions folder:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/530/1*cnSy4fb3l-S7srpgMx72eQ.png" /></figure><p>Then these are referenced in the main <a href="https://github.com/NTCoding/living-architecture/blob/main/CLAUDE.md">CLAUDE.md</a> so that Claude knows when to read them.</p><pre>## Code Conventions<br><br>When writing, editing, refactoring, or reviewing code: <br><br>- always follow `docs/conventions/software-design.md` <br>- look for standard implementation patterns defined in `docs/conventions/standard-patterns.md`<br>- avoid `@docs/conventions/anti-patterns.md`<br><br>The automatic code review agent enforces these conventions (see `./claude/automatic-code-review/rules.md`)<br><br>Code quality is of highest importance. Rushing or taking shortcuts is never acceptable.<br><br>## Testing<br><br>Follow `docs/conventions/testing.md`.<br><br>100% test coverage is mandatory and enforced.</pre><h3>Reviews</h3><p>For anything that is not enforced by a tool (i.e. lint and test coverage) I recommend setting up reviews where a separate, specialist agent will review the work that has been done and provide feedback to the main agent.</p><p>In living-architecture I have fully automated reviews by a second agent that take place before the human in the loop is asked to review the work.</p><h4>Automated code review</h4><p>In a <a href="https://medium.com/nick-tune-tech-strategy-blog/auto-reviewing-claudes-code-cb3a58d0a3d0">previous post</a> I mentioned the automatic code review technique I had setup with Claude Code hooks. In living-architecture I have found this to be extremely useful.</p><p>The automatic code review runs every time Claude has finished. It automatically kicks in. In living-architecture I set up the automatic code review to <a href="https://github.com/NTCoding/living-architecture/blob/main/docs/workflow/code-review.md">re-inforce the same conventions</a> that are in CLAUDE.md</p><pre><strong>Architecture, modularity check</strong><br>Check all production code files (not test files) against the following conventions:<br><br>Read @/docs/architecture/overview.md Read @/docs/conventions/codebase-structure.md<br><br>Ensure that all code is in the correct place and aligns with boundaries and layering requirements. Look at each line of code and ask &quot;What is the purpose of this code? Is it related to other code? Is it highly cohesive? Should it really be here or would it fit better somewhere else?&quot;<br><br><strong>Coding Standards</strong><br>Check all production code files (not test files) against the following conventions:<br><br>Read @/docs/conventions/software-design.md <br>Read @/docs/conventions/standard-patterns.md</pre><p>Claude will not always follow standards and guidelines, no matter how great your prompts and CLAUDE.mdfile are. But with a dedicated agent focused on review fewer things slip through the net the chance of the standards being applies increases substantially.</p><p>Whenever you see bad code, add the convention or pattern to one of the files and next time the reviewer will automatically pick it up.</p><h4>Functionality review</h4><p>Similar to auto code review I setup an auto <a href="https://github.com/NTCoding/claude-skillz/blob/main/task-check/agents/task-check.md">task check</a>:</p><ul><li><a href="https://github.com/NTCoding/living-architecture/blob/main/docs/workflow/task-workflow.md">/docs/workflow/task-workflow.md</a> that tells Claude to run the task-check command when it’s finished a piece of work</li><li>CLAUDE.md references /docs/workflow/task-workflow.md</li><li>task-check compares the work done to the requirements</li></ul><p>This is where defining requirements is crucial. My approach is to <a href="https://github.com/NTCoding/claude-skillz/blob/main/system-prompts/prd-expert.md">write PRDs</a> and then break them down into <a href="https://github.com/NTCoding/living-architecture/blob/main/docs/workflow/task-workflow.md">tasks stored in task master</a>.</p><p>So when I start a new session I say “next task” and Claude picks the next ticket and marks it in progress and starts working on it. Then, the reviewer agent knows what to check.</p><blockquote>It’s also a good idea to use code quality tools like <a href="https://codescene.io/docs/developer-tools/mcp/codescene-mcp-server.html">CodeScene</a>, <a href="https://www.sonarsource.com/products/sonarqube/mcp-server/">Sonarqube</a> and <a href="https://www.coderabbit.ai/">CodeRabbit</a>. The tools can built in your CI pipeline and also provide local feedback to your coding assistants via MCP.</blockquote><blockquote>In living-architecture — I use SonarQube and CodeRabbit.</blockquote><h3>Blocks</h3><p>Whatever rules and guidelines you try to put in place, your AI assistant is going to try and find ways around them. So here are some simple techniques you can use to prevent that.</p><h4>git hooks</h4><p>I highly recommend using git hooks. I do a full lint, typecheck, test verification for the whole monorepo on <a href="https://github.com/NTCoding/living-architecture/blob/main/.husky/pre-commit">each commit</a>.</p><p><a href="https://github.com/NTCoding/living-architecture/blob/main/.husky/pre-commit">NX</a> has an effective caching system which means the performance of this is quite reasonable for the high level of confidence it provides.</p><h4>Prevent dangerous operations and file modification</h4><p>All of the rules you set up can be bypassed AI. For example, when the git hooks fail Claude will always try to use --no-verify to bypass checks.</p><p>You can use Claude hooks to prevent these dangerous commands:</p><pre>&quot;PreToolUse&quot;: [<br>      {<br>        &quot;matcher&quot;: &quot;Bash&quot;,<br>        &quot;hooks&quot;: [<br>          {<br>            &quot;type&quot;: &quot;command&quot;,<br>            &quot;command&quot;: &quot;$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous-commands.sh&quot;<br>          }<br>        ]<br>      }<br>    ]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V3kRkdEfXW4v_KIFE_cVsQ.png" /></figure><p>I also recommend setting up file modification rules so that AI cannot modify your lint or test coverage configuration files.</p><h4>Branch protections</h4><p>If you want to enforce that all commits to main go through your CI process then it’s good to setup branch protections on your repository.</p><p>In living-architecture pushes to main are forbidden and AI must satisfy <a href="https://www.sonarsource.com/">SonarQube</a> and <a href="https://www.coderabbit.ai/">CodeRabbit</a> checks. I have my CodeRabbit <a href="https://github.com/NTCoding/living-architecture/blob/main/.coderabbit.yaml">setup to run the same code conventions</a> that Claude will check locally:</p><pre>knowledge_base:<br>  code_guidelines:<br>    enabled: true<br>    filePatterns:<br>      - &quot;docs/conventions/software-design.md&quot;<br>      - &quot;docs/conventions/anti-patterns.md&quot;<br>      - &quot;docs/conventions/testing.md&quot;<br>      - &quot;docs/conventions/standard-patterns.md&quot;<br>      - &quot;docs/conventions/codebase-structure.md&quot;<br>      - &quot;docs/architecture/overview.md&quot;<br>      - &quot;docs/workflow/code-review.md&quot;</pre><p>With this approach, even if someone doesn’t run Claude or do the checks locally, the CI will ensure that those checks happen.</p><p>For consistency, I try to have a single source of truth for my rules and conventions, and then each tool like CLAUDE.md or .coderabbit.yml just points to the source of truth.</p><p>Both SonarQube and CodeRabbit are free for open source projects.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4880f5948394" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/code-quality-foundations-for-ai-assisted-codebases-4880f5948394">Code Quality Foundations for AI-assisted Codebases</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Auto-Reviewing Claude's Code]]></title>
            <link>https://medium.com/nick-tune-tech-strategy-blog/auto-reviewing-claudes-code-cb3a58d0a3d0?source=rss-d012fdeb34b0------2</link>
            <guid isPermaLink="false">https://medium.com/p/cb3a58d0a3d0</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-design]]></category>
            <category><![CDATA[domain-driven-design]]></category>
            <dc:creator><![CDATA[Nick Tune]]></dc:creator>
            <pubDate>Sun, 30 Nov 2025 18:54:06 GMT</pubDate>
            <atom:updated>2025-11-30T18:54:06.742Z</atom:updated>
            <content:encoded><![CDATA[<h3>Auto-Reviewing Claude’s Code</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BMd2h3cx7wiXJcI6gykUXA.png" /></figure><p>A well-crafted system prompt will increase the quality of code produced by your coding assistant. It does make a difference. If you provide guidelines in your system prompt for writing code and tests, coding assistants will follow the guidelines.</p><p>Although that depends on your definition of “will follow”. If your definition is “will follow often” then it’s accurate. If your definition is “will follow always” or even “will follow most of the time” then it’s inaccurate (unless you’ve found a way to make them reliable that I haven’t, please let me know).</p><p>Coding agents will ignore instructions in the system prompt on a regular basis. As the context window fills up and starts to intoxicate them, all bets are off.</p><p>Even with the latest Opus 4.5 model, I haven’t noticed a major improvement. So if we can’t rely on models to follow system prompts, we need to invest in feedback cycles.</p><p>I’ll show you how I’m using Claude Code hooks to implement automatic code review on all AI-generated code, so that code quality is higher before it reaches the human in the loop.</p><blockquote>A code example that demonstratest the concepts discussed in this post is on my github: <a href="https://github.com/NTCoding/claude-skillz/tree/main/automatic-code-review">https://github.com/NTCoding/claude-skillz/tree/main/automatic-code-review</a></blockquote><blockquote>All my posts are 100% human written — all mistakes (and em dashes) are my own</blockquote><h3>Auto code review for fast, semantic feedback</h3><p>When I talk about auto code review in this post, I am describing a fast feedback mechanism intended to review common code quality issues. This will be run whenever Claude has finished making edits so it needs to be fast and efficient.</p><p>I also use coding assistants for detailed code reviews when reviewing a PR, for example. That will spin up multiple subagents and take a bit longer. That’s not what I’m talking about here.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tNHtlRpCPqM9tA0ZFEqQxA.jpeg" /></figure><p>The purpose of the auto code review is to reinforce what’s in your system prompt, project documentation, and on-demand skills. Things that Claude may have ignored. Part of a multi-pronged approach.</p><p>Wherever possible, I recommend using your lint and test rules to bake in quality, and leave auto code review for more semantic issues that tools can’t check.</p><p>If you want to set a maximum length for your files or maximum level of indentation, then uses your lint tool. If you want to enforce a minimum test coverage, use your test framework.</p><h4>Semantic code review</h4><p>A semantic code review looks at how well the code is designed. For example, naming. Does the code accurately describe the business concepts it represents?</p><p>AI will often default to names like “helper” and “utils”. But AI is also good at understanding the nuance and finding better names if you challenge it, and it can do this quickly. So this is a good example of a semantic rule.</p><p>You can ban certain words like “helper” and “utils” with lint tools (I recommend doing that). But that won’t catch everything.</p><p>Another example is logic leaking out of the domain model. When a use case / application service queries an entity and then makes a decision, it’s highly like your domain logic is leaking into the application layer. Not so easy to catch with lint tools, but worth addressing.</p><pre>class RandomUseCase {<br>  ...<br>  <br>  const customer = repository.load(customerId)<br><br>  // query the domain then make a decision based on it&#39;s state. <br>  // common anti-pattern. The customer entity should protect its invariants<br>  if (customer.isActive()) {<br>    customer.awardLoyalty(...)<br>  }<br>  ...<br>}</pre><p>Another example is default fallback values. When Claude has an undefined value where a value is expected, it will set a default value. It seems to hate throwing exceptions or challenging the type signature and asking “should we allow undefined here?”. It wants to make the code run no matter what and no matter how much the system prompt tells it not to.</p><pre><br>const paymentType = command.paymentType ?? &#39;bank-transfer&#39; // Bad - we shouldn&#39;t guess or assume. But AI will do this a lot<br><br>const age = request.age ?? 18 // probably a bad default value<br><br>const effectiveDate = event.effectiveDate ?? new Date() // This is probably ok<br><br>const isEnabled = feature.enabled ?? false // This could be ok</pre><p>You can catch some of this with lint rules but it’s very nuanced and depends on the context. Sometimes falling back to a default value is correct.</p><h3>Building an auto code review with Claude hooks</h3><p>If you’re using Claude Code and want to build an auto code review for checks that you can’t easily define with lint or testing tools, then a solution is to configure a script that runs on the Stophook.</p><p>The Stophook is when Claude has finished working and passes control back to the user to make a decision. So here, you can trigger a subagent to perform the review on the modified files.</p><p>To trigger the subagent you need to return the error status code which blocks the main agent and forces them to read the output.</p><pre>⏺ Ran 1 stop hook<br>  ⎿  Stop hook error: 📋 CODE REVIEW REQUIRED<br><br>  Files modified since last review:<br>  - /.../src/features/flows/components/CodeLinkMenu/C<br>  odeLinkMenu.tsx<br><br>  INSTRUCTION: Use the Task tool with subagent_type &quot;automatic-code-reviewer&quot;. Pass only the<br>  file list as the prompt. The agent will follow its configured review procedure. SHOW ALL<br>  FINDINGS TO THE USER.<br><br><br>⏺ automatic-code-review:automatic-code-reviewer(Review modified code)<br>  ⎿  Done (19 tool uses · 18.0k tokens · 52s)<br><br>⏺ Code Review Findings<br><br>  The automatic code reviewer found 3 violations related to domain modeling:<br><br>  Violations Found<br><br>  1. [DOMAIN MODELING] - CodeLinkMenu.tsx:6<br>    - Issue: filePath is a bare string primitive representing a domain concept. Should use a<br>  branded type like FilePath to enforce type safety.<br>    - Fix: Create a branded type type FilePath = string &amp; { readonly __brand: &#39;FilePath&#39; } and<br>  update the parameter to filePath: FilePath</pre><p>I think it’s generally considered a best practice to use subagent focused on the review with a very critical mindset. Asking the main agent to mark it’s own homework is obviously not a good approach and it will use up context window.</p><blockquote>The solution I use is <a href="https://github.com/NTCoding/claude-skillz/tree/main/automatic-code-review">available on github</a>. You can install it as a plugin in your repo and customize the code review instructions or just use it as inspiration for your own solution. Any feedback is welcome, btw</blockquote><p>In the example above you can see it took 52s. Probably quicker than me reviewing providing the feedback myself. But that’s not always the case, sometimes it can take a few minutes.</p><p>If you’re sitting there blocked waiting for review, this might be slower than doing it yourself. But if you’re not blocked and are working on something else (or watching TV), this saves you time because the end result will be higher quality and require less of your time to review and fix.</p><h4>Scanning for updated files</h4><p>I want my auto code review to only review files that have been modified since the last pull request. But Claude doesn’t provide this information the context to the Stop hook.</p><p>I can find all files modified or unstaged using git, but that’s not good enough.</p><p>What I do instead is to hook into PostToolUse by keeping a log of each modified file.</p><pre>&quot;PostToolUse&quot;: [<br>   {<br>    &quot;matcher&quot;: &quot;Write|Edit|MultiEdit&quot;,<br>    &quot;hooks&quot;: [<br>      {<br>         &quot;type&quot;: &quot;command&quot;,<br>         &quot;command&quot;: &quot;${CLAUDE_PLUGIN_ROOT}/hooks/tools/automatic-code-review-plugin.sh log&quot;<br>       }<br>     ]<br>   }</pre><p>When the Stop hook is triggered, the review will find the files modified since the last review and ask the subagent to review only those. If there are no modified files, the code review is not activated.</p><h4>Challenges with the stop hook</h4><p>Unfortunately the stop hook is not 100% reliable for this use case for a few reasons. Firstly, Claude might stop to ask a question, e.g. for you to clarify some requirements. You might not want the auto review to trigger here until you’ve answered Claude and it has finished..</p><p>The second reason is that Claude can commit changes before the Stop hook. So by the time the subagent performs the review, the changes are already committed to git.</p><p>That might not be a problem and there are simple ways to solve it if it is. It’s just extra things to keep in mind and setup.</p><p>The ideal solution would be for Anthropic (or other tool vendors) to provide us hooks that are higher level in abstraction — more aligned with the software development worklow and not just low level file modification operations.</p><p>What I would really love is a CodeReadyForReview hook which provides all the files that Claude has modified — then we can throw away our custom solutions.</p><h3>Let me know if you have a better approach</h3><p>I don’t know if I’m not looking in the right places or if the information isn’t out there, but I feel like this solution is solving a problem that should already be solved.</p><p>I’d be really grateful if you can share any advice that helps to bake in code quality before the human in the loop has to review it.</p><p>Until then I’ll continue to use this auto code review solution. When you’re giving AI some autonomy to implement tasks and reviewing what it produces, this is a useful pattern that can save you time and reduce frustration from having to repeat the same feedback to AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cb3a58d0a3d0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/nick-tune-tech-strategy-blog/auto-reviewing-claudes-code-cb3a58d0a3d0">Auto-Reviewing Claude&#39;s Code</a> was originally published in <a href="https://medium.com/nick-tune-tech-strategy-blog">Nick Tune’s weird ideas</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>