AI-assisted development
Letting AI Agents Into a Codebase Without Losing Its Architecture
AI coding agents are fast. That is the whole problem.
Given a task like “add an endpoint that returns a resident’s open filings”, an agent will produce a controller, a query, a DTO, and a test in a couple of minutes. The code compiles. The test passes. And the controller talks to the database directly, because that was the shortest path to green, and nothing told it otherwise.
It is not wrong code. It is misplaced code. On a Clean Architecture codebase, misplaced code is the thing that turns a clear design into a pile over the course of a year, one reasonable-looking pull request at a time. Agents do not make that failure new. They make it fast.
Here is what I put in place so that generated code lands where a careful engineer would have put it.
An instruction file the agent actually reads
Every agent has a file it loads before it starts: copilot-instructions.md for GitHub Copilot, CLAUDE.md for Claude Code, AGENTS.md for most of the rest. The mistake is to treat it as documentation. It is a briefing, and it gets skimmed, so it has to be short and it has to lead with the rules that matter.
Mine has four parts, in this order:
- The dependency direction, in one paragraph. Domain references nothing. Application references Domain. Infrastructure references Application. Web references all of them and exposes nothing back. If you only read one paragraph, read this one.
- Where things live. A request handler goes in Application under its feature folder. An EF configuration goes in Infrastructure. A Vue view that calls an endpoint goes next to the other views for that feature. Paths, not prose.
- What not to do. No
DbContextoutside Infrastructure. No HTTP types in Application. No business rules in a controller or a Vue component. Each rule is one line, because the agent will match on it, not reason about it. - How to verify. The exact commands to build, run the tests, and run the architecture tests. An agent that knows how to check its work will check it.
Everything else, the style guide, the naming conventions, the history, goes in linked files that the agent can open when it needs them. The top file stays under a screen.
Skills for the work that repeats
Most of what an agent does in a mature codebase is a variation of something that has been done before. Add an endpoint. Add a migration. Add a screen that calls an endpoint. Each one has a dozen small conventions attached, and every time the agent rediscovers them it gets a few wrong.
So I write the conventions down once, as a skill: a short file that says what the task is, the steps in order, the files that get touched, and what done looks like. “Add an endpoint” lists the request and response records, the handler, the validator, the endpoint registration, the integration test, and the front-end client method, with the folder for each. The agent loads the skill, follows the steps, and produces the same shape of change that the last twenty endpoints have.
The side effect is that the skills become the best onboarding documentation the project has. A new engineer reads the same files the agent does, and for the same reason.
Architecture the agent can see
A diagram in a wiki is invisible to an agent. A diagram in the repository is not. I keep the layer diagram as text, Mermaid or plain ASCII, next to the instruction file, and I keep it honest: if a dependency exists, it is on the diagram, and if it is on the diagram, it is allowed.
That gives the agent something it can quote back. When it proposes a change that crosses a boundary, it can see the boundary. When it explains a change in a pull request, it can say which layer each file belongs to, and a reviewer can check the claim in seconds.
Rules that fail the build
None of the above is enforcement. An instruction is a suggestion, and agents, like people, take the shortest path when nobody is checking. So the boundaries are also tests.
In .NET that is a small xUnit project using NetArchTest or ArchUnitNET. Each test is one sentence: types in Domain must not depend on Infrastructure; types in Application must not reference ASP.NET Core; nothing outside Infrastructure may reference the EF Core namespace. They run with the rest of the suite and they run in CI.
This changes how the agent behaves more than any instruction does. A red test is feedback in the agent’s own loop. It sees the failure, reads the test name, and moves the code to the right place, usually without being asked. Prose gets skimmed. A failing build gets fixed.
Review as if it were a new team member
Agent-generated pull requests get the review a new hire’s would: light on syntax, heavy on placement. The questions are the same ones I would ask a person in their first month. Why is this here? What does this layer know about now that it did not know before? Would this still be the right place if the feature grew?
The instruction file, the skills, and the tests exist so that the answer is usually “it is in the right place”. When it is not, the fix goes into one of those three things, not only into the pull request, so the same mistake does not come back next week from a different prompt.
What you get
Done this way, agents are a genuine speed-up rather than a debt generator. Generated code lands in the right layer. The conventions are written down, current, and read by every contributor, human or not. The architecture is checked on every push instead of being remembered by whoever has been on the project longest.
The tools will keep getting faster. The architecture is what stops them being fast in the wrong direction.
Found this useful? Share it.
A ready-made LinkedIn post is one click away, or share the link wherever your team talks.
AI coding agents are fast. That is the whole problem. Ask one to "add an endpoint" and in two minutes you have a controller, a query, and a passing test. And the controller talks to the database directly, because that was the shortest path to green and nothing told it otherwise. It is not wrong code. It is misplaced code. On a Clean Architecture codebase, misplaced code is what turns a clear design into a pile, one reasonable-looking pull request at a time. Agents did not invent that failure. They made it fast. What I put in place so generated code lands where a careful engineer would have put it: • An instruction file that leads with the dependency direction, not the style guide • Skills for the work that repeats: add an endpoint, add a migration, add a screen • A layer diagram in the repo, as text, that the agent can actually read • Architecture tests that fail the build. A red test is feedback in the agent's own loop. Prose gets skimmed. The tools will keep getting faster. The architecture is what stops them being fast in the wrong direction. {url}