<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harshit Rathod</title>
    <description>The latest articles on DEV Community by Harshit Rathod (@harshit_rathod).</description>
    <link>https://dev.to/harshit_rathod</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3972207%2F3affed39-1b37-462a-8eb0-c163842a5353.jpg</url>
      <title>DEV Community: Harshit Rathod</title>
      <link>https://dev.to/harshit_rathod</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harshit_rathod"/>
    <language>en</language>
    <item>
      <title>Beyond Vibe Coding: From AI-Assisted Coding to Agentic SDLC Automation</title>
      <dc:creator>Harshit Rathod</dc:creator>
      <pubDate>Sat, 12 Sep 2026 15:52:54 +0000</pubDate>
      <link>https://dev.to/harshit_rathod/beyond-vibe-coding-from-ai-assisted-coding-to-agentic-sdlc-automation-918</link>
      <guid>https://dev.to/harshit_rathod/beyond-vibe-coding-from-ai-assisted-coding-to-agentic-sdlc-automation-918</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hook:&lt;/strong&gt; Vibe coding works beautifully right up until it doesn't. You prompt, you skim, you accept, you move on — and it's genuinely faster, for a while. Then you hit a feature that spans fifteen repositories, and the model confidently puts the table in the wrong service.&lt;/p&gt;

&lt;p&gt;The problem was never the model's code. It's that nobody decided which repository owned the change, which endpoints were needed, or in what order twelve tickets had to merge so nobody's branch conflicted. That decomposition work &lt;em&gt;is&lt;/em&gt; the engineering — and it's the part vibe coding has no answer for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This describes the architecture of an internal engineering tool. Product specifics, repository names, and proprietary conventions are generalized. The patterns transfer; our config files wouldn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentic SDLC at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An AI development agent that takes a blank Jira story and returns a stack of reviewed merge requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it replaces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ad-hoc prompting (vibe coding) for work that spans multiple repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it automates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Planning, ticket decomposition, dependency ordering, TDD implementation, code review, MR creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DB → Backend → Shared UI → App UI → E2E → Review → MR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human gates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2 — plan approval, and per-ticket approval. Nothing else requires a human&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agentic CLI plugin + MCP tool server + prose workflow rules + on-demand skills + multi-model routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Guardrail model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independent model review of tests &lt;em&gt;and&lt;/em&gt; code; 5-failure stop; no work on unapproved tickets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Implementation size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~7,800 lines, almost entirely English prose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scale measured&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;35+ stories · 500+ tickets · ~11,400 tests · ~86% line coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Impact measured&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1,650 manual hours avoided (~90% of estimated manual effort)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Longest unattended run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~12 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vibe coding doesn't scale past a single repository.&lt;/strong&gt; It optimizes the step that was never the bottleneck: typing the code. Decomposition, sequencing, and verification are what actually consume the calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decomposition, not code generation, is the bottleneck&lt;/strong&gt; in multi-repo development. An AI coding assistant that only writes code solves the easy half.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An AI development agent is not just an LLM.&lt;/strong&gt; It's LLM reasoning + deterministic orchestration + curated context + skills + rules + tools + verification. Remove any one and it stops being trustworthy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your agent's business logic should be prose, not code&lt;/strong&gt; — a workflow contract read fresh at the start of every run, changed via pull request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose determinism over parallelism.&lt;/strong&gt; A single linear dependency chain guarantees exactly one ticket is ready at a time, which makes an autonomous loop trivially correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stacked branches keep AI output reviewable.&lt;/strong&gt; One MR per ticket, each targeting its predecessor's branch. Reviewability — not generation speed — is the real bottleneck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A model that writes both the tests and the code will write tests its code passes.&lt;/strong&gt; Independent review by a &lt;em&gt;different&lt;/em&gt; model is what breaks that loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measured results:&lt;/strong&gt; 35+ stories, 500+ tickets, ~1,650 manual hours avoided, ~11,400 tests at ~91% line coverage, longest unattended run ~12 hours.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Answer: What Is Agentic SDLC Automation?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agentic SDLC automation is the practice of giving an AI system ownership of a complete software delivery workflow — requirement intake, planning, task decomposition, implementation, testing, and code review — under deterministic orchestration and explicit verification gates, rather than generating code on request.&lt;/strong&gt; The distinction from vibe coding is scope and accountability: a prompt answers "write this function," an agentic system answers "deliver this story, in the right repositories, in the right order, with tests someone reviewed."&lt;/p&gt;

&lt;p&gt;Ours takes a blank Jira story key plus context and returns a stack of reviewed merge requests. It fetches the latest code from every affected repository, drafts a plan, writes one markdown file per proposed ticket for local review, publishes the plan to the wiki, and stops. You say LGTM. It creates the child tickets as a single linear dependency chain and stops again. You approve tickets. It then develops each in strict order — DB → Backend → Shared UI → App UI → E2E — writing tests first, getting them reviewed, implementing, getting the code reviewed, and opening one merge request per ticket on stacked branches.&lt;/p&gt;

&lt;p&gt;Two human gates. Everything between them is autonomous.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vibe Coding vs AI Coding Assistant vs Agentic SDLC
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;These are three points on one progression, not three competing tools.&lt;/strong&gt; Each solves the bottleneck the previous one exposed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Vibe coding&lt;/th&gt;
&lt;th&gt;AI coding assistant&lt;/th&gt;
&lt;th&gt;Agentic SDLC automation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit of work&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A prompt&lt;/td&gt;
&lt;td&gt;A function, file, or diff&lt;/td&gt;
&lt;td&gt;A story, decomposed into tickets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who decomposes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nobody — you improvise&lt;/td&gt;
&lt;td&gt;You do, before prompting&lt;/td&gt;
&lt;td&gt;The agent proposes; you approve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None — you sequence it&lt;/td&gt;
&lt;td&gt;Dependency-aware execution chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State across steps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chat history&lt;/td&gt;
&lt;td&gt;Conversation context&lt;/td&gt;
&lt;td&gt;Ticket board + branches + MRs as durable state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository awareness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whatever's open&lt;/td&gt;
&lt;td&gt;The open file or workspace&lt;/td&gt;
&lt;td&gt;Explicit repo map, module ownership, shared-library rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vibes — you skim and accept&lt;/td&gt;
&lt;td&gt;You run the tests&lt;/td&gt;
&lt;td&gt;Tests written first, reviewed by a second model, gated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Silently wrong, found later&lt;/td&gt;
&lt;td&gt;Returns a wrong answer confidently&lt;/td&gt;
&lt;td&gt;Stops after 5 consecutive failures, posts a blocker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code in your editor&lt;/td&gt;
&lt;td&gt;Code you paste&lt;/td&gt;
&lt;td&gt;Stacked merge requests with test evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Accept or re-prompt&lt;/td&gt;
&lt;td&gt;Prompt and review each output&lt;/td&gt;
&lt;td&gt;Approve at two gates; review MRs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scales to&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One repo, one developer&lt;/td&gt;
&lt;td&gt;One repo, one task at a time&lt;/td&gt;
&lt;td&gt;A story spanning many repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vibe coding removed the friction of typing code. Assistants removed the friction of writing &lt;em&gt;correct&lt;/em&gt; code for a scoped task. Neither touched the part that actually consumes a sprint: deciding what the tickets are, which repository each belongs to, what order they merge in, and whether the result is trustworthy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition — Agentic SDLC automation:&lt;/strong&gt; A software delivery workflow in which an AI system executes planning, decomposition, implementation, testing, and review under deterministic orchestration, with explicit human approval gates and automated verification — as opposed to ad-hoc prompting with human-only review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definition — Autonomous development loop:&lt;/strong&gt; A control loop that polls a ticket system for the next ready task, executes a full development workflow against it, and repeats without human input until every task is complete or a guardrail halts it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Vibe Coding Stalls at the Repository Boundary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vibe coding has a real domain where it wins.&lt;/strong&gt; One repository, one developer, a task you could have scoped in your head anyway — prompt, skim, accept, move on. The loop is fast because the coordination cost is zero.&lt;/p&gt;

&lt;p&gt;That cost is not zero in a microservices monorepo, where &lt;strong&gt;one feature is never one change.&lt;/strong&gt; A single story routinely means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2–3 database migrations across different services&lt;/li&gt;
&lt;li&gt;8–10 backend endpoints in two or three repositories&lt;/li&gt;
&lt;li&gt;Several new shared UI components, plus the pages consuming them&lt;/li&gt;
&lt;li&gt;API integration work per endpoint on the frontend&lt;/li&gt;
&lt;li&gt;End-to-end coverage per user journey&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these lives in an &lt;strong&gt;independent Git repository&lt;/strong&gt; — its own &lt;code&gt;main&lt;/code&gt;, its own CI, its own merge queue. Landing them in the right order without merge conflicts or half-integrated states is a coordination problem long before it's a coding problem.&lt;/p&gt;

&lt;p&gt;Give a coding assistant the ticket "Add crop planning" and it will confidently start writing something. The question it can't answer alone is &lt;em&gt;which repository, which module, which order, and what does this depend on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is where the vibe-coding loop inverts. Accepting a plausible diff is cheap; discovering three days later that the migration landed in the wrong service is not. &lt;strong&gt;The faster you generate unverified code across repository boundaries, the more expensive the correction becomes&lt;/strong&gt; — you're not saving time, you're borrowing it at interest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every under-specified ticket you hand an agent is a ticket where it guesses — and a guess in ticket #3 propagates through tickets #4 through #12.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where the Work Actually Goes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before building anything, we looked at where the hours went.&lt;/strong&gt; Across the ticket types the agent now generates, the distribution is lopsided in a useful way:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ticket type&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;th&gt;Character of the work&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI (incl. shared components)&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;td&gt;Highly patterned — forms, tables, modals, API wiring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend endpoints&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;35%&lt;/td&gt;
&lt;td&gt;Highly patterned — controller, service, DTO, validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database migrations&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;Convention-heavy, high blast radius if wrong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E2E journeys&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;Scenario design, then mechanical execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Roughly 78% of tickets are backend endpoints and their frontend integration.&lt;/strong&gt; That's repetitive, convention-bound work where the decision space is narrow and the right answer is largely determined by what the codebase already does.&lt;/p&gt;

&lt;p&gt;That's precisely the shape of work an agent handles well — &lt;em&gt;if&lt;/em&gt; it's given a tight enough specification. Which brings us back to decomposition.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Anatomy of an AI Development Agent
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The most common misconception is that the model is the system.&lt;/strong&gt; It isn't. In our experience the LLM is one of seven components, and it's not the one that determines whether you can trust the output.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;What breaks without it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Planning, decomposition, implementation, review judgment&lt;/td&gt;
&lt;td&gt;Nothing works at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deterministic orchestration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed step order, dependency chain, state machine&lt;/td&gt;
&lt;td&gt;Non-reproducible runs; conflicting concurrent work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Curated context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Repo map, latest code fetched before planning&lt;/td&gt;
&lt;td&gt;Confident placement of code in the wrong service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stack conventions loaded on demand&lt;/td&gt;
&lt;td&gt;Output that passes tests but violates house style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The workflow contract, read fresh every run&lt;/td&gt;
&lt;td&gt;Behavior drifts from what the team agreed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Typed access to ticketing, wiki, git hosting&lt;/td&gt;
&lt;td&gt;Malformed API calls; no audit boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independent review of tests and code, hard gates&lt;/td&gt;
&lt;td&gt;Plausible code with worthless tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition — Agent orchestration:&lt;/strong&gt; The deterministic layer that decides &lt;em&gt;what runs next and in what order&lt;/em&gt;, leaving the LLM to decide &lt;em&gt;how&lt;/em&gt; each step is performed. Orchestration is ordinary code and configuration — not model output — which is what makes agent behavior reproducible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The split that matters: deterministic workflow, LLM reasoning.&lt;/strong&gt; Step order, dependency resolution, branch topology, gate enforcement, and stop conditions are all deterministic. Design judgment, code, tests, and review opinions come from the model. We never let the model decide &lt;em&gt;whether&lt;/em&gt; to run a gate — only what it concludes inside one.&lt;/p&gt;




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

&lt;h2&gt;
  
  
  Architecture Layer 1: An Agentic CLI Plugin, Not a Service
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The agent ships as a plugin for an agentic coding CLI.&lt;/strong&gt; This was the single highest-leverage decision.&lt;/p&gt;

&lt;p&gt;It inherits, for free: filesystem access, shell execution, git, a permissions model, session management, and model routing. We wrote &lt;strong&gt;zero&lt;/strong&gt; infrastructure code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dev-agent/
├── commands/          # slash commands — the entry points
├── rules/             # the workflow contract (authoritative)
├── skills/            # stack conventions, loaded on demand
├── dev-tools-mcp/     # MCP server — ticketing, wiki, git hosting
└── Plans/             # generated artifacts, reviewed by humans
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole thing is roughly &lt;strong&gt;7,800 lines&lt;/strong&gt; — and almost all of it is &lt;em&gt;English&lt;/em&gt;. The only real code is a ~1,200-line MCP server wrapping three REST APIs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In an agentic architecture, your business logic is prose. Leverage comes from the precision of your contract, not the cleverness of your implementation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture Layer 2: MCP as the Tool Boundary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The agent reaches external systems through a single Model Context Protocol server exposing 44 tools.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ticketing&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Read/update stories, create children, link dependencies, poll status, transition, comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wiki&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Search, read requirement docs, publish approved plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git hosting&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Branch existence, branch creation, MR create/get/list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why not just let the model shell out to &lt;code&gt;curl&lt;/code&gt;?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Typed contracts.&lt;/strong&gt; Each tool has a schema. The model cannot mis-shape a payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real audit boundary.&lt;/strong&gt; Every external mutation flows through one surface you can log, permission, and rate-limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure containment.&lt;/strong&gt; An API change breaks one Python function, not a hundred lines of prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege.&lt;/strong&gt; The server exposes exactly the operations the workflow needs. There is no generic "make an HTTP request" escape hatch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Git is the deliberate exception — it runs through the shell, because &lt;code&gt;git&lt;/code&gt; is already a precise CLI and the agent must operate inside whichever submodule it's working in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Layer 3: Rules as a Contract, Read Fresh Every Run
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;rules/&lt;/code&gt; holds the workflow specification, and the agent reads it at the start of every single run.&lt;/strong&gt; Not fine-tuned. Not buried in a system prompt. Read fresh.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule file&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;workflow.md&lt;/code&gt; (644 lines)&lt;/td&gt;
&lt;td&gt;The full contract — step order, both gates, TDD discipline, the 5-consecutive-failure stop, the 30-minute re-read rule, status flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;repositories.md&lt;/code&gt; (206 lines)&lt;/td&gt;
&lt;td&gt;Repo map, in-scope submodules, domain-module discovery, chain order, stacked-branch mechanics, MR targeting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;review-tests.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test-review checklist — the gate before any implementation is written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;review-code.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Code-review checklist — the gate before a ticket reaches In Review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;wiki-template.md&lt;/code&gt; (332 lines)&lt;/td&gt;
&lt;td&gt;Required structure of the published requirement doc&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rules are &lt;strong&gt;layered and overridable&lt;/strong&gt;: the agent checks for a project-level override before falling back to the plugin default. A team with stricter conventions overrides one file instead of forking the agent.&lt;/p&gt;

&lt;p&gt;Rereading the contract every run sounds wasteful. It's the opposite. Changing agent behavior becomes a &lt;strong&gt;pull request against a markdown file&lt;/strong&gt; — reviewable, diffable, revertible. No retraining, no redeploy, and no version skew between what the agent believes and what the team agreed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Layer 4: Skills Loaded on Demand
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stack conventions live in skills that load only when the agent touches matching files.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Loads when&lt;/th&gt;
&lt;th&gt;Covers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;editing &lt;code&gt;.sql&lt;/code&gt; migrations&lt;/td&gt;
&lt;td&gt;Naming, UUID v7 keys, mandatory per-tenant partitioning, tenant-leading indexes, Flyway, rollback discipline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;editing &lt;code&gt;.java&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Package layout, REST resources, DTOs/mappers, CDI, exception handling, red-green-refactor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java-test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;writing JUnit&lt;/td&gt;
&lt;td&gt;One behaviour per test, given/when/then, mocking strategy, naming format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;editing &lt;code&gt;.tsx&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Component structure, hooks, state, forms, accessibility, shared-component placement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;writing RTL tests&lt;/td&gt;
&lt;td&gt;Strategy by component type, query hierarchy, mocking, async patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jira&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;any ticket operation&lt;/td&gt;
&lt;td&gt;Search, create, update, transition, comment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is &lt;strong&gt;progressive disclosure applied to context&lt;/strong&gt;. The DB ticket never loads React conventions. The E2E ticket never loads migration rules. Context relevance, not context volume, is what degrades over a long run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why On-Demand Conventions Matter More Than They Sound
&lt;/h3&gt;

&lt;p&gt;Our platform is multi-tenant on a single database, so &lt;strong&gt;every table must be partitioned by tenant&lt;/strong&gt;. That one rule cascades into consequences that are not obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partitioned tables can't carry a simple primary key — so identity is a single UUID v7 column with &lt;strong&gt;no&lt;/strong&gt; primary key constraint&lt;/li&gt;
&lt;li&gt;Uniqueness becomes a unique index that must &lt;strong&gt;lead with the tenant column&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;There's no default partition, so writes for an unprovisioned tenant &lt;strong&gt;fail loudly&lt;/strong&gt; instead of silently landing in a catch-all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a chain a new engineer gets wrong on their first migration, every time. Written down once, it's enforced across all 42 DB tickets without anyone remembering to check.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Layer 5: Model Routing by Cognitive Load
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Not every step deserves the same model.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Model tier&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Planning &amp;amp; decomposition&lt;/td&gt;
&lt;td&gt;Frontier / reasoning&lt;/td&gt;
&lt;td&gt;Highest-leverage step; an error here propagates through every downstream ticket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test review&lt;/td&gt;
&lt;td&gt;Frontier / reasoning&lt;/td&gt;
&lt;td&gt;Adversarial judgment — is this test &lt;em&gt;real&lt;/em&gt; or tautological?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;Frontier / reasoning&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation (all types)&lt;/td&gt;
&lt;td&gt;Fast / balanced&lt;/td&gt;
&lt;td&gt;Constrained by tests already written and reviewed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The insight: &lt;strong&gt;implementation is the cheap part when the specification is tight.&lt;/strong&gt; Once tests exist and a strong model has reviewed them, writing code that passes is comparatively mechanical. Spend your reasoning budget where judgment compounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dependency-Aware Execution: One Chain, Not a Tree
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every generated ticket is blocked by exactly one predecessor and blocks exactly one successor.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB1 → DB2 → BE1 → BE2 → BE3 → UI-C1 → UI-C2 → FE1 → FE2 → E2E1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fan-out. Ever.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definition — Dependency-aware execution:&lt;/strong&gt; Ordering generated tasks so each begins only after the work it builds on is complete, so the agent never implements against code that doesn't exist yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A dependency DAG would be more "correct" — plenty of these tickets could genuinely run in parallel. We chose the chain anyway, because it guarantees one property worth more than theoretical throughput:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;At most one ticket is ready at any moment.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single invariant eliminates a whole class of failures: no two agents editing the same file, no duplicated shared components, no conflicts between sibling branches, no "which of these three branches has the migration?" archaeology. The loop that picks the next ticket becomes trivially correct — find the one approved ticket whose single predecessor is done.&lt;/p&gt;

&lt;p&gt;Parallelism is a tempting optimization. Determinism is what makes an autonomous system trustworthy enough to leave running for twelve hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Execution Order, and Why It's That Order
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DB&lt;/strong&gt; — migrations first, because every layer above depends on the schema existing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt; — endpoints against the committed schema, one ticket per endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared UI&lt;/strong&gt; — common components land in the shared library before anything consumes them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App UI&lt;/strong&gt; — pages and API integration, consuming those components rather than rebuilding them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E&lt;/strong&gt; — user journeys against the real stack, once there's a stack to test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt; — independent model review at the test gate and the code gate, per ticket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MR&lt;/strong&gt; — one merge request per ticket, stacked on its predecessor's branch&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Stacked Branches: One MR Per Ticket
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Within each repository, every ticket branches from its same-repo predecessor.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main
 └── STORY-100                    (integration branch)
      └── TICKET-101              MR → STORY-100
           └── TICKET-102         MR → TICKET-101
                └── TICKET-103    MR → TICKET-102
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each ticket's MR targets its predecessor's branch. When all child MRs merge into the integration branch, one final MR takes &lt;code&gt;STORY-100 → main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The payoff is reviewability. A reviewer opening &lt;code&gt;TICKET-102&lt;/code&gt; sees &lt;strong&gt;only&lt;/strong&gt; that ticket's diff — not the migration from &lt;code&gt;TICKET-101&lt;/code&gt; underneath it. Without stacking, the last MR in a chain of twelve shows the cumulative diff of all twelve, and review collapses into rubber-stamping.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generating code faster than humans can meaningfully review it isn't a throughput gain. It's risk, relocated downstream.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Verification and Guardrails Instead of Blind Autonomy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Autonomy without verification is just vibe coding at machine speed.&lt;/strong&gt; That's the trap worth naming: an agent that plans, decomposes, and implements without gates doesn't fix the accept-and-move-on problem — it industrializes it. Every ticket runs strict TDD with two mandatory review gates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write tests → [GATE] → Implement → Tests pass → [GATE] → MR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reviewing model emits either &lt;code&gt;APPROVED&lt;/code&gt; or &lt;code&gt;CHANGES REQUESTED:&lt;/code&gt; with a numbered, file-referenced list, and the implementing agent revises and resubmits.&lt;/p&gt;

&lt;p&gt;The test-review checklist carries most of the value. It explicitly hunts the failure mode that makes AI-generated tests worthless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No tautological tests&lt;/strong&gt; — asserting a mock returned what it was told to return proves nothing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific assertions&lt;/strong&gt; — not "no exception thrown" but actual value checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No flaky waits&lt;/strong&gt; — no &lt;code&gt;sleep(N)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realistic test data&lt;/strong&gt; — not &lt;code&gt;"abc"&lt;/code&gt; / &lt;code&gt;123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Explicit coverage of validation, authorization, edge cases, and state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code-review checklist adds scope discipline (no unrelated changes, no opportunistic refactoring) and a security pass (no sensitive data logged, no internal errors leaked, server-side validation, auth on every endpoint, tenant isolation preserved).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Full Guardrail Set
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guardrail&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test gate&lt;/td&gt;
&lt;td&gt;No implementation begins until a second model approves the tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code gate&lt;/td&gt;
&lt;td&gt;No MR opens until a second model approves the implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approval gate&lt;/td&gt;
&lt;td&gt;The agent never touches a ticket a human hasn't moved to approved status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-failure stop&lt;/td&gt;
&lt;td&gt;Five consecutive failing test runs halts the ticket and posts a blocker comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30-minute re-read&lt;/td&gt;
&lt;td&gt;Long-running tickets trigger a re-read of the original requirement before continuing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope discipline&lt;/td&gt;
&lt;td&gt;Review rejects changes outside the ticket's stated scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local-first artifacts&lt;/td&gt;
&lt;td&gt;Plans and tickets are markdown files before they're board state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A model that writes both the tests and the code will happily write tests its code passes.&lt;/strong&gt; Independent review is the only thing that breaks that loop — and it's why the test numbers below mean something instead of being coverage theater.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring the Impact
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;From production use on a real microservices platform (~15 active repositories, Java/Quarkus backends, React frontends):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Feature stories planned end-to-end&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;35+&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Child tickets generated &amp;amp; developed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;500+&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manual development hours avoided&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1,650 hrs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual estimate vs. agent time&lt;/td&gt;
&lt;td&gt;~2,480 hrs → ~835 hrs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share of manual effort removed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~90%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests written&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~11,400&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Line coverage across services (sampled)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~86%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REST endpoints shipped&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;156&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB tables shipped&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E2E user journeys&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longest unattended run&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~12 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Test breakdown, counted directly from the test suites: &lt;strong&gt;~5,325 backend unit tests&lt;/strong&gt; (405 test classes) and &lt;strong&gt;~6,052 frontend unit/component tests&lt;/strong&gt; (591 test files), plus 19 E2E journey suites running against the real stack — real database, real backend, real UI, no mocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading These Numbers Honestly
&lt;/h3&gt;

&lt;p&gt;Benchmark sections are usually where rigor goes to die. Four caveats worth stating plainly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test count and coverage are counted, the hours are extrapolated.&lt;/strong&gt; Test cases (~11,400) and file/line counts came directly from the test suites and source trees. Line coverage (~86%) is a weighted average from JaCoCo reports that exist for a &lt;em&gt;sample&lt;/em&gt; of backend modules, not a full-suite run across all 19 repositories — real data, partial coverage of the estate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 1,650 hours is a scaled comparison against estimates, not a controlled trial.&lt;/strong&gt; The underlying ratio — roughly 90% of estimated manual effort removed, i.e. manual time running about 3x agent time — was established on an earlier, smaller slice of delivered work and held steady as ticket volume grew past 500. Applying that same ratio to the current scale gives ~2,480 estimated manual hours against ~835 hours of actual agent time. The manual side is an engineering estimate, defensible because these are ticket types the team has sized for years, but nobody built the same 500+ tickets twice to confirm it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ~90% figure measures effort removed, not effort eliminated.&lt;/strong&gt; Those ~835 agent hours aren't free — they include human review at both gates, MR review, and intervention when a run stalls. What shrank is the &lt;em&gt;manual writing&lt;/em&gt; of migrations, endpoints, components, and tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~86% line coverage is a floor claim, not a quality claim.&lt;/strong&gt; Coverage measures execution, not assertion quality. What makes it meaningful here is the review gate: every test set was reviewed for tautologies and weak assertions &lt;em&gt;before&lt;/em&gt; implementation existed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned: Mistakes That Cost Us Time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Each of these looked reasonable and quietly made the system worse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting the agent create the parent story.&lt;/strong&gt; It now receives a &lt;em&gt;blank story key&lt;/em&gt; to populate. Humans decide what gets built; the agent decides how it decomposes. That boundary turned out to be load-bearing — for trust as much as correctness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallelizing development.&lt;/strong&gt; Our first version modeled dependencies as a DAG and ran ready tickets concurrently. Merge conflicts and duplicated shared components ate the entire speedup and then some. The linear chain is slower on paper and faster in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing tickets straight into the tracker.&lt;/strong&gt; Wrong decomposition meant deleting tickets, unlinking dependencies, and cleaning up a polluted board. Now the plan and every proposed ticket are written as &lt;strong&gt;local markdown first&lt;/strong&gt;, iterated with the developer, and pushed only after approval. Iterating on a file is free; iterating on a ticket board is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bundling tickets to reduce count.&lt;/strong&gt; "One ticket for the CRUD API" seems efficient. It produces an enormous diff, an unreviewable MR, and a ticket that's half-done for three days. The rule is now aggressively granular: one ticket per schema, per endpoint, per page, per integration, per journey.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming the model would infer repository layout.&lt;/strong&gt; It doesn't, reliably. The repo map, module conventions, and shared-component rules are written down explicitly — and the agent re-fetches the latest code from every affected repository before planning, so it plans against what's actually there rather than what it remembers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Walkthrough: One Story, End to End
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Here's what a typical run looks like from the outside.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt; — a blank story key plus context: an existing repo, a PRD, some design notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt; — the agent fetches latest code across affected submodules, asks clarifying questions, and drafts a plan plus one markdown file per proposed ticket. Each records its target repo, target module, dependency, acceptance criteria, and verification scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish&lt;/strong&gt; — the plan goes to the wiki. Cheap and reversible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate 1&lt;/strong&gt; — you read the markdown locally and iterate. Nothing has touched the ticket board yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create&lt;/strong&gt; — on approval, ~12 child tickets are created in Triage and linked as one linear chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate 2&lt;/strong&gt; — you validate each ticket and approve it. The agent will not touch an unapproved ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Develop&lt;/strong&gt; — the loop polls for the one ready ticket, runs the matching workflow (DB / Backend / UI / E2E), and repeats. Each ticket: tests → review → implement → tests pass → review → stacked MR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate&lt;/strong&gt; — once all tickets are in review, one integration MR per repository lands the stack.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The half-day unattended stretch happens between steps 7 and 8 — polling, developing, and opening MRs with nobody watching.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pattern, Generalized
&lt;/h2&gt;

&lt;p&gt;Strip out the domain specifics and this applies to any team doing multi-repo development against a ticketing system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build on an agentic runtime, not from scratch.&lt;/strong&gt; Filesystem, shell, git, permissions, model routing — already solved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put external integrations behind MCP.&lt;/strong&gt; Typed, auditable, least-privilege, independently fixable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the contract in prose; read it fresh every run.&lt;/strong&gt; Behavior changes become pull requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer rules so projects override without forking.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load stack conventions on demand.&lt;/strong&gt; Context relevance beats context volume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route models by cognitive load.&lt;/strong&gt; Reasoning for planning and review; fast models for constrained implementation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose determinism over parallelism.&lt;/strong&gt; A linear chain makes an autonomous loop trivially correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack your branches.&lt;/strong&gt; Reviewability is the real bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make review gates hard blocks — reviewed by a different model than the one implementing.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep humans at the decomposition gate and the approval gate.&lt;/strong&gt; Autonomy between, judgment at the boundaries.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is vibe coding, and when does it stop working?&lt;/strong&gt;&lt;br&gt;
Vibe coding is prompting an AI for code and accepting it on plausibility rather than verification. It works well for one repository, one developer, and tasks you could have scoped yourself. It stops working when a change spans repositories — because the hard part is no longer writing the code, it's deciding where the code belongs and in what order pieces must land.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is agentic SDLC automation?&lt;/strong&gt;&lt;br&gt;
A delivery workflow where an AI system executes planning, decomposition, implementation, testing, and review under deterministic orchestration, with explicit human approval gates and automated verification. The defining trait is accountability for a story, not a diff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is an AI development agent different from an AI coding assistant?&lt;/strong&gt;&lt;br&gt;
An assistant makes you faster at a task you've already scoped. An agent does the scoping: it decides which repository and module each change belongs in, generates the ticket breakdown, orders the work by dependency, and produces reviewable merge requests. See the comparison table above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is an agentic SDLC just vibe coding with more steps?&lt;/strong&gt;&lt;br&gt;
No — the difference is what happens to unverified output. Vibe coding accepts code on plausibility. An agentic system cannot: tests are written and reviewed by an independent model before implementation exists, code is reviewed before an MR opens, and no ticket is touched until a human approves it. Remove those gates and you do get vibe coding at higher volume, which is worse than doing it by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this replace developers?&lt;/strong&gt;&lt;br&gt;
No. It removes the manual writing of highly-patterned code and stops decomposition from being the thing that gets cut when a sprint is tight. Humans still decide what gets built, review every plan, and approve every ticket and merge request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why a linear dependency chain instead of parallel execution?&lt;/strong&gt;&lt;br&gt;
Because the invariant "exactly one ticket is ready at a time" eliminates merge conflicts, duplicated shared components, and concurrent edits to the same file. We tried parallel first. The conflicts cost more than the parallelism saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you stop an AI agent from writing tests that pass trivially?&lt;/strong&gt;&lt;br&gt;
A different model reviews the tests &lt;em&gt;before&lt;/em&gt; any implementation exists, against a checklist targeting tautological assertions, vague "no exception thrown" checks, &lt;code&gt;sleep()&lt;/code&gt; calls, and unrealistic test data. The implementing model never reviews its own tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when the agent gets stuck?&lt;/strong&gt;&lt;br&gt;
It stops. Five consecutive test failures halts the ticket and posts a blocker comment rather than thrashing. Tickets running long trigger a re-read of the original requirement before continuing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isn't 7,800 lines of prose harder to maintain than code?&lt;/strong&gt;&lt;br&gt;
It's easier. It diffs, it reviews in a pull request, and any engineer can read a proposed workflow change without knowing the implementation. There's no retraining step and no gap between what the team agreed and what the agent does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much context does an agent like this need?&lt;/strong&gt;&lt;br&gt;
Less than you'd expect, if you're deliberate. Conventions load only when relevant — the DB ticket never loads frontend rules. Curated context beats large context: the repo map and freshly-fetched code prevent far more errors than a bigger window would.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can this work outside a monorepo?&lt;/strong&gt;&lt;br&gt;
The decomposition, review-gate, and model-routing patterns are repo-agnostic. The stacked-branch and single-chain mechanics assume multiple coordinated repositories — in a single-repo project they simplify considerably but still help reviewability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the minimum viable version of this?&lt;/strong&gt;&lt;br&gt;
A written workflow contract, one typed tool boundary to your ticket system, and an independent review gate before code is accepted. Dependency chaining and stacked branches matter once a story spans more than a couple of repositories.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways (Recap)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vibe coding optimizes typing speed — never the actual bottleneck past one repository&lt;/li&gt;
&lt;li&gt;Decomposition is the bottleneck in multi-repo development, not code generation&lt;/li&gt;
&lt;li&gt;An AI development agent is LLM + orchestration + context + skills + rules + tools + verification — not a model alone&lt;/li&gt;
&lt;li&gt;Deterministic orchestration decides &lt;em&gt;what runs next&lt;/em&gt;; the LLM decides &lt;em&gt;how&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Determinism beats parallelism when you want to leave a system running unattended&lt;/li&gt;
&lt;li&gt;Stacked branches keep AI output reviewable; reviewability is the real constraint&lt;/li&gt;
&lt;li&gt;A different model must review the tests, or the tests are worthless&lt;/li&gt;
&lt;li&gt;~1,650 hours avoided across 500+ tickets, ~11,400 tests at ~86% coverage, ~12 hours unattended&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The most surprising thing about building this was how little of it is code.&lt;/strong&gt; The agent is ~7,800 lines, and the meaningful part is a workflow contract written in English — precise enough to execute, readable enough that any engineer can review a change to it in a pull request.&lt;/p&gt;

&lt;p&gt;We didn't build a system that writes code. We built a system that &lt;strong&gt;holds a specification precisely enough that writing the code becomes the easy part&lt;/strong&gt; — then put a human at each end of it.&lt;/p&gt;

&lt;p&gt;The twelve-hour unattended run is the part people react to. But that isn't a claim about model capability. It's a claim about specification quality: the agent ran that long without help because the contract was tight enough that there was nothing left to ask.&lt;/p&gt;

&lt;p&gt;The path from vibe coding to an agentic SDLC isn't a better model or a longer context window. It's the unglamorous work of writing down what your team already knows — which service owns what, what a good test looks like, what order things merge in — precisely enough that a machine can execute it and a human can review the execution.&lt;/p&gt;

&lt;p&gt;Autonomous development isn't an AI capability problem. It's an &lt;strong&gt;information architecture problem&lt;/strong&gt; — and that one you can actually solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — the open standard behind the typed tool boundary&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.claude.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code documentation&lt;/a&gt; — plugins, skills, and slash commands&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.claude.com/en/api/agent-sdk/overview" rel="noopener noreferrer"&gt;Claude Agent SDK&lt;/a&gt; — for building agents outside a CLI plugin&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.gitlab.com/ee/user/project/merge_requests/" rel="noopener noreferrer"&gt;GitLab merge request docs&lt;/a&gt; — the branch and MR model referenced above&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Questions about the architecture are welcome in the comments — happy to go deeper on the dependency chain or the review-gate checklists.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentskills</category>
      <category>sdlc</category>
      <category>automation</category>
    </item>
    <item>
      <title>Why Large Codebases Drain Your AI Token Budget and How to Fit It</title>
      <dc:creator>Harshit Rathod</dc:creator>
      <pubDate>Sun, 05 Jul 2026 13:33:09 +0000</pubDate>
      <link>https://dev.to/harshit_rathod/why-large-codebases-drain-your-ai-token-budget-and-how-to-fit-it-5a2m</link>
      <guid>https://dev.to/harshit_rathod/why-large-codebases-drain-your-ai-token-budget-and-how-to-fit-it-5a2m</guid>
      <description>&lt;p&gt;&lt;em&gt;A technical breakdown of where context budget goes in large codebases, and the structural patterns that reduce consumption while increasing accuracy.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hook:&lt;/strong&gt; You add a new feature to a large NestJS monorepo with Claude's help. By the end of the conversation, the model has read 40 files, re-derived your module conventions twice, and still got the service placement wrong. You spent thousands of tokens. Claude spent most of them guessing. This guide explains why — and what to do about it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context windows don't scale with codebases.&lt;/strong&gt; A large repo has more decisions, more conventions, and more ambiguity — all of which drive up token consumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The largest token sinks are re-derivation and uncertainty.&lt;/strong&gt; Claude reads more files when it doesn't know the architecture; it generates more text when it doesn't know the conventions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the highest-leverage token saver.&lt;/strong&gt; A 300-line briefing file can replace hundreds of lines of exploratory file reads per session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules save tokens by preventing regeneration.&lt;/strong&gt; A wrong pattern that gets corrected costs twice — once to generate, once to fix. A prohibition rule prevents it entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills load context on demand.&lt;/strong&gt; Templates and references in skills only enter the context window when invoked, not on every session.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;Large repositories burn through AI tokens because Claude must read files to infer what any experienced team member already knows: which service owns what, what the error-handling pattern is, how responses are shaped, what's been deliberately removed. Every exploratory read, every wrong-service guess, every pattern that violates a convention and gets corrected is wasted context. The fix is to externalize that tribal knowledge into &lt;code&gt;CLAUDE.md&lt;/code&gt; (a briefing loaded every session), rules (prohibitions that prevent wrong output), and skills (on-demand workflows with templates). One-time setup; recurs on every feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Context Budget Is a Finite Resource
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every token Claude reads or writes is budget spent.&lt;/strong&gt; Context windows are measured in tokens — not files, not lines — and every session starts with the same ceiling. In a small project, that budget is generous. In a large monorepo with several services, dozens of modules, hundreds of DTOs, and cross-cutting conventions, the budget evaporates fast.&lt;/p&gt;

&lt;p&gt;There are two places budget goes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input tokens&lt;/strong&gt; — everything Claude reads: your prompt, files it opens, rules and docs it loads, the running conversation history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output tokens&lt;/strong&gt; — everything Claude generates: code, explanations, corrections, re-generations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both are expensive. But in a large repo, the &lt;strong&gt;input side is where the hemorrhage starts&lt;/strong&gt; — because Claude reads aggressively to compensate for what it doesn't know.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the Tokens Actually Go
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Exploratory File Reads
&lt;/h3&gt;

&lt;p&gt;When Claude doesn't know where something lives, it goes looking. In a well-indexed codebase, that might mean reading 3–4 files to locate the right module. In a large monorepo with ambiguous service boundaries, it can mean reading 15–20 before it's confident enough to act.&lt;/p&gt;

&lt;p&gt;A typical unconfigured session flow for "add a consumer-visible status to a booking package":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads &lt;code&gt;services/&lt;/code&gt; directory listing — &lt;em&gt;orientation&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;operator-service/src/modules/order/&lt;/code&gt; — &lt;em&gt;wrong service, discovers mismatch&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;api-service/src/modules/order/&lt;/code&gt; — &lt;em&gt;corrects course&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;order.entity.ts&lt;/code&gt; — &lt;em&gt;finds the shape&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;order.service.ts&lt;/code&gt; — &lt;em&gt;finds the pattern&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;order.repository.ts&lt;/code&gt; — &lt;em&gt;finds the query layer&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;order.controller.ts&lt;/code&gt; — &lt;em&gt;finds the response shape&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;order.dto.ts&lt;/code&gt; — &lt;em&gt;finds the DTO convention&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads &lt;code&gt;transform-response.helper.ts&lt;/code&gt; — &lt;em&gt;finds the adapter pattern&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Reads 3–4 more files to verify cross-service adapter usage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's ~10 file reads &lt;strong&gt;before writing a single line of output&lt;/strong&gt;. Each read costs input tokens. In a large NestJS monorepo, files are not small — services, repositories, and controllers routinely exceed 200–400 lines each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same task in a configured session:&lt;/strong&gt; Claude reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, knows the service boundary immediately, reads the 3 directly relevant files, and starts generating. The exploratory reads never happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Convention Re-derivation
&lt;/h3&gt;

&lt;p&gt;Even when Claude lands in the right file, it still needs to infer your team's conventions. Without explicit documentation, it reads examples to reconstruct patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It reads existing controllers to learn how responses are shaped&lt;/li&gt;
&lt;li&gt;It reads existing services to find which logger you use&lt;/li&gt;
&lt;li&gt;It reads existing guards to understand your permission system&lt;/li&gt;
&lt;li&gt;It reads multiple DTOs to infer your validation patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is pattern-matching by example. It works — but it costs tokens for every session, for every engineer, indefinitely. The conventions don't change; the re-derivation does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Convention re-derivation cost per session (rough estimate for a large NestJS monorepo):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Convention Claude Must Re-Derive&lt;/th&gt;
&lt;th&gt;Files Typically Read&lt;/th&gt;
&lt;th&gt;Estimated Token Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service ownership (which belongs where)&lt;/td&gt;
&lt;td&gt;3–5 entry points&lt;/td&gt;
&lt;td&gt;~2,000–4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response shape and interceptor pattern&lt;/td&gt;
&lt;td&gt;2–3 controllers&lt;/td&gt;
&lt;td&gt;~1,500–3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission system (guards + decorators)&lt;/td&gt;
&lt;td&gt;2–3 guards/controllers&lt;/td&gt;
&lt;td&gt;~1,000–2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error handling (constants + exceptions)&lt;/td&gt;
&lt;td&gt;2–3 services&lt;/td&gt;
&lt;td&gt;~1,000–2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logger usage&lt;/td&gt;
&lt;td&gt;1–2 services&lt;/td&gt;
&lt;td&gt;~500–1,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import style (alias vs relative)&lt;/td&gt;
&lt;td&gt;Several files&lt;/td&gt;
&lt;td&gt;~300–600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total per session (estimation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~13–18 files&lt;/td&gt;
&lt;td&gt;~6,300–12,600 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's before the prompt, before the output, and before any task-specific reads. It's overhead — pure re-derivation of knowledge your team already has.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Wrong-Service Placement and Corrections
&lt;/h3&gt;

&lt;p&gt;When a codebase has multiple services with overlapping domains — say, a &lt;code&gt;package&lt;/code&gt; entity that exists in both the operator service (definition) and the consumer API (consumer-visible metadata) — Claude will guess. Sometimes it guesses right. Often it doesn't.&lt;/p&gt;

&lt;p&gt;A wrong-service guess costs tokens in three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exploration in the wrong service&lt;/strong&gt; — reading module structure, entities, and services in a service that won't receive the change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generating the wrong output&lt;/strong&gt; — producing code in the wrong service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correction&lt;/strong&gt; — you point out the error; Claude re-reads, re-reasons, and regenerates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In practice, one wrong-service guess can cost more tokens than the correct implementation from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pattern Violations and Regeneration
&lt;/h3&gt;

&lt;p&gt;Without explicit prohibitions, Claude uses its training defaults. Those defaults are reasonable — but they won't match your team's conventions. Common mismatches in NestJS projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;console.log&lt;/code&gt; instead of a structured logger&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if (user.roles.includes('admin'))&lt;/code&gt; instead of &lt;code&gt;PermissionCodes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Relative &lt;code&gt;../../&lt;/code&gt; imports instead of the &lt;code&gt;src/&lt;/code&gt; alias&lt;/li&gt;
&lt;li&gt;Hardcoded error strings instead of &lt;code&gt;ErrorMessages&lt;/code&gt; constants&lt;/li&gt;
&lt;li&gt;Raw DB queries in a service instead of going through a repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each violation costs tokens twice: once to generate the wrong pattern, once to correct it. A single code generation that needs three corrections has effectively quadrupled the output token cost for that block.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Conversation History Accumulation
&lt;/h3&gt;

&lt;p&gt;As a session grows, the running conversation history is included in every subsequent call. In a long debugging or implementation session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early file reads stay in context even when they're no longer relevant&lt;/li&gt;
&lt;li&gt;Corrections and re-generations add to history&lt;/li&gt;
&lt;li&gt;Claude's reasoning about dead ends accumulates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A session that spans 20 exchanges in a large codebase can have a conversation history that alone approaches the token limit of shorter sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Externalize Knowledge, Don't Re-Derive It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The underlying cause of excess token consumption is the same in every case: Claude is spending tokens to learn things your team already knows.&lt;/strong&gt; The fix is to make that knowledge available upfront, in a form Claude can read efficiently, rather than re-deriving it from source files every session.&lt;/p&gt;

&lt;p&gt;Three mechanisms do this, each suited to a different type of knowledge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;What it encodes&lt;/th&gt;
&lt;th&gt;When Claude reads it&lt;/th&gt;
&lt;th&gt;Token impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Architecture, ownership, conventions&lt;/td&gt;
&lt;td&gt;Loaded automatically, every session&lt;/td&gt;
&lt;td&gt;Replaces exploratory reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard prohibitions and invariants&lt;/td&gt;
&lt;td&gt;Always active; prevents wrong output&lt;/td&gt;
&lt;td&gt;Eliminates regeneration cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-step workflows + templates&lt;/td&gt;
&lt;td&gt;On demand, when invoked&lt;/td&gt;
&lt;td&gt;Keeps templates out of always-on context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  CLAUDE.md: Replace Exploration With Declaration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the single highest-leverage tool for reducing token consumption.&lt;/strong&gt; It's loaded at the start of every session, before any file reads, and it replaces the exploratory phase entirely for the information it contains.&lt;/p&gt;

&lt;p&gt;The key insight is &lt;strong&gt;specificity over completeness.&lt;/strong&gt; A bloated &lt;code&gt;CLAUDE.md&lt;/code&gt; that describes everything about NestJS is worse than a lean one that answers the exact questions Claude would otherwise need to read files to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which service owns what kind of data?&lt;/li&gt;
&lt;li&gt;What does the standard response shape look like?&lt;/li&gt;
&lt;li&gt;How does cross-service communication work?&lt;/li&gt;
&lt;li&gt;What are the project-specific patterns (logger, errors, permissions)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 300-line &lt;code&gt;CLAUDE.md&lt;/code&gt; that precisely answers those questions saves more tokens than a 1,000-line one that also explains what a repository pattern is.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Include
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Service Routing Decision Table
&lt;/h4&gt;

&lt;p&gt;This is the single most effective thing you can document. Instead of Claude reading 10 files to figure out which service owns a new feature, it reads one table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Which Service Owns What
&lt;span class="p"&gt;
-&lt;/span&gt; operator-service — if the data describes _what an experience is_:
  package definitions, pricing, taxes, availability, operator accounts.
&lt;span class="p"&gt;-&lt;/span&gt; api-service — if the data describes _a consumer's interaction_:
  bookings, users, auth, trip planning, payments, notifications.

| Task involves...                   | Service                            |
| ---------------------------------- | ---------------------------------- |
| Package categories, pricing, slots | operator-service                   |
| Booking status, user profiles      | api-service                        |
| Stripe payments (consumer side)    | api-service                        |
| Cross-service booking flow         | Start in api-service → IMS adapter |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This table costs ~150 tokens to include in the context. It replaces 2,000–4,000 tokens of exploratory service reads. &lt;strong&gt;That's a 10–25× return on every session.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Conventions That Would Otherwise Be Re-Derived
&lt;/h4&gt;

&lt;p&gt;Document only what isn't already obvious from reading a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What Claude Must Not Do&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`console.log`&lt;/span&gt; → use &lt;span class="sb"&gt;`logMessage()`&lt;/span&gt; from &lt;span class="sb"&gt;`src/common/utils/logger`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Check &lt;span class="sb"&gt;`user.role`&lt;/span&gt; → use &lt;span class="sb"&gt;`PermissionCodes`&lt;/span&gt; + &lt;span class="sb"&gt;`@Permissions()`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; DB call inside a loop → use bulk queries (&lt;span class="sb"&gt;`IN`&lt;/span&gt;, &lt;span class="sb"&gt;`JOIN`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Relative &lt;span class="sb"&gt;`../../`&lt;/span&gt; imports → use the &lt;span class="sb"&gt;`src/`&lt;/span&gt; alias
&lt;span class="p"&gt;-&lt;/span&gt; TypeORM &lt;span class="sb"&gt;`synchronize: true`&lt;/span&gt; → Flyway manages all schema changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five prohibitions. ~100 tokens. Replaces all the file reads Claude would do to infer these patterns — and eliminates the regeneration cost when it gets them wrong.&lt;/p&gt;

&lt;h4&gt;
  
  
  Response Shape
&lt;/h4&gt;

&lt;p&gt;Include the exact shape, not a prose description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Standard Response Shape&lt;/span&gt;

Controllers return &lt;span class="sb"&gt;`{ data, meta }`&lt;/span&gt;. TransformInterceptor wraps automatically:

return { data: { items }, meta: { path: request.path } };
// → { success: true, data: { items }, meta: { path }, error: null }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, Claude reads 2–3 controllers to infer the pattern. With it, Claude knows immediately — and gets it right the first time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Exclude
&lt;/h3&gt;

&lt;p&gt;The token efficiency of &lt;code&gt;CLAUDE.md&lt;/code&gt; depends as much on what you leave out as what you put in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework documentation&lt;/strong&gt; — Claude already knows what a NestJS guard is. Document only your project-specific guard setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code that's derivable from files&lt;/strong&gt; — if Claude can read one file and learn a pattern, don't repeat it in &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable, obvious conventions&lt;/strong&gt; — use TypeScript, use async/await, follow the module pattern. These are defaults; they cost tokens but add no information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task-specific templates&lt;/strong&gt; — templates belong in skills, not &lt;code&gt;CLAUDE.md&lt;/code&gt;. They should enter the context on demand, not every session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Target: under 300 lines.&lt;/strong&gt; If your &lt;code&gt;CLAUDE.md&lt;/code&gt; is longer, audit it for derivable content. Every line that doesn't prevent a file read or a wrong pattern is a line that should be cut.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rules: Prevent Regeneration at the Source
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A wrong pattern that's regenerated after correction costs twice the tokens of getting it right the first time.&lt;/strong&gt; Rules cut this cost to zero by making the wrong pattern impossible to generate.&lt;/p&gt;

&lt;p&gt;Rules live in &lt;code&gt;.claude/rules/&lt;/code&gt; as Markdown files. They're phrased as prohibitions — not preferences — because "NEVER call the DB in a loop" is enforced, while "prefer bulk queries" is suggestion that gets ignored under pressure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token Cost of a Rule vs. a Correction
&lt;/h3&gt;

&lt;p&gt;Consider N+1 queries. Without a rule, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude generates code with a DB call inside a loop &lt;em&gt;(output tokens)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You read the generated code and identify the N+1 &lt;em&gt;(your time)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You explain the issue to Claude &lt;em&gt;(input tokens)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Claude re-reads the context to understand the correction &lt;em&gt;(context re-processing)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Claude regenerates the correct bulk-query version &lt;em&gt;(output tokens)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total: roughly 2× the output tokens for that block, plus the input tokens for the correction exchange.&lt;/p&gt;

&lt;p&gt;With a rule:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rule is in context; Claude generates the correct bulk-query pattern &lt;em&gt;(output tokens)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total: 1× output tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule itself costs ~200 tokens to include in the session. It saves 1× output tokens plus correction overhead on every N+1 occurrence.&lt;/strong&gt; In a session with 3–4 such violations, it pays for itself many times over.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-Value Rules for NestJS Projects
&lt;/h3&gt;

&lt;p&gt;Rules that deliver the highest token savings are those that prevent patterns Claude's training considers reasonable but that violate your project's conventions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N+1 Query Prevention&lt;/strong&gt; — Claude defaults to per-entity lookups in loops; this stops it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Database Query Rules — No N+1&lt;/span&gt;

NEVER make a database call inside a loop. Use a single bulk query, then
group results in memory. If unsure whether a pattern causes N+1, ask first.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Permission System&lt;/strong&gt; — Claude defaults to role-name checks; this redirects it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Authorization — Never Check Role Names&lt;/span&gt;

NEVER check &lt;span class="sb"&gt;`user.role`&lt;/span&gt;, &lt;span class="sb"&gt;`user.roles`&lt;/span&gt;, or any role-name string.
Use &lt;span class="sb"&gt;`@Permissions([PermissionCodes.X])`&lt;/span&gt; with &lt;span class="sb"&gt;`PermissionsGuard`&lt;/span&gt; instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Import Style&lt;/strong&gt; — Claude defaults to relative imports; this enforces the alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Imports — Always Use the Path Alias&lt;/span&gt;

NEVER use relative imports that traverse more than one directory level.
Use &lt;span class="sb"&gt;`src/modules/...`&lt;/span&gt; alias imports everywhere.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these rules costs ~100–200 tokens per session to include. Each saves the token cost of generating a violation plus the correction exchange — conservatively 500–2,000 tokens per occurrence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Skills: Keep Templates Out of Always-On Context
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Skills are on-demand context.&lt;/strong&gt; Templates, references, and workflow guides for repeated tasks are exactly the kind of high-token content that should not be in &lt;code&gt;CLAUDE.md&lt;/code&gt; — because they're only relevant when you're doing that specific task.&lt;/p&gt;

&lt;p&gt;A controller template for your NestJS service is ~50–100 lines. In a CLAUDE.md, that's 50–100 lines of context included in every session, even when you're debugging, writing tests, or reading logs. In a skill, those same lines only enter the context when you run &lt;code&gt;/api-development&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token Budget Impact of Skills
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;In CLAUDE.md (always loaded)&lt;/th&gt;
&lt;th&gt;In a skill (on demand)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Controller template (80 lines)&lt;/td&gt;
&lt;td&gt;~400 tokens × every session&lt;/td&gt;
&lt;td&gt;~400 tokens × only when invoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service template (80 lines)&lt;/td&gt;
&lt;td&gt;~400 tokens × every session&lt;/td&gt;
&lt;td&gt;~400 tokens × only when invoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository template (60 lines)&lt;/td&gt;
&lt;td&gt;~300 tokens × every session&lt;/td&gt;
&lt;td&gt;~300 tokens × only when invoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DTO examples (40 lines)&lt;/td&gt;
&lt;td&gt;~200 tokens × every session&lt;/td&gt;
&lt;td&gt;~200 tokens × only when invoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1,300 tokens per session&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1,300 tokens when you need it&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you scaffold endpoints 3 times a week but run 20 sessions per week, templates in &lt;code&gt;CLAUDE.md&lt;/code&gt; cost 20 × 1,300 = 26,000 tokens. In a skill, they cost 3 × 1,300 = 3,900 tokens. &lt;strong&gt;Same result, 85% fewer tokens for the templates alone.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/api-development/
├── SKILL.md                    # Workflow: trigger, steps, decision points
├── assets/
│   ├── controller-template.md  # Real, compilable code templates
│   ├── service-template.md
│   ├── repository-template.md
│   └── dto-template.md
└── references/
    ├── nestjs-conventions.md
    └── swagger-documentation.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SKILL.md&lt;/code&gt; workflow guides Claude through the task deterministically — DTO first (it defines the contract), then repository (queries only), then service (business logic), then controller (HTTP routing). Without the workflow, Claude invents its own order and sometimes generates the controller before the DTO exists, requiring re-generation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: Per-Service CLAUDE.md Files
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A monorepo has a root context problem.&lt;/strong&gt; Everything in the root &lt;code&gt;CLAUDE.md&lt;/code&gt; loads every session, regardless of which service you're working in. Details specific to &lt;code&gt;operator-service&lt;/code&gt;'s DI patterns are wasted tokens when you're debugging auth in &lt;code&gt;api-service&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix is a two-level structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/CLAUDE.md                   # Cross-cutting: routing, shared conventions
services/
  api-service/CLAUDE.md             # api-service specific: module patterns, auth, DI
  operator-service/CLAUDE.md        # operator-service specific: IMS adapter, pricing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude reads the root &lt;code&gt;CLAUDE.md&lt;/code&gt; always, and the service-level file when you work in that directory. Cross-cutting concerns (service ownership, response shape, shared error constants) live at root. Service-specific depth (auth flows, module patterns, DI tokens) lives in the service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token impact:&lt;/strong&gt; If each service file is 150 lines (~750 tokens), keeping them separate means you load at most 750 extra tokens per session instead of both services' details at all times. For a multi-service monorepo, that's up to 2,250 tokens of context avoided per session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring the Impact
&lt;/h2&gt;

&lt;p&gt;Here's the before/after for a realistic "add a consumer-visible booking status" task in a large NestJS monorepo:&lt;/p&gt;

&lt;h3&gt;
  
  
  Without Configuration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Token Estimate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exploratory directory reads (service placement)&lt;/td&gt;
&lt;td&gt;~3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convention re-derivation (logger, perms, errors)&lt;/td&gt;
&lt;td&gt;~6,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong-service generation and correction&lt;/td&gt;
&lt;td&gt;~4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pattern violations and regeneration (3×)&lt;/td&gt;
&lt;td&gt;~3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual task output (correct code, 4 files)&lt;/td&gt;
&lt;td&gt;~4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~20,000&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  With CLAUDE.md + Rules + Skills
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Token Estimate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLAUDE.md load (routing, conventions, shape)&lt;/td&gt;
&lt;td&gt;~1,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules load (3 rules)&lt;/td&gt;
&lt;td&gt;~600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Targeted file reads (3 directly relevant files)&lt;/td&gt;
&lt;td&gt;~2,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skill invocation (templates, workflow)&lt;/td&gt;
&lt;td&gt;~1,300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual task output (correct code, 4 files)&lt;/td&gt;
&lt;td&gt;~4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~9,900&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;~50% reduction in total token consumption for a single task.&lt;/strong&gt; For a team running dozens of sessions per week, the savings are substantial — and they compound because the ratio stays roughly constant across tasks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes That Maximize Token Waste
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Each of these looks harmless and silently burns budget across every session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Putting templates in CLAUDE.md.&lt;/strong&gt; Templates are task-specific. Loading them every session is waste. Move them to skills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing CLAUDE.md as a tutorial.&lt;/strong&gt; Explaining NestJS fundamentals Claude already knows inflates context without replacing any file reads. Document only what's unique to your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Omitting the routing decision table.&lt;/strong&gt; Service placement ambiguity is the single largest driver of exploratory reads. A table makes the decision free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing rules as preferences.&lt;/strong&gt; "Prefer bulk queries" leaves Claude room to choose. "NEVER call the DB in a loop" doesn't. The second form prevents regeneration; the first doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One flat CLAUDE.md for a monorepo.&lt;/strong&gt; All service-specific content loads every session. Split into root + per-service files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cross-service communication rules.&lt;/strong&gt; Without explicit guidance, Claude reaches across service boundaries — reads the wrong DB, imports across services. The correction exchange is expensive. One sentence in CLAUDE.md prevents it.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Real-World Example: Token Budget on a Feature Request
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The request:&lt;/strong&gt; &lt;em&gt;"Add a consumer-visible status to a booking package."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unconfigured session token trace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[read] services/                              ~200 tokens
[read] operator-service/src/modules/order/ ~150 tokens
[read] operator-service order.entity.ts    ~600 tokens  ← wrong service
[read] api-service/src/modules/order/      ~150 tokens  ← course correction
[read] api-service order.entity.ts         ~500 tokens
[read] api-service order.service.ts        ~800 tokens
[read] api-service order.repository.ts     ~600 tokens
[read] api-service order.controller.ts     ~700 tokens
[read] api-service order.dto.ts            ~400 tokens
[read] transform-response.helper.ts          ~300 tokens
[read] ims-adapter.ts                        ~700 tokens
...                                          ~3,000 more (permissions, logger, errors)
[generate] wrong permission check            ~400 tokens
[correction] permission explanation          ~200 tokens
[generate] correct permission                ~200 tokens
[generate] actual implementation (4 files)  ~4,000 tokens
Total: ~12,900 input + ~4,600 output ≈ 17,500 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configured session token trace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[load] CLAUDE.md (routing, conventions)      ~1,500 tokens
[load] rules (3 rules)                        ~600 tokens
[invoke] /api-development skill             ~1,300 tokens
[read] api-service order.service.ts        ~800 tokens  ← targeted read
[read] api-service order.repository.ts     ~600 tokens  ← targeted read
[read] ims-adapter.ts                        ~700 tokens  ← targeted read
[generate] implementation (4 files, correct) ~4,000 tokens
Total: ~5,500 input + ~4,000 output ≈ 9,500 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation output is nearly identical. The savings come entirely from eliminating exploratory reads, wrong-service generation, and pattern violations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary: The Token Budget Formula
&lt;/h2&gt;

&lt;p&gt;Token consumption in a large repo follows a predictable formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total tokens = (exploration overhead) + (re-derivation overhead) + (correction overhead) + (actual task output)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration attacks the first three terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLAUDE.md&lt;/strong&gt; eliminates exploration overhead and most re-derivation overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules&lt;/strong&gt; eliminate correction overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; shift task-specific re-derivation from every-session to on-demand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual task output — the code you asked for — is roughly constant. Everything else is overhead, and it's reducible.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why does Claude read so many files in a large repo?&lt;/strong&gt;&lt;br&gt;
Claude compensates for uncertainty with exploration. When it doesn't know which service owns a feature, it reads multiple services to decide. When it doesn't know your response shape, it reads multiple controllers. &lt;code&gt;CLAUDE.md&lt;/code&gt; replaces that uncertainty with declared knowledge, eliminating the reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does a larger CLAUDE.md always save more tokens?&lt;/strong&gt;&lt;br&gt;
No — the relationship inverts past a threshold. A 300-line &lt;code&gt;CLAUDE.md&lt;/code&gt; that answers the right questions saves more tokens than a 1,000-line one that also includes tutorials and templates. The goal is maximum information density per line, not maximum coverage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much do wrong patterns actually cost?&lt;/strong&gt;&lt;br&gt;
A single N+1 query correction in a longer session can cost 500–2,000 tokens: input for your correction message plus output for the re-generation. Across a week of development with multiple sessions, prevention rules pay for themselves quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I measure token usage per session in Claude Code?&lt;/strong&gt;&lt;br&gt;
Claude Code doesn't currently expose per-session token metrics directly. Proxy signals: session length before context compression kicks in, whether you see "context window approaching limit" warnings, and how many file reads appear in the tool call log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are skills just macros?&lt;/strong&gt;&lt;br&gt;
Skills are more than macros — they bundle a workflow (ordered steps, decision points), templates (compilable code), and references (conventions, Swagger patterns) into a single invocable command. The workflow is what prevents out-of-order generation and the correction overhead that comes with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should &lt;code&gt;CLAUDE.md&lt;/code&gt; be the same for every engineer on the team?&lt;/strong&gt;&lt;br&gt;
Yes — commit it to git so the configuration is shared and versioned. Personal overrides go in &lt;code&gt;.claude/settings.local.json&lt;/code&gt;, which is gitignored. The team-level conventions in &lt;code&gt;CLAUDE.md&lt;/code&gt; apply to everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this approach work for repos that aren't NestJS?&lt;/strong&gt;&lt;br&gt;
The token-saving logic applies to any large codebase with team-specific conventions. The specific content changes (replace NestJS patterns with your stack's patterns), but the structure — &lt;code&gt;CLAUDE.md&lt;/code&gt; for routing and conventions, rules for prohibitions, skills for repeated workflows — is framework-agnostic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways (Recap)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token waste in large repos has a root cause:&lt;/strong&gt; Claude reads files to learn what your team already knows. The fix is to tell it upfront.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; replaces exploration.&lt;/strong&gt; A service routing table costs ~150 tokens; it replaces 2,000–4,000 tokens of exploratory reads per session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules eliminate regeneration.&lt;/strong&gt; A prohibition costs ~200 tokens once; it saves 500–2,000 tokens every time the wrong pattern would have been generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills keep templates off the always-on context.&lt;/strong&gt; Load them only when you're scaffolding, not in every session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split by service.&lt;/strong&gt; Per-service &lt;code&gt;CLAUDE.md&lt;/code&gt; files prevent service-specific context from loading in unrelated sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The savings compound.&lt;/strong&gt; Every session, every engineer, every feature — the overhead reduction is consistent because the root cause is structural.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Token exhaustion in large repositories isn't a limitation of the AI — it's an information architecture problem. Claude reads aggressively because the knowledge it needs to act confidently isn't in any single file. It re-derives conventions that haven't changed. It generates wrong patterns that get corrected. It explores the wrong service before finding the right one.&lt;/p&gt;

&lt;p&gt;The fix isn't a larger context window. It's externalizing your team's tribal knowledge into structured, version-controlled form — a &lt;code&gt;CLAUDE.md&lt;/code&gt; that answers the questions Claude would otherwise spend tokens asking, rules that prevent the patterns it would otherwise spend tokens regenerating, and skills that load task-specific context only when you need it.&lt;/p&gt;

&lt;p&gt;The investment is a few hours. The savings recur on every session, for every engineer, indefinitely.&lt;/p&gt;




&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.claude.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code documentation&lt;/a&gt; — official setup, configuration, and best-practices reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="//./claude-code-nestjs-setup.md"&gt;How to Set Up Claude Code for a NestJS Monorepo&lt;/a&gt; — the companion guide to this article; step-by-step configuration walkthrough&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.nestjs.com" rel="noopener noreferrer"&gt;NestJS documentation&lt;/a&gt; — providers, guards, interceptors, and module architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Token estimates in this article are illustrative approximations based on observed session patterns in a production NestJS monorepo with several services. Actual consumption varies by model, file sizes, and task complexity.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>contextengineering</category>
      <category>tokenoptimizations</category>
      <category>claude</category>
    </item>
    <item>
      <title>Claude Code for NestJS Monorepos: A Practical Setup Guide</title>
      <dc:creator>Harshit Rathod</dc:creator>
      <pubDate>Sun, 07 Jun 2026 08:53:54 +0000</pubDate>
      <link>https://dev.to/harshit_rathod/claude-code-for-nestjs-monorepos-a-practical-setup-guide-46l8</link>
      <guid>https://dev.to/harshit_rathod/claude-code-for-nestjs-monorepos-a-practical-setup-guide-46l8</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical, step-by-step guide to configuring Claude Code with CLAUDE.md, rules, and skills so AI-generated code matches your team's conventions in a large NestJS monorepo.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hook:&lt;/strong&gt; Ask Claude to "add a consumer-visible status to a booking" in an unconfigured monorepo, and it will happily edit the &lt;em&gt;wrong microservice&lt;/em&gt;, hardcode an error string, and check &lt;code&gt;user.role === 'admin'&lt;/code&gt;. None of that is Claude being wrong — it's Claude guessing. This guide removes the guessing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code reads &lt;code&gt;.claude/&lt;/code&gt; from your git root&lt;/strong&gt;, so one configuration governs every service in the monorepo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three mechanisms do the heavy lifting:&lt;/strong&gt; &lt;code&gt;CLAUDE.md&lt;/code&gt; (always-loaded project briefing), &lt;strong&gt;rules&lt;/strong&gt; (hard prohibitions), and &lt;strong&gt;skills&lt;/strong&gt; (multi-step workflows with templates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The single biggest win is service routing&lt;/strong&gt; — a decision table in &lt;code&gt;CLAUDE.md&lt;/code&gt; stops Claude from putting consumer logic in the operator service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write rules as prohibitions, not preferences&lt;/strong&gt; — "NEVER query the DB in a loop" is enforced; "prefer bulk queries" is ignored under pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup is a few hours, one-time&lt;/strong&gt;, and pays back on every feature: faster scaffolding, fewer convention violations in review, and faster onboarding.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;To set up Claude Code for a NestJS monorepo: &lt;br&gt;
(1) install it with &lt;code&gt;npm install -g @anthropic-ai/claude-code&lt;/code&gt; and authenticate once via &lt;code&gt;claude&lt;/code&gt;; (2) create a root &lt;code&gt;.claude/CLAUDE.md&lt;/code&gt; that documents which service owns what, your request pipeline, and your standard response shape; (3) add a &lt;code&gt;.claude/rules/&lt;/code&gt; folder with hard prohibitions (no N+1 queries, no role-name checks, no relative imports); (4) build a &lt;code&gt;.claude/skills/&lt;/code&gt; folder with multi-step workflows and code templates for repeated tasks like creating an endpoint; and (5) add &lt;code&gt;.claude/settings.json&lt;/code&gt; to allow safe commands and deny destructive ones. Commit &lt;code&gt;.claude/&lt;/code&gt; to git so the whole team shares it. The result: Claude generates code that follows your conventions on the first try, instead of guessing.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem: Why Claude Struggles With Large NestJS Projects
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A large NestJS monorepo is one of the hardest environments for any AI coding assistant, because correctness depends on conventions that are invisible in any single file.&lt;/strong&gt; A codebase with dozens of modules, hundreds of DTOs, and custom guards, interceptors, filters, and adapters is genuinely ambiguous. Without context, Claude fills that ambiguity with reasonable defaults — and those defaults won't match your team's conventions.&lt;/p&gt;

&lt;p&gt;Concretely, an unconfigured Claude will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the database directly from a service instead of going through a repository&lt;/li&gt;
&lt;li&gt;Hardcode error strings instead of using your &lt;code&gt;ErrorMessages&lt;/code&gt; constants&lt;/li&gt;
&lt;li&gt;Put a new file in the wrong service (booking logic in &lt;code&gt;operator-service&lt;/code&gt; when it belongs in &lt;code&gt;api-service&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;user.role === 'admin'&lt;/code&gt; instead of using &lt;code&gt;PermissionCodes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;console.log&lt;/code&gt; instead of your structured logger&lt;/li&gt;
&lt;li&gt;Write &lt;code&gt;../../common/utils&lt;/code&gt; instead of the &lt;code&gt;src/&lt;/code&gt; path alias&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Why This Problem Exists
&lt;/h2&gt;

&lt;p&gt;These aren't bugs in Claude — they're the predictable result of asking a model to infer team-specific decisions it was never told about. An AI assistant only sees the files it reads in a session; it cannot see &lt;em&gt;why&lt;/em&gt; your team removed &lt;code&gt;RolesGuard&lt;/code&gt;, that &lt;code&gt;synchronize&lt;/code&gt; must stay &lt;code&gt;false&lt;/code&gt;, or that two services communicate only over HTTP adapters. Tribal knowledge that lives in your engineers' heads is exactly the knowledge Claude lacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix is to externalize that tribal knowledge into version-controlled context&lt;/strong&gt; through three mechanisms, each suited to a different kind of knowledge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;What it encodes&lt;/th&gt;
&lt;th&gt;When Claude uses it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Architecture, ownership, conventions&lt;/td&gt;
&lt;td&gt;Loaded automatically every session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard prohibitions and invariants&lt;/td&gt;
&lt;td&gt;Enforced on every code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-step workflows + templates&lt;/td&gt;
&lt;td&gt;Invoked on demand for repeated tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Repository Structure
&lt;/h2&gt;

&lt;p&gt;Before configuring anything, anchor on the layout. A well-structured NestJS monorepo looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-backend/
├── .claude/                  # Everything Claude needs to operate well
│   ├── CLAUDE.md             # Root-level instructions (always loaded)
│   ├── rules/                # Enforced coding constraints
│   ├── skills/               # Custom slash commands with deep context
│   └── settings.json         # Command permissions
├── services/
│   ├── api-service/          # NestJS consumer API (port 5001)
│   ├── operator-service/     # NestJS operator/vendor API (port 5002)
│   ├── chat-service/         # Express + Socket.IO (JavaScript, port 5005)
│   └── job-service/          # Scheduled jobs, no HTTP server
├── db/migration/             # Flyway migrations per schema
└── package.json              # Root: only husky + lint-staged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service is an independent NestJS app with its own &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;tsconfig.json&lt;/code&gt;, and build output. Inside each service, every feature module follows the same strict three-layer structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/modules/booking/
├── booking.module.ts
├── controllers/    # HTTP routing only, no logic
├── services/       # Business logic and orchestration
├── repositories/   # TypeORM queries only
└── dtos/           # Request/response shapes, validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; This separation is exactly what Claude needs to respect. If it doesn't know the pattern, it mixes layers — services querying the DB directly, controllers calling repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Suggested visual: a diagram of the git-root &lt;code&gt;.claude/&lt;/code&gt; directory applying across all four services.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-Step: Configuring Claude Code for NestJS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Install Claude Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
claude   &lt;span class="c"&gt;# opens a browser for one-time OAuth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After authenticating, &lt;code&gt;claude&lt;/code&gt; works from any directory. Each developer installs and authenticates independently — there's no shared API key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Monorepo tip:&lt;/strong&gt; Claude reads &lt;code&gt;.claude/&lt;/code&gt; from the &lt;strong&gt;git root&lt;/strong&gt;, so its instructions apply no matter where you invoke Claude. When working on one service, &lt;code&gt;cd&lt;/code&gt; into it (e.g. &lt;code&gt;services/api-service/&lt;/code&gt;) so file searches and commands scope correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2 — Create CLAUDE.md
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the single most important file you will write — it's the briefing Claude reads at the start of every session.&lt;/strong&gt; Think of it as onboarding documentation for a senior engineer on day one. Place it at two levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Root &lt;code&gt;.claude/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — service ownership, shared conventions, the request pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-service &lt;code&gt;services/{service}/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — module patterns, auth, and DI specific to that service&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  The Root CLAUDE.md
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Claude can't tell which service should own a new file, so it guesses — and lands consumer logic in the operator service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; A routing decision table plus explicit "never do this" rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Files land in the correct service on the first pass.
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;span class="gu"&gt;## Monorepo Structure&lt;/span&gt;

Four services under &lt;span class="sb"&gt;`services/`&lt;/span&gt;, each independently runnable.
Run all commands from inside the service directory.

| Service            | Port | Stack                    | Purpose              |
| ------------------ | ---- | ------------------------ | -------------------- |
| &lt;span class="sb"&gt;`api-service`&lt;/span&gt;      | 5001 | NestJS + TypeScript      | Consumer-facing API  |
| &lt;span class="sb"&gt;`operator-service`&lt;/span&gt; | 5002 | NestJS + TypeScript      | Operator/vendor mgmt |
| &lt;span class="sb"&gt;`chat-service`&lt;/span&gt;     | 5005 | Express + Socket.IO + JS | Real-time messaging  |
| &lt;span class="sb"&gt;`job-service`&lt;/span&gt;      | —    | Node.js + TypeScript     | Scheduled jobs       |

&lt;span class="gu"&gt;## Which Service Owns What&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**operator-service**&lt;/span&gt; — if the data describes _what an experience is_:
  package definitions, pricing, taxes, availability, operator accounts.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**api-service**&lt;/span&gt; — if the data describes _a consumer's interaction_:
  bookings, users, auth, trip planning, payments, notifications.

| Task involves...                   | Service                            |
| ---------------------------------- | ---------------------------------- |
| Package categories, pricing, slots | operator-service                   |
| Booking status, user profiles      | api-service                        |
| Stripe payments (consumer side)    | api-service                        |
| Cross-service booking flow         | Start in api-service → IMS adapter |

&lt;span class="gu"&gt;## Global Request Pipeline (in order)&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; &lt;span class="sb"&gt;`TraceContextMiddleware`&lt;/span&gt; — sets &lt;span class="sb"&gt;`correlationId`&lt;/span&gt; in &lt;span class="sb"&gt;`AsyncLocalStorage`&lt;/span&gt;
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="sb"&gt;`ValidationPipe`&lt;/span&gt; — whitelist mode, flattens DTO errors
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`LoggerInterceptor`&lt;/span&gt; — logs request/response
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="sb"&gt;`TransformInterceptor`&lt;/span&gt; — wraps return value in the standard shape
&lt;span class="p"&gt;5.&lt;/span&gt; Exception filters (&lt;span class="sb"&gt;`HttpExceptionFilter`&lt;/span&gt;, &lt;span class="sb"&gt;`TypeOrmExceptionFilter`&lt;/span&gt;, …)

&lt;span class="gu"&gt;## Standard Response Shape&lt;/span&gt;

Controllers return &lt;span class="sb"&gt;`{ data, meta }`&lt;/span&gt;; &lt;span class="sb"&gt;`TransformInterceptor`&lt;/span&gt; wraps it:
return { data: { items }, meta: { path: request.path } };
// → { success: true, data: { items }, meta: { path }, error: null }

&lt;span class="gu"&gt;## Cross-Service Communication&lt;/span&gt;

api-service NEVER touches operator-service's database. All reads/writes go
through HTTP adapters in &lt;span class="sb"&gt;`src/common/adapters/`&lt;/span&gt; (they handle OAuth refresh and
trace propagation). Need operator data? Call the adapter, never the DB.

&lt;span class="gu"&gt;## What Claude Must Never Do&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Use &lt;span class="sb"&gt;`console.log`&lt;/span&gt; → use &lt;span class="sb"&gt;`logMessage()`&lt;/span&gt; from &lt;span class="sb"&gt;`src/common/utils/logger`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Check &lt;span class="sb"&gt;`user.role`&lt;/span&gt; → use &lt;span class="sb"&gt;`PermissionCodes`&lt;/span&gt; + &lt;span class="sb"&gt;`@Permissions()`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Query the DB inside a loop → use bulk queries (&lt;span class="sb"&gt;`IN`&lt;/span&gt;, &lt;span class="sb"&gt;`JOIN`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Use relative &lt;span class="sb"&gt;`../../`&lt;/span&gt; imports → use the &lt;span class="sb"&gt;`src/`&lt;/span&gt; alias
&lt;span class="p"&gt;-&lt;/span&gt; Set TypeORM &lt;span class="sb"&gt;`synchronize: true`&lt;/span&gt; → Flyway manages all schema changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What makes a good CLAUDE.md:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision rules over directory listings.&lt;/strong&gt; "If the data describes what an experience &lt;em&gt;is&lt;/em&gt;, it lives in operator-service" lets Claude resolve ambiguity without asking you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include the response shape.&lt;/strong&gt; Otherwise Claude manually wraps responses in every controller, creating inconsistencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List what NOT to do.&lt;/strong&gt; "Never use &lt;code&gt;console.log&lt;/code&gt;" is more actionable than "use our logger."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document what was removed.&lt;/strong&gt; When you delete a &lt;code&gt;RolesGuard&lt;/code&gt; or a &lt;code&gt;synchronize&lt;/code&gt; flag, say why — or Claude will reintroduce it because it looks natural.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Service-Level CLAUDE.md
&lt;/h4&gt;

&lt;p&gt;Each service gets its own file for details too specific for the root:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="gu"&gt;## Module Architecture — never mix layers&lt;/span&gt;

controllers/ HTTP routing only — call service, return data
services/ Business logic — orchestrate repos, no direct DB calls
repositories/ TypeORM queries only — accept EntityManager for transactions

&lt;span class="gu"&gt;## Authentication&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`JwtAuthGuard`&lt;/span&gt; validates Bearer tokens, attaches &lt;span class="sb"&gt;`UserRequest`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`PermissionsGuard`&lt;/span&gt; reads &lt;span class="sb"&gt;`@Permissions([PermissionCodes.X])`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never use RolesGuard or check role names — removed intentionally

&lt;span class="gu"&gt;## Dependency Injection&lt;/span&gt;

Register services by interface token, and inject by token, not class:
{ provide: SERVICE_INTERFACE.BOOKING_SERVICE, useClass: BookingService }

&lt;span class="gu"&gt;## Background Jobs&lt;/span&gt;

BullMQ + Redis. Processors live in &lt;span class="sb"&gt;`src/modules/{module}/processors/`&lt;/span&gt;.
Enqueue via the module's service — never from a controller.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Add Rules
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rules are hard prohibitions — they don't describe architecture, they prevent specific patterns, like an ESLint config for AI-generated code.&lt;/strong&gt; They live in &lt;code&gt;.claude/rules/&lt;/code&gt; as Markdown files.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use &lt;code&gt;CLAUDE.md&lt;/code&gt; for&lt;/th&gt;
&lt;th&gt;Use &lt;code&gt;rules/&lt;/code&gt; for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture, module structure&lt;/td&gt;
&lt;td&gt;Hard prohibitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service ownership decisions&lt;/td&gt;
&lt;td&gt;Patterns Claude must never emit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conventions, command reference&lt;/td&gt;
&lt;td&gt;Security / performance invariants&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Rule: Prevent N+1 Queries
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; N+1 queries are easy to introduce and slip through review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; a prohibition rule with a correct bulk-query pattern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Claude writes the bulk query at generation time — the bug is never created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;.claude/rules/database-queries.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong — N queries&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;bookings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Correct — one query&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;In&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="nx"&gt;bookings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Rule: Permission Checks
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;.claude/rules/permissions.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Correct&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;PermissionCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MANAGE_BOOKINGS&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseGuards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JwtAuthGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PermissionsGuard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; role names get merged, split, and renamed; checks against them break&lt;br&gt;
silently. &lt;code&gt;PermissionCodes&lt;/code&gt; are stable constants that survive role&lt;br&gt;
restructuring. Import from &lt;code&gt;src/common/constants/permissions.constant.ts&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Rule: Import Style
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;.claude/rules/imports.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../services/user.service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Correct&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/modules/user/services/user.service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;src/&lt;/code&gt; alias is configured in &lt;code&gt;tsconfig.json&lt;/code&gt; (&lt;code&gt;baseUrl: "./"&lt;/code&gt;) and Jest's&lt;br&gt;
&lt;code&gt;moduleNameMapper&lt;/code&gt;. Use it everywhere.&lt;/p&gt;
&lt;h4&gt;
  
  
  Other Rules Worth Defining
&lt;/h4&gt;

&lt;p&gt;The same prohibition pattern applies to many recurring NestJS pitfalls. A few more rules worth their own file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt; — always throw from &lt;code&gt;ErrorMessages&lt;/code&gt;/&lt;code&gt;ResponseCodeKeys&lt;/code&gt; constants and use NestJS exceptions; never hardcode strings or return raw error objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt; — use the structured &lt;code&gt;logMessage()&lt;/code&gt; helper at the right level; never &lt;code&gt;console.log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; — multi-write operations must run in a single &lt;code&gt;EntityManager&lt;/code&gt; transaction; never fire independent writes that can half-commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DTO validation&lt;/strong&gt; — every request DTO needs &lt;code&gt;class-validator&lt;/code&gt; decorators; the &lt;code&gt;ValidationPipe&lt;/code&gt; whitelist drops anything undecorated, so missing decorators silently lose data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity ↔ migration parity&lt;/strong&gt; — any entity change needs a matching Flyway migration; &lt;code&gt;synchronize&lt;/code&gt; stays &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swagger coverage&lt;/strong&gt; — every endpoint carries &lt;code&gt;@ApiSpec&lt;/code&gt; + a typed response DTO so the generated docs stay accurate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 4 — Build Skills
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Skills are project-specific slash commands that bundle a workflow, code templates, and references — they turn a repeated multi-file task into one command.&lt;/strong&gt; Write one for any repeated, multi-step task: building an endpoint, writing a migration, scaffolding a module, reviewing a PR.&lt;/p&gt;
&lt;h4&gt;
  
  
  Skill Structure
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/api-development/
├── SKILL.md                    # Workflow, trigger, steps
├── assets/
│   ├── controller-template.md
│   ├── service-template.md
│   ├── repository-template.md
│   └── dto-template.md
└── references/
    ├── nestjs-conventions.md
    └── swagger-documentation.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Writing SKILL.md
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# API Development Skill&lt;/span&gt;

&lt;span class="gu"&gt;## Trigger&lt;/span&gt;

Invoke with &lt;span class="sb"&gt;`/api-development`&lt;/span&gt; when building or modifying a NestJS endpoint.

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; &lt;span class="gs"&gt;**Determine service &amp;amp; module**&lt;/span&gt; — check CLAUDE.md routing; ask if ambiguous.
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Read what exists**&lt;/span&gt; — entities, repositories, DTOs already in the module.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Generate the DTO first**&lt;/span&gt; — it defines the contract. class-validator
   decorators; extend &lt;span class="sb"&gt;`PaginationRequestDTO`&lt;/span&gt; for lists.
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="gs"&gt;**Generate the repository**&lt;/span&gt; — inject &lt;span class="sb"&gt;`DataSource`&lt;/span&gt;; accept an optional
   &lt;span class="sb"&gt;`EntityManager`&lt;/span&gt; for transactions; queries only, no logic.
&lt;span class="p"&gt;5.&lt;/span&gt; &lt;span class="gs"&gt;**Generate the service**&lt;/span&gt; — inject the repository interface token; use
   &lt;span class="sb"&gt;`ErrorMessages`&lt;/span&gt; constants; throw &lt;span class="sb"&gt;`NotFoundException`&lt;/span&gt; etc.
&lt;span class="p"&gt;6.&lt;/span&gt; &lt;span class="gs"&gt;**Generate the controller**&lt;/span&gt; — &lt;span class="sb"&gt;`@ApiSpec`&lt;/span&gt; + &lt;span class="sb"&gt;`@ApiOkResponse`&lt;/span&gt;; return
   &lt;span class="sb"&gt;`{ data, meta }`&lt;/span&gt;; no business logic.
&lt;span class="p"&gt;7.&lt;/span&gt; &lt;span class="gs"&gt;**Wire the module**&lt;/span&gt; — add to &lt;span class="sb"&gt;`providers`&lt;/span&gt;, export by interface token.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Asset Template (example)
&lt;/h4&gt;

&lt;p&gt;Templates should be real, compilable code — not pseudocode. &lt;code&gt;assets/controller-template.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiTags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookingController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SERVICE_INTERFACE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BOOKING_SERVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;bookingService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IBookingService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseGuards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JwtAuthGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PermissionsGuard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;PermissionCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATE_BOOKING&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create a new booking&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiCreatedResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateBookingSuccessDto&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateBookingDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BookingEntity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;booking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bookingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;booking&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this skill, a complete endpoint (controller + service + repository + DTOs, wired into the module) takes &lt;strong&gt;under 3 minutes instead of ~20&lt;/strong&gt; — and matches your conventions on the first try.&lt;/p&gt;

&lt;h4&gt;
  
  
  Other Skills Worth Building
&lt;/h4&gt;

&lt;p&gt;Any repeated, multi-step workflow with team-specific conventions is a skill candidate. Beyond endpoint scaffolding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Migration generator&lt;/strong&gt; — scaffold a Flyway migration in the right schema folder with your versioning and naming convention, plus the matching entity change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module scaffolder&lt;/strong&gt; — generate a full module skeleton (module file, three layers, DI tokens) wired and ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PR review&lt;/strong&gt; — review a diff against your conventions: N+1 checks, permission usage, layer boundaries, missing tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test generator&lt;/strong&gt; — produce &lt;code&gt;*.spec.ts&lt;/code&gt; files following your mocking and fixture patterns for a given service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-service adapter&lt;/strong&gt; — generate a new IMS adapter method plus its response interface and transform helper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use-case / BDD docs&lt;/strong&gt; — turn a requirement into user stories with Given/When/Then acceptance criteria.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5 — Configure Settings &amp;amp; Permissions
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt; controls which Bash commands run without a permission prompt. Allow read-only and safe test/lint operations freely; deny anything destructive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm run lint:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm run test:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm run build)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npx jest *)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(rm -rf *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git reset --hard *)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Applies to&lt;/th&gt;
&lt;th&gt;Committed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everyone on the team&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You only (overrides)&lt;/td&gt;
&lt;td&gt;No (gitignored)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Best Practice callout:&lt;/strong&gt; Treat &lt;code&gt;.claude/&lt;/code&gt; as production code. It ships your conventions to every engineer and every AI session — review it, version it, and prune it like any other source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep CLAUDE.md under ~300 lines.&lt;/strong&gt; Context has a cost; a bloated file crowds out the code Claude needs to read. If it's derivable from the code, cut it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write rules as prohibitions, not guidance.&lt;/strong&gt; "NEVER call the DB in a loop" is enforced; "prefer bulk queries" is ignored under pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put templates in skills, not CLAUDE.md&lt;/strong&gt; — they should load only when the skill is invoked, not on every session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version &lt;code&gt;.claude/&lt;/code&gt; in git.&lt;/strong&gt; It's part of your codebase: same review, same history. New rules get PRs; stale rules get deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use decision tables liberally.&lt;/strong&gt; Anywhere two reasonable choices exist — DTO placement, entity location, service boundaries — a table beats prose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Recommended &lt;code&gt;.claude/&lt;/code&gt; layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/
├── CLAUDE.md                  # Root guide (&amp;lt;300 lines)
├── settings.json              # Team permissions (committed)
├── rules/
│   ├── database-queries.md    # No N+1
│   ├── permissions.md         # PermissionCodes, no role checks
│   └── imports.md             # src/ alias only
└── skills/
    └── api-development/
        ├── SKILL.md
        ├── assets/            # controller/service/repository/dto templates
        └── references/        # conventions, swagger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Each of these silently degrades output quality — Claude will &lt;em&gt;look&lt;/em&gt; like it's working while quietly violating your conventions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Writing CLAUDE.md as a tutorial.&lt;/strong&gt; Don't explain what NestJS is — Claude knows. Document only what's unique to your project: module names, error constants, response shape, permission system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Omitting negative rules.&lt;/strong&gt; Telling Claude what &lt;em&gt;not&lt;/em&gt; to do prevents whole classes of mistakes. The expensive errors are the ones that look correct but violate your patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cramming everything into the root CLAUDE.md.&lt;/strong&gt; It loads in every session. Split service-specific depth into per-service files so it doesn't pollute context when you're elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No routing decision table.&lt;/strong&gt; Wrong-service placement comes from ambiguity, not ignorance. A table makes the decision deterministic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting the cross-service adapter pattern.&lt;/strong&gt; In an HTTP-linked monorepo, Claude will try to import across services or query the other DB. Spell out that all cross-service calls go through adapters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not mentioning &lt;code&gt;synchronize: false&lt;/code&gt;.&lt;/strong&gt; Claude assumes TypeORM manages the schema. If you use Flyway, entity changes without a matching migration fail silently — say so explicitly.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Real-World Example: Adding a Consumer-Visible Booking Status
&lt;/h2&gt;

&lt;p&gt;A walkthrough of how a configured setup changes one real task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The request:&lt;/strong&gt; &lt;em&gt;"Add a consumer-visible status to a booking package."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without configuration&lt;/strong&gt;, Claude reasons: "packages live in &lt;code&gt;operator-service&lt;/code&gt;," and edits the package entity there — the wrong service. It hardcodes the status string, and adds an &lt;code&gt;if (user.roles.includes('admin'))&lt;/code&gt; guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With configuration&lt;/strong&gt;, here's the chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Routing table&lt;/strong&gt; in &lt;code&gt;CLAUDE.md&lt;/code&gt; tells Claude that &lt;em&gt;consumer-visible&lt;/em&gt; status describes a consumer interaction → it belongs in &lt;code&gt;api-service&lt;/code&gt;, not &lt;code&gt;operator-service&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-service rule&lt;/strong&gt; reminds it the operator's package definition is read via an IMS adapter, so it adds the field to the adapter's transform — not by reaching into the other DB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api-development&lt;/code&gt; skill&lt;/strong&gt; scaffolds the DTO, repository method, service logic, and controller in the right order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission rule&lt;/strong&gt; makes it emit &lt;code&gt;@Permissions([PermissionCodes.VIEW_BOOKING_STATUS])&lt;/code&gt; instead of a role check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error rule&lt;/strong&gt; makes it throw &lt;code&gt;NotFoundException&lt;/code&gt; with an &lt;code&gt;ErrorMessages&lt;/code&gt; constant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One ambiguous sentence, resolved into correct, convention-matching code across four files — without a single clarifying question.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results &amp;amp; Metrics
&lt;/h2&gt;

&lt;p&gt;A few hours of one-time setup changes the day-to-day in measurable ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Review every generated file for hardcoded strings, wrong imports, role checks&lt;/td&gt;
&lt;td&gt;Rules make those impossible — review focuses on logic, not conventions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New endpoint ≈ 20 min wiring four layers by hand&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/api-development&lt;/code&gt; scaffolds + wires it in under 3 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files landed in the wrong service, caught late in review&lt;/td&gt;
&lt;td&gt;Routing table places them correctly on the first pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 queries and &lt;code&gt;console.log&lt;/code&gt; slipped into PRs&lt;/td&gt;
&lt;td&gt;Caught at generation time, never written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New hires guessed at module boundaries and patterns&lt;/td&gt;
&lt;td&gt;CLAUDE.md teaches the conventions implicitly on day one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compounding wins that don't show up in a single PR:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency.&lt;/strong&gt; Code generated months apart looks the same, because it comes from the same templates and rules — not from whatever Claude inferred that day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less review fatigue.&lt;/strong&gt; Reviewers stop flagging the same convention violations and spend attention on actual design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster onboarding.&lt;/strong&gt; Routing tables and rules are documentation that also executes — a new engineer's first AI-assisted feature already follows house style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower context cost.&lt;/strong&gt; A tight CLAUDE.md plus on-demand skills means Claude spends its context budget reading &lt;em&gt;your&lt;/em&gt; code, not re-deriving conventions every session.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extractable insight:&lt;/strong&gt; The investment pays for itself within the first few features and keeps paying on every one after — because the cost is one-time and the benefit recurs on every task.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is CLAUDE.md?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; is a Markdown file Claude Code reads automatically at the start of every session. It briefs the AI on your project's architecture, conventions, and prohibitions — the equivalent of onboarding docs for a new engineer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should CLAUDE.md go in a monorepo?&lt;/strong&gt;&lt;br&gt;
Put a root &lt;code&gt;.claude/CLAUDE.md&lt;/code&gt; at the git root for cross-cutting concerns (service ownership, shared conventions), and a &lt;code&gt;CLAUDE.md&lt;/code&gt; inside each service for service-specific patterns. Claude reads the root first, then the service-level file when you work in that directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between CLAUDE.md and rules?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; describes architecture and conventions; rules (in &lt;code&gt;.claude/rules/&lt;/code&gt;) are hard prohibitions Claude must never violate. Use &lt;code&gt;CLAUDE.md&lt;/code&gt; for "here's how things work" and rules for "never do this," such as banning N+1 queries or role-name checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do Claude Code skills work?&lt;/strong&gt;&lt;br&gt;
A skill is a project-specific slash command (e.g. &lt;code&gt;/api-development&lt;/code&gt;) backed by a &lt;code&gt;SKILL.md&lt;/code&gt; workflow plus code templates and references. Invoking it makes Claude follow a deterministic, multi-step process — like scaffolding a full NestJS endpoint across four files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long should CLAUDE.md be?&lt;/strong&gt;&lt;br&gt;
Aim for under ~300 lines. Context is finite, and a bloated file crowds out the actual code Claude needs to read. Document only what isn't derivable from the codebase itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Claude Code work with a NestJS monorepo of multiple services?&lt;/strong&gt;&lt;br&gt;
Yes. Claude reads &lt;code&gt;.claude/&lt;/code&gt; from the git root, so one configuration governs every service. A routing decision table in &lt;code&gt;CLAUDE.md&lt;/code&gt; tells Claude which service owns which kind of data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop Claude from writing N+1 queries or role-based permission checks?&lt;/strong&gt;&lt;br&gt;
Add prohibition rules in &lt;code&gt;.claude/rules/&lt;/code&gt;. A &lt;code&gt;database-queries.md&lt;/code&gt; rule bans DB calls inside loops; a &lt;code&gt;permissions.md&lt;/code&gt; rule bans &lt;code&gt;user.role&lt;/code&gt; checks in favor of &lt;code&gt;PermissionCodes&lt;/code&gt; and &lt;code&gt;@Permissions()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I commit the .claude directory to git?&lt;/strong&gt;&lt;br&gt;
Yes — commit &lt;code&gt;.claude/CLAUDE.md&lt;/code&gt;, &lt;code&gt;rules/&lt;/code&gt;, &lt;code&gt;skills/&lt;/code&gt;, and &lt;code&gt;settings.json&lt;/code&gt; so the whole team shares the same configuration. Keep personal overrides in &lt;code&gt;.claude/settings.local.json&lt;/code&gt;, which is gitignored.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways (Recap)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claude struggles in large NestJS monorepos because &lt;strong&gt;correctness depends on conventions invisible in any single file&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Externalize that knowledge with &lt;strong&gt;CLAUDE.md (briefing), rules (prohibitions), and skills (workflows)&lt;/strong&gt; — all committed to git.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;service-routing decision table&lt;/strong&gt; is the highest-leverage thing you can write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules phrased as prohibitions&lt;/strong&gt; are enforced; preferences are not.&lt;/li&gt;
&lt;li&gt;Setup is &lt;strong&gt;a few hours, one-time&lt;/strong&gt;, and compounds across every feature and every new hire.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up Claude Code for a NestJS monorepo isn't about making Claude smarter — it's about eliminating guesswork. A codebase with dozens of modules, hundreds of DTOs, and custom guards and adapters is genuinely ambiguous without context, and Claude's reasonable defaults won't match your conventions.&lt;/p&gt;

&lt;p&gt;The investment is small and one-time: a focused &lt;code&gt;CLAUDE.md&lt;/code&gt; for routing and prohibitions, a few rules files for hard constraints, and one or two skills for your most repeated tasks. After that, Claude generates code that looks like your team wrote it — not like someone who just read the NestJS docs.&lt;/p&gt;




&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.claude.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code documentation&lt;/a&gt; — official setup, configuration, and best-practices reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.nestjs.com" rel="noopener noreferrer"&gt;NestJS documentation&lt;/a&gt; — providers, guards, interceptors, and module architecture&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://typeorm.io" rel="noopener noreferrer"&gt;TypeORM&lt;/a&gt; · &lt;a href="https://github.com/typestack/class-validator" rel="noopener noreferrer"&gt;class-validator&lt;/a&gt; · &lt;a href="https://documentation.red-gate.com/flyway" rel="noopener noreferrer"&gt;Flyway&lt;/a&gt; — the data, validation, and migration tooling referenced above&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Patterns here are drawn from a production NestJS monorepo running four services: a consumer API, an operator management system, a real-time chat service, and a scheduled job runner. The approach scales to any NestJS project with conventions worth preserving.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>claudecode</category>
      <category>ai</category>
      <category>monorepo</category>
    </item>
  </channel>
</rss>
