Working with AI agents day to day, I’ve started noticing a trend that worries me. Teams — including experienced engineers who used to know better — have quietly stopped pushing to rewrite or restructure the gnarliest parts of their systems. It’s not about the quality of the code the agents write. It’s about a decision that used to happen almost reflexively and now rarely does: the decision to stop and say this has become unmanageable, we need to refactor it before we go any further.
Human context is small, and that shaped how we build software
A computer can hold far more in “working memory” than a human can. We can’t reason about a complex system when it branches in dozens of directions, each branch with its own implications, all interconnected. It’s simply too much to keep in our heads at once.
So historically we did the only thing we could: we split systems into modules small enough to understand in isolation, and then we spent effort connecting those modules together with interfaces we could also understand. Modularity, encapsulation, layering — these aren’t aesthetic preferences. They’re concessions to the size of human working memory. We break the system down until each piece fits in one person’s head, because that’s the only way a person can reason about it, change it safely, and review someone else’s change.
The refactoring reflex
Systems rarely start out confusing. A piece of code is written when the requirements are still simple, and at first it reads cleanly. Then the requirements change. A developer adds a branch for a new case, then another for an exception, then a special case on top of that exception. Over enough iterations the original “rule” the code expressed is buried under exceptions — sometimes the requirements have shifted so far that the code is now nothing but exceptions, with no clear rule left at all.
There’s a moment every experienced developer recognizes: you’re debugging an issue, you follow the code, and you get lost. The branches no longer form a picture you can hold in your head. Historically, that feeling was a signal. A senior engineer, upon getting lost in a piece of the system, would pause and say: this has become unmanageable — before I add anything else, I need to rewrite or refactor this so it’s understandable again. Not for elegance, but so that they and everyone after them could reason about it and safely review future changes.
That reflex — “I’m lost, therefore it’s time to refactor” — has quietly been one of the most important forces keeping long-lived systems maintainable. And it was triggered by a human limitation: the moment a person could no longer follow the code.
To be fair, that reflex was already under pressure long before AI agents. Deadlines, roadmaps, and managers asking “why are you rewriting something that works?” have always fought against it, and refactoring was usually the first thing to get deprioritized. Senior engineers had already, over the years, stopped pushing back as hard as they once did when a system got unmanageable. Agents didn’t create that weakness. What they did was remove the last internal trigger that used to fire in spite of it — the visceral experience of a human getting lost.
AI agents don’t get lost
Here’s the problem. AI agents are not bound by human context limits in the same way. An agent can read the tangled function, trace every caller, and make sense of the mess that would have stopped a human cold. It can add the next branch correctly, and the one after that, working confidently inside code that no human on the team fully understands anymore.
That sounds like a strength, and in the short term it is. But notice what’s missing: the agent never gets lost, so the signal never fires. The agent has no reflex that says “this has become unmanageable, we should stop and refactor.” It just keeps adding branches to the pile. Unless it’s specifically instructed — through its harness, its prompt, or explicit review criteria — to step back and question the structure, it will happily maintain a mess indefinitely, because the mess isn’t a problem for the agent.
The real risk: humans stop being able to police the code
The failure mode isn’t that the agent writes bad code. It’s that the natural checkpoint disappears, and the humans stop noticing the code has drifted beyond their understanding. Over time you arrive at a system where:
- No developer on the team can fully reason about key parts of the code.
- Reviews become rubber stamps, because the reviewer can’t actually follow the change well enough to judge it.
- The team increasingly trusts the agent precisely because they no longer understand the code themselves — which is exactly backwards from how trust should work.
At that point you’ve lost something important: the ability to reason about your own system without an agent as an intermediary. And you lost it gradually, without any single alarming moment, because the moment that used to raise the alarm — a human getting lost — was quietly removed from the loop.
Clean code is cheaper for the agents too
It’s tempting to frame all of this as a purely principled concern — we ought to understand our own systems — and leave it there. But there’s a hard-nosed, practical reason to keep the code organized, and it’s one that survives even if you’re perfectly happy to let agents do the work.
An agent that never gets lost still pays a price for a mess. The more tangled and interconnected a piece of code is, the more context the agent has to load and hold to make a correct change: more files to read, more branches to trace, more tokens burned on every single edit. A system built from small, self-contained modules that are easy to reason about isn’t just kinder to humans — it’s cheaper to operate, because every future change costs the agent less to understand.
And it isn’t only about cost. When the relevant logic doesn’t fit into a bounded, coherent slice, agents are more likely to lose the thread and hallucinate — to assume a branch does something it doesn’t, or to miss an exception buried three levels deep. The same modularity that keeps a system inside a human’s head keeps each change inside a well-defined boundary the agent can reason about reliably. Clean boundaries reduce mistakes on both sides, for the same reason.
So keeping the source organized isn’t a favor we do for humans at the agents’ expense. It benefits humans, it improves the agents’ accuracy, and it lowers the token cost of every change we’ll ever make to that code again. The refactoring reflex we’re at risk of losing was never only about human comfort — it turns out to be good economics too.
Policing ourselves
I don’t think the answer is to hobble the agents. The answer is for us to bring back the checkpoint deliberately, since it no longer happens on its own. We have to keep asking the question the agent won’t ask:
- Do I still understand this part of the system, or have I been letting the agent understand it for me?
- If a human had to debug this without the agent, could they follow it?
- Have the requirements drifted so far that this code is now all exceptions and no rule — the classic signal that it’s time to rewrite?
- Is now the moment to pause feature work and refactor this into something a person can hold in their head again?
You can also push some of this into the harness — instruct your agents to flag when a module has grown beyond a reasonable size or branching complexity, to propose refactorings rather than only extending, and to call out when a change is getting hard to reason about. That helps. But the ultimate responsibility stays with us, because we’re the ones who need to be able to understand our systems, and we’re the ones who lose that ability if we’re not paying attention.
The convenience of an agent that never gets lost is real. But “the agent can still make sense of it” is not the same as “the system is healthy.” The first is about the agent’s capacity; the second is about ours. Keep asking whether it’s time to refactor — because the tool that used to remind you, by getting lost, doesn’t get lost anymore.