Why Do AI-Generated Codebases Lose Coherence Over Time?
How local perfection in AI code creates global architectural chaos

In the next 3 minutes:
Why AI excels at individual tasks but struggles with architectural consistency across projects
The specific patterns that predict codebase decay before your tests even start failing
How to enforce coherence rules that keep AI-generated code from fragmenting over time
---
The Hidden Rot in AI-Generated Codebases (And It's Not What You Think)
Here's a scenario that will feel familiar to anyone who has spent serious time building with AI assistance: the tests pass, the code reviews look clean, each individual change is defensible — and yet, six months in, the codebase feels like it was written by a committee that never once met. Four different ways to handle a failed API call. A retry wrapper that got reinvented three times under three different names. A naming scheme that silently flips somewhere around the halfway point of the repo. Nothing is broken. Everything is just noisier, stranger, harder to navigate than it should be.
The thing decaying here isn't correctness. It's coherence. And understanding that distinction is the key to building AI-assisted systems that stay livable over time.
The Mechanism: How Correct Decisions Compound Into Chaos
Every time an AI model writes code, it's working with local context — the file in front of it, a handful of related files pulled in to understand the problem, and the specific thing you just asked for. It doesn't see the shape of the whole system. So when there are five reasonable ways to handle an error, or name a helper, or place a small abstraction, it picks one that's locally correct. The next session picks a different one that's also locally correct. Neither commit is wrong. But now there are two patterns where there used to be one.
Multiply that across a few hundred small decisions and the codebase starts speaking in dialects instead of one voice.
The part that surprises most developers is what happens at session boundaries. Each new session starts fresh — not just without memory of what was built three weeks ago, but without memory of the reasoning behind it. The model reads the code and reconstructs intent from surface patterns, because surface patterns are all it has. Which means it will confidently copy a convention that was itself an accident. Someone was under pressure eight months ago, did the reasonable-under-pressure thing, and now that module is the nearest neighbor to whatever the model is writing next. It copies it faithfully. A one-off becomes house style without anyone ever deciding it should be.
This gets worse, not better, as the codebase grows — which is counterintuitive. You might assume more code means more signal about the conventions. In practice, more code means a smaller fraction of it is ever visible at once. When the repo is twenty files, whatever gets read is basically the whole thing. At two thousand files, the model is seeing a sample, and that sample is chosen by retrieval — by what the search matched, by what happened to be open. Your conventions end up decided by retrieval luck. Not by design, not by review. By which files landed in the context window.
Why Documentation Alone Won't Save You
The instinct, when you recognize this problem, is to write more documentation. The second instinct is to wait for context windows to get large enough that the model can simply see everything. Both are understandable. Neither solves it.
Documentation is advisory. A model reads it, tries to comply, and when the document disagrees with the code in front of it, the code usually wins — because the code is concrete and the document is a claim about the code. A bigger context window doesn't fix a sampling problem, it just makes the sample bigger. There is always more repository than window, and even when there isn't, more text in context is not the same as more attention on the part that actually mattered.
The more reliable fix is mechanical. A linter doesn't ask nicely. A shared base class doesn't have an opinion you can drift away from — if the only path to making an HTTP call runs through one client, there's no second pattern to invent. A failing test is not advisory. What these three things share is that they don't depend on the model remembering anything, because the convention isn't stored in the model's memory. It's in the build. When the CI pipeline reports "you introduced a second HTTP client, we already have one in lib/http," it catches the drift at the moment it happens, when it's still cheap to fix.
That said, a well-placed conventions file — a `CLAUDE.md` or `AGENTS.md` at the repo root — does real work, and it's worth being precise about what kind. One line of "we use Result types, never exceptions, because we need failures in the type signature" propagates across every session, shaping the model's first guess before it touches anything. It's a nudge, not enforcement. The reason to write it isn't that it guarantees compliance; it's that it costs one sentence and it moves the odds. Confusing nudge with enforcement is how teams end up surprised. The linter enforces. The file nudges. You need both, and you need to know which is which.
The biggest trap with a conventions file is honesty. A file that grew to four hundred lines over eighteen months — half aspirational, a quarter contradicting itself, the rest describing a codebase that no longer exists — is worse than no file at all. The model reads it, dutifully complies, and produces code that matches the document instead of the repo. Keeping it honest requires pruning, and pruning requires the willingness to say "we tried that, it didn't hold, here's what we do now" rather than letting the file become a graveyard of good intentions.
Making the Habit Stick Without Building Theater
If you want to start this week, here's the smallest useful move: open the repo and find the last three code review comments that started with "we usually…" or "in this codebase we…" Write those three sentences somewhere the model will actually read them. Not a style guide, not an architecture document — three sentences that already existed as tribal knowledge, now living where the next decision gets made. You're not inventing rules. You're refusing to type them a fourth time. For one or two of those conventions — the ones you'd actually defend in a review — take the extra step and make one of them mechanical.
The durable habit isn't a weekly coherence review. It's that when you catch yourself writing "we usually…" in a pull request comment, you finish the comment and then take thirty seconds to move that sentence into the conventions file. One extra motion inside a motion you were already making.
Watch out for the trap that looks like success from the outside: the team converts the reflex into a checklist, every PR must flag whether it introduces a new pattern, and within three weeks the checkbox is getting ticked without thought. Noticing doesn't survive being turned into paperwork. Keep it small enough that it can't become theater, because theater needs a stage, and the moment a practice grows a template and a dedicated meeting, the actors will show up and the substance will quietly leave.
Coherence Is Enforced, Not Emergent
No one gets a coherent codebase by accident, and no amount of individually correct work produces one automatically. If a convention isn't encoded somewhere mechanical, you haven't made a decision — you've expressed a preference. And a preference will get averaged away, politely, one locally correct commit at a time. Not through carelessness, but through the model doing exactly what was asked in the context it had.
The work, then, isn't writing more rules. It's noticing the decisions you're already making and giving them somewhere to live that doesn't depend on anyone's memory. And if you find yourself explaining the same architectural choice for the second time, that's a convention asking to be born. Don't let it die in the comment thread.
---
If you want to hear these ideas explored in conversation, check out the Claude Code Conversations with Claudine radio show. Available on all major podcast sites.

