AI Writes the Code. You Own the Debt.
The uncomfortable economics of AI-generated code in enterprise systems, from someone who reviews it every week.
There is a kind of pull request I see every week now. The diff is small, the names are reasonable, the tests pass. And buried inside it, a utility that has existed in the codebase for years has been rewritten from scratch, slightly differently, with slightly different edge cases.
Nobody did this on purpose. A developer asked an assistant, the assistant produced something locally correct, and nothing in the diff gave the reviewer a reason to look further. Approved, merged, and the debt lands quietly.
I lead engineering teams on national-scale public systems, the kind where a tax calculation or an identity check has to keep being right for years, and I review AI-assisted code in enterprise Java codebases every week. My position after doing this for a while is not that AI writes bad code. It is more uncomfortable than that. AI writes cheap code, and we are booking the cost in the wrong quarter.
Writing got cheap. Understanding didn’t.
For most of my career, the scarce resource in a delivery team was the time it took to produce working code. Estimation, staffing, sprint math: all of it was built around that constraint.
That constraint is gone. An engineer with a good assistant can produce in an afternoon what used to take a week. The typing bottleneck is dead, and I do not miss it.
But the cost of a line of code was never mostly in typing it. It was in understanding it, reviewing it, debugging it at 2 a.m., migrating it, explaining it to the next person. None of that got cheaper. On a system that will live for a decade, the write-time cost was always a rounding error against the own-time cost.
So the economics did not improve as much as the demo suggests. They shifted. We now produce the cheap part faster, which means we accumulate the expensive part faster too.
AI did not remove the cost of code. It deferred it, and the interest compounds in exactly the places diffs don’t show.
The debt is invisible at the diff level
Here is what makes this generation of technical debt different from the debt we already knew how to fight: it passes review.
Classic debt announces itself. A method nobody wants to touch, a TODO older than half the team. You can see it in the diff and argue about it in review.
AI-generated debt is polite. Each change is locally coherent, idiomatically written, often better formatted than the code around it. What it does wrong happens between the diffs.
It duplicates logic, because the model does not know your shared validation module exists. It re-implements utilities that have been hardened for years, minus the fixes those years produced. And it flattens architectural boundaries in small, plausible steps: a fresh adapter where the existing one should have been extended, a port bypassed because calling the repository directly made the generated code simpler.
In a hexagonal architecture, those boundaries are the design. Each individual bypass looks like pragmatism. Enough of them and you have mud.
No single reviewer sees the pattern, because no single diff contains it. That is the trap. The unit of review is the diff, and the debt lives at the level of the system.
The unit of quality is the system
In a codebase with a long life and many hands, consistency with existing patterns matters more than local elegance. I will take a slightly clumsy solution that follows the established shape over a beautiful one that invents a new shape, every time. The clumsy one can be understood by anyone who knows the codebase; the beautiful one adds a dialect.
Get Stavros Theodorou’s stories in your inbox
Join Medium for free to get updates from this writer.
This is exactly where AI is weakest by default. The model has no memory of your conventions, your naming, your layering rules, your hard-won decisions about where a transaction begins. Unless you force that context into every interaction, it optimizes for the only thing it can see, which is the local problem. It will happily produce yet another slightly different pagination approach, and every version will be defensible on its own.
Enterprise code review was always partly about protecting the system from well-intentioned local optimization. AI just industrialized the supply of well-intentioned local optimization.
What actually holds the line
Not policy documents, and not hoping developers prompt better. Specific, enforceable things.
First, AI output gets reviewed as if a junior engineer wrote it. Same bar, same questions. “The tool wrote it” is not a review category, and it is definitely not an excuse. If a human teammate had submitted the change, you would ask why they didn’t use the existing mapper. Ask it now. The moment a team starts waving AI diffs through because they look clean, the debt curve goes vertical.
Second, architectural boundaries move out of tribal knowledge and into executable checks. A convention that lives in a senior engineer’s head is invisible to a model, and increasingly invisible to a team shipping at AI speed. A convention that lives in the build fails loudly. In a Java stack, that means dependency rules the CI actually runs:
@ArchTest
static final ArchRule domain_stays_pure =
noClasses().that().resideInAPackage("..domain..")
.should().dependOnClassesThat()
.resideInAPackage("..adapter..");This test does not care whether a human or a model wrote the violation. That is precisely why it works. Lint rules and dependency tests are how a codebase remembers its own architecture when the hands writing into it, human or not, do not.
Third, some decisions stay human. API design, domain boundaries, security decisions. A model can produce plausible output for all of them. The reason they stay human is that these are the decisions with the longest ownership tail. An awkward internal method costs a refactor. An awkward public API costs every consumer, for as long as the API lives. On a national platform, an access-control decision is a liability with your organization’s name on it. In those places, plausible is not the standard.
The maintenance bill has a fixed address
Here is the part that makes delivery managers uncomfortable. If your measure of productivity is code shipped, AI looks like a miracle, because it maximizes exactly that number. Every dashboard goes green.
But nobody ships code as an end in itself. You ship systems, and systems have to be operated, debugged, extended, audited, handed over to people who were not there when the code was written. All of that scales with how much code exists and how consistent it is, not with how fast it was produced.
Teams sprinting on AI-generated volume today are running an experiment on their future selves. The results arrive on maintenance day, when someone has to fix a bug that lives in one of several near-identical implementations of the same rule, or trace a data path through an adapter nobody remembers approving.
And when that day comes, the vendor will not be in the room. The model that wrote the code carries no pager, attends no incident review, answers to no auditor. Ownership does not transfer to the tool. It never left you.
None of this is an argument against AI-assisted development. I use it daily, my teams use it daily, and the write-time gains are real. It is an argument against pretending the deferred cost is zero. Cheap to write was never the same as cheap to own, and the gap between those two is now the most important number in software economics that no dashboard shows.
Takeaways
- AI collapsed the cost of writing code. The cost of understanding and owning it did not move, so the bottleneck relocated to review and maintenance.
- AI-generated debt passes review because every diff looks fine in isolation. It accumulates between the diffs: duplicated logic, re-implemented utilities, quietly flattened boundaries.
- In enterprise systems, the unit of quality is the system, not the diff. Consistency with existing patterns beats local elegance, and a model will not know your patterns unless you force them into its context.
- Review AI output at the same bar as a junior engineer’s PR, and turn architectural conventions into executable checks the build enforces. Tribal knowledge does not survive AI-speed shipping.
- Keep API design, domain boundaries, and security decisions human. They carry the longest ownership tail, and ownership never transfers to the tool.
I’m Stavros Theodorou, a Senior Software Engineer & Architect. I design and run national-scale, public-sector systems. I write about enterprise architecture, clean design in production, and the senior-to-architect shift. If that’s your world, follow me here and connect on LinkedIn.










