Blog
5 min read

Garbage In, Garbage Out: Why Your Repository Is the Real Model

An AI agent does not obey your prompt. It obeys your codebase. If the base of the project is weak, the model copies that weakness at high speed. The fix is not a better prompt or a second AI reviewer. The fix is deterministic checks that cannot be argued with.

In my last post I described a moment that defines this era. An agent writes 500 lines of code in a few seconds, and nobody in the room fully understands the logic. The first instinct is to blame the speed. I no longer think that is the real issue. The real issue is what the agent was copying from.

An AI agent does not write code in a vacuum. It reads the code already in your repository and it imitates it. It looks at your folders, your names, and your patterns, and then it produces more of the same. This is the part most teams miss. The repository is the real model. The base model is fixed, but the code around the cursor is what tells the agent how "good" should look for the next 500 lines.

The Base of the Pyramid

Think of a project as a pyramid. The base is the architecture, the boundaries between parts, and the main building blocks. Everything else sits on top. When the base is clean, the agent has good examples to copy, so it writes code that fits. When the base is messy, the agent copies the mess, faster than any junior developer ever could.

This is the old idea of garbage in, garbage out, but the copier now runs at machine speed. One weak pattern is no longer repeated three times by three people over three months. It is repeated thirty times in one afternoon.

The hard part comes next. If a project starts from a weak base, it becomes very hard to fix later. You are not fighting one bad choice. You are fighting every copy the agent made of it. So the lesson is not "write a better prompt." Your foundation is now a multiplier, in both directions.

The Circular Validation Trap

So how do you stop the copying from spreading? The easy answer, and the one I see everywhere, is to ask another AI. You generate the code with one model, and then you ask a model to judge if the code is clean.

This is a trap, and it helps to say it plainly. Asking an AI to judge the work of an AI is a mirror looking at a mirror. The reviewer has the same blind spots and the same habits, and it likes code that looks correct. If the reviewer knows the original request, it also wants to confirm the request was met. You are not adding a check. You are adding a second opinion from the same mind.

An opinion cannot be a quality gate. A gate has to give the same answer every time, no matter how the code was written or who is asking. A language model does not give you that. Deterministic software has given us that for years.

Determinism as the Referee

The way out is to hand the rules to plain, deterministic tools. Linters, type checkers, and static analyzers. These tools do not have feelings. They return pass or fail, and they return the same answer every time. Put them in the CI pipeline and they become gates that nothing can skip. The agent can write whatever it wants, but nothing merges unless it passes.

In a modern Python project that means Ruff, which does linting and formatting in one very fast pass, plus Mypy or Pyright to check types so the agent cannot quietly break an interface. In TypeScript and JavaScript, Biome is now the baseline. It replaces the old pair of ESLint and Prettier with one tool that both formats and lints. These are not style choices. They decide if the code is allowed to exist.

Executable Architecture Contracts

Linting keeps single files honest. It does nothing for architecture, and architecture is the first thing an agent breaks. An agent that does not know your layering rules will happily import the database layer straight into a controller, because to the agent that looks like the shortest path to working code.

The answer is to turn your architecture into something the build can run. In Python, Tach and import-linter let you declare which layers may import which, and they fail the build the moment a forbidden import appears. In TypeScript, dependency-cruiser reads the whole dependency graph and catches circular dependencies and orphan files, while eslint-plugin-boundaries gives you the same warning inside the editor. Your architecture stops being a diagram on a wiki that everyone ignores. It becomes a contract the build enforces.

Numbers the Agent Cannot Argue With

Agents have one clear weakness. They write long, deeply nested, over-parameterized code that works but is painful to maintain. You fight this with fixed numbers, checked by machine, with no room to argue.

Good defaults when you work with an agent look like this. Keep files under 300 lines and functions under 40 lines. Keep cyclomatic complexity at 8 or below and cognitive complexity, in the SonarSource sense, at 12 or below. Keep nesting at 3 levels or less, and keep function parameters at 4 or fewer. A long list of parameters is usually a sign that an abstraction is missing.

One last number matters the most. Because writing code is now almost free, an agent will happily rebuild a function that already lives two folders away, simply because it did not remember it was there. The tool jscpd measures that duplication directly and fails the build when it goes above three percent.

The Gate Is the Contract

None of this comes from distrust of the agent. It comes from being honest about what an agent is. It is a fast and steady copier of whatever you place in front of it. Give it a clean base and a wall of deterministic gates, and it becomes a real multiplier. Give it a weak base and a second AI reviewer, and you have built a machine that produces technical debt at scale.

My view is simple. In the age of agents, the code you write matters less than the gates you set. Prompts change, opinions change, and even the model can be swapped. The gate is the one thing in the pipeline that cannot be flattered, rushed, or talked out of its answer. That is why it is the part you can trust.

In the next post I will look at the other half. Gates decide what is allowed out, but the quality of what goes in depends on context. That is where Obsidian, small and focused context windows, and a reviewer that knows nothing about your intent come in.