Writing 路 31 July 2026
Stop Vibe Coding: The Foundations of Agentic Development
Agentic development isn't vibe coding. The CLAUDE.md foundation I use across four years of agent-assisted engineering, with a 75% DORA improvement at Ligentia.

I get asked a lot, both in the training I run for clients and in the technical communities I spend time in, how to get started with agentic development. The phrasing changes; the underlying question is always the same. “What’s your process? Where do I begin?”
I have started writing the answer down, partly so I can stop copy-pasting the same paragraphs into community Slack channels and Discord servers, and partly because the answer keeps getting longer. What follows is the first part of that answer, turned into something I can point people at.
A note on the term before I begin, because there is genuine confusion in the wider conversation. What I am describing is agentic development: using Artificial Intelligence (AI) agents and Large Language Models (LLMs) to help write, refactor, and reason about software. This is sometimes called “AI engineering,” but that label belongs more properly to a different discipline; building applications on top of foundation models, in the sense Chip Huyen uses in her 2024 book of the same name. The two are easy to confuse and I don’t blame anyone for arriving at the wrong door. If you turned up looking for the second discipline, the one about building AI features into applications, Codrina Merigo and I covered exactly that on episode 21 of season 8 of The Modern .NET Show. That episode is a much better starting point than this post will be. The rest of this series is about the other thing.
I should also say what this is not, because the loudest voice in the room about agentic development right now belongs to people advocating something they call vibe coding; the practice of generating code by feel, shipping it without reading it, and letting the agent figure out the consequences. That is not what I do. I have spent four years applying agentic development across personal projects, open-source contributions, and client production work, including a twelve-month engagement at Ligentia that closed in May with a 75% improvement in their DevOps Research and Assessment (DORA) feature-delivery measurements (the longer story is in the case study). The need to ask permission before using these tools on real engineering work is fading; the industry is coming around to seeing them the way it sees Integrated Development Environments (IDEs) and debuggers, as standard equipment for the job rather than exotic toys. What hasn’t changed, and shouldn’t, is the engineering discipline around them.
This is the foundation that twelve months of work at Ligentia, and four years of work before it, was built on. It is the first of three posts. In Part 2 I will cover three workflows I use to tackle actual feature work and bug fixes. In Part 3 I will cover hardening: rules, permissions, and tooling discipline.
Why Vibe Coding Doesn’t Scale
The term vibe coding was coined by Andrej Karpathy in February 2025 and has been adopted widely enough to have its own Wikipedia page. The idea, in Karpathy’s own framing, is to “fully give in to the vibes, embrace exponentials, and forget that the code even exists.” You describe what you want, accept what the agent produces, and ship.
The seductive logic is hard to ignore. If the agent can write correct code, what does it matter whether you read it? If the tests pass, isn’t that the point? If the feature works in production, who cares which keystrokes produced it? The argument starts to sound reasonable until you look at what happens when stakes rise.
The first thing that goes wrong is maintenance. Code that nobody on the team has read becomes code that nobody on the team can change with confidence. When the bug report lands six weeks later, the engineer reaching for the file has no mental model of how it works, and the agent that wrote it has no continuity with the conversation that produced it. The first hour of investigation is just rebuilding the context that the original session threw away.
The second thing that goes wrong is security. Models will, given the chance, produce code that compiles, passes obvious tests, and contains a category of mistake the agent has seen often enough to consider normal. Hard-coded credentials, missing input validation, the wrong cryptographic primitive used in the right place. None of these are detected by “the tests pass”; all of them are detected by “an engineer who knows what they are looking at read the diff.”
And then there is judgement. Software engineering has never really been about typing; it has been about the decisions made before the typing started: which interface to expose, what invariants a module must preserve, what happens at the edges of the system where most of the real work lives. Agents can propose answers to those questions, and increasingly good ones, but they cannot be accountable for them. That accountability lives with the engineer.
The metaphor I keep returning to is one of collaboration. You wouldn’t merge a junior developer’s first pull request without reading it. Juniors do not necessarily produce bad work; review is how a team produces work it can stand behind. The agent’s output is no different in kind, only in volume. The discipline of reviewing it, really reading it, asking it about the parts you don’t understand, sending it back to revise, is the discipline that separates serious adoption from vibe coding.
If you are going to do this seriously, three things have to be in place from the first day. The agent needs a shared understanding of the project (a document I will come to in a moment). You need a technique for giving it context. And you need a process for keeping both honest as the project evolves. The rest of this post is those three things.
CLAUDE.MD as the Agent’s Onboarding Document
The single most useful thing I have built into my agentic development setup is a file called CLAUDE.md, which lives at the root of every repository I work on. Other tooling uses different filenames; the cross-vendor convention is now AGENTS.md (the spec is on GitHub), and the principle is the same whether you call it one thing or the other. I will use CLAUDE.md for the rest of this post because that is what I use day to day; substitute your own filename as needed.
The file is loaded alongside the system prompt every time you start a new conversation. That means two things. The first is that everything in it is paying for space in your context window before you have even sent a message. The second is that the agent will treat its contents as ground truth. Both of those facts shape how I write it.
The way I learned to create one was by asking the agent. I gave Claude a fresh repository, asked it to do a shallow scan (which projects existed where, without diving into the source code files individually), read the README, and propose a first draft of CLAUDE.md based on what it had found. The agent knows how it works and how to make itself useful to you; it just needs permission to tell you. Asking it to write its own onboarding document is the cleanest version of that permission.
Once I had a draft, I asked it to explain what each section was for, and to guide me through writing one of my own. Then I asked it about context length; specifically, when this file gets loaded and how big it should be. The answer surfaced the discipline that has shaped my CLAUDE.md files ever since: keep them lean, because every token in here is a token you cannot spend elsewhere in the conversation. A CLAUDE.md file should not read like a Shakespearean play.
The next question I asked was about deeper documentation. Architecture notes, design decisions, coding standards: where do those go? The answer is that the agent does not automatically follow Markdown links inside CLAUDE.md, but it does know about them. So you can keep CLAUDE.md short, and link out to files in a docs/ directory that the agent will read when the conversation needs them (a process called “prograssive discovery”) and ignore when it does not. You get the smallest possible always-loaded surface, and the agent gets a map of where everything else lives.
What ends up in mine is roughly this. A one-line description of the project. A list of the build, test, and run commands the agent will need. A short summary of the architecture, often with a sentence per major component. The conventions that apply across the codebase (linting, naming, file layout). A small set of behavioural rules with explanations of why they exist (I will come back to those in a later section, because they deserve more space than they would get here). And links out to deeper documentation in docs/.
馃挕 Tip
The agent will, the first time you talk to it about anything substantial, want to put a changelog directly into
CLAUDE.md. Don’t let it. The changelog is the worst kind of always-loaded content; it grows monotonically, every entry pushes the actually-useful content further down, and almost none of it will be relevant to any individual conversation. Move it todocs/CHANGELOG.mdand link to it from the root file. Your future self will thank you.
Two Ways to Give the Agent Context
CLAUDE.md solves the always-loaded baseline. Most of the conversations you will have with the agent need more than the baseline; specific files, specific scenarios, specific constraints. There are two techniques I use for getting that context into the conversation, and I pick between them based on how well I know the codebase.
The first is to front-load. When I know the part of the system I am about to change, I tell the agent before I tell it the task. A typical opener looks something like this:
“I would like your help to fix a bug. There is some documentation in
docs/, and I’d focus ondocs/orders.mdandsrc/orders/dispatcher.tsbecause they explain how the order-dispatch flow works. Read those, get a good understanding of how it all hangs together, and then I will describe the bug.”
Two things matter about that opening. The first is that I am telling the agent exactly where to look. That saves the conversation from a noisy, expensive opening round of “let me explore the repository,” which on a large codebase can chew through context before any real work begins. The second is that I am asking the agent to build understanding before I introduce the task. Models that have read the relevant files first will write better follow-up questions and propose better plans than models that are reading and reasoning in the same breath as you are typing the requirements.
The second technique is the opposite shape, and I use it when I do not know the codebase well enough to front-load usefully. Instead of telling the agent what to read, I ask it what it would need to know. The shortest version of the prompt is “what questions would you ask me to help solve this?” and you can build on it: “what would you want to investigate before starting?”, “which files would you want to read to answer this?”. The replies surface gaps you may not have anticipated. The agent will, surprisingly often, ask for context you would not have thought to give it and which turns out to be load-bearing.
This second pattern is the one I described to a non-engineer earlier this year, when I was working with the journalist Ollie Peart on his first production application. Ollie applied it to a specific debugging problem (“what questions would you ask me to help fix the image display issue?”) and got back a list of constraints he had not initially provided. The story is in the case study for that engagement, and the technique is the same one I use when I am onboarding an agent to a million-line brownfield codebase.
The technique scales from a journalist shipping his first app to a senior engineer working on production logistics infrastructure. The technique stays the same; the discipline around it is what changes. The discipline is review, verification, and the judgement to push back on answers that do not smell right. That is what separates serious agentic development from vibe coding.
Exploring a Brownfield Codebase Without Blowing Through Context
The two techniques above work well when the codebase is small enough for the agent to hold in its head, or when you already know which files matter. They struggle on what most working engineers spend most of their time in: large, old, partially-undocumented codebases that no individual on the current team understands end to end.
The technique I use for those is older than agentic development, and adapts to it well. Treat the codebase like a tree, and walk it one level at a time.
I will start the agent at the root of the repository and ask it to read what is there, then descend one directory at a time. At each level, it writes a small CLAUDE.md (an intermediate one, not the final root file) summarising what it found. The intermediate files act as a save point; when the conversation runs out of context, I can clear it, point the agent at the intermediate files, and resume where I was without losing the understanding I had already paid for.
I cap the depth at roughly five levels as a heuristic, not a rule. The point is to protect the context budget on deeply nested codebases; if your tree is fifteen levels deep, you would burn through every token long before you reached anything useful. Five is the number I have found I rarely need to exceed before I have enough to work with. I also tell the agent explicitly which top-level directories to start in. src/, tests/, docs/, and similar; anywhere meaningful source code or documentation lives. Without that instruction, the agent will happily start reading node_modules/, vendor/, dist/, and build artefacts, none of which earn their place in the context window.
Once the agent has reached its depth limit or finished the tree, it comes back up, combining and summarising the intermediate CLAUDE.md files as it goes. By the time it is back at the root, it has a top-down picture of the codebase. From there I ask it to create a docs/ directory and write a file per subject: architecture, design, the coding standards already in use, deployment specifics. The root CLAUDE.md links to each of those files, and the intermediate ones get deleted.
The reason this works, and why I keep coming back to it, is that it forces the agent to actually understand the code rather than pattern-match against it. The intermediate write-up at each level is doing real cognitive work; the consolidation step is doing real synthesis. The output of that process is a set of documents that are useful both to the agent and, just as importantly, to the humans who have to maintain the codebase.
The most striking demonstration of this technique I have run was during my engagement at Ligentia. Part of the work involved a twenty-plus-year-old codebase that no one in the company fully understood; the original architects had long since moved on, and the institutional memory had decayed with them. I ran the directory-by-directory walk (also known as progressive discovery) over the codebase, ended up with a docs/ directory of human-readable architecture and behaviour notes, and handed it back to the team. They could then use those documents to work with Claude on surgical bug fixes; the agent had the context it needed, and so did they.
Keeping the System Honest
A CLAUDE.md file and a docs/ directory are only useful if they stay current. The most common failure mode I have seen in teams adopting this is shipping a beautiful initial set of documentation and then letting it rot, until six months later it is actively misleading; the agent reads it and confidently produces code based on assumptions that no longer hold.
The habit that prevents this is small and unglamorous. Every time I make a change, or work with the agent to make one, the last step of the conversation is “check CLAUDE.md and the files in docs/. What needs updating?” The agent compares what it just helped me change against what the documentation says. Most of the time the answer is “nothing”; when the answer is “this file no longer reflects how the dispatch flow works,” I update it. That is it. Five seconds per session. The result is a documentation set that stays usefully accurate over months and years.
The changelog deserves a particular note because the agent will keep wanting to extend it. Move it out of CLAUDE.md (as I mentioned earlier), keep it in docs/CHANGELOG.md, and standardise the format. I usually point the agent at the Keep a Changelog specification when I set this up, which gives you a consistent structure that is easy to read, easy to diff, and easy to consume programmatically. That last property matters more than it sounds; once you have a standards-compliant changelog, you can build automation around it. The natural next step is to write a skill or command that, at Continuous Integration / Continuous Deployment (CI/CD) time, reads the developer-facing changelog and produces a public-facing one with the implementation detail stripped out. That is something I have built for clients before, and it is the kind of tooling I will come back to in Part 3.
The other thing worth maintaining is the set of behavioural rules in CLAUDE.md. The rules I include are things like “run the linter when you are finished,” “add unit tests before implementation, then make them pass,” and “do not run git add.” What matters more than the rules themselves is that each one is accompanied by the reason it exists. “Do not run git add, because I want to review every change before it is committed.” The reason is what makes the agent useful at edge cases. If you only give it the rule, it will look for loopholes; if you give it the rule and the reason, it understands the spirit and behaves sensibly when the situation falls outside the literal text. Rules are deep enough to deserve their own treatment, and I will come back to them in Part 3.
Where This Is Heading
This is the foundation. In Part 2 I will cover three workflows I use for tackling actual engineering work: a lightweight conversation-driven approach for small tasks, GitHub’s spec-kit for larger features (which I discussed on episode 640 of Coder Radio), and the BMAD-Method for solo work where the multi-agent personas are doing real cognitive work. In Part 3 I will cover hardening the setup: rules with reasons (in more depth), secure coding guidance, why I have never once run Claude with --dangerously-skip-permissions, and the discipline around custom skills, commands, and Model Context Protocol (MCP) servers.
The argument across all three posts is the same. We have done this before, as an industry. We wrote in assembly, then we wrote in higher-level languages and trusted the compiler to translate. We wrote frameworks, then we wrote against frameworks and trusted other people’s libraries. Each abstraction layer changed what we typed; none of them changed what we were. Engineers exercising judgement over a system they were responsible for. Agentic development is the next layer up. The judgement is still ours.
That is, in the end, what divides serious agentic development from vibe coding. Vibe coding is what happens when engineers forget that the layer below is still their responsibility. Serious agentic development is the practice of staying responsible for it; with help, at speed, and with the discipline to keep both the codebase and the documentation honest as the work compounds.
If you want your team to adopt this kind of discipline rather than vibe-coding their way into a maintenance nightmare, this is the work I do with clients in training and embedded engagements. Get in touch to talk about what your team needs.