AI still generates bad code. Bad system design, redundant logic, repeated components, unnecessary re-renders. That hasn’t changed. What changed, from what I’ve seen, is that people stopped looking.
Let me walk through how we got here.
The Autocomplete Era
This was probably the best phase of AI-assisted development. You knew exactly what you wanted to write and where. The AI just typed it for you. If the suggestion was off, you saw it immediately and corrected it. You were still in the driver’s seat. The AI was a fast typist, nothing more.
The code that came out of this era was mostly yours. You understood it because you were making every decision. The AI was filling in the blanks you already knew the answers to.
Agents Enter the Picture
Then Cursor and Claude Code showed up, and the dynamic shifted. Now the agent wasn’t just finishing your sentence. It was writing entire features.
In the beginning, developers were still reviewing the output. The agent would do most of the work, but you’d scrutinize the result, find the issues, and fix them. And while you were in there fixing things, you’d notice other problems. A structural issue here. A questionable design decision there. You’d fix those too. The process of reviewing AI output was accidentally making developers engage deeply with the codebase. It wasn’t perfect, but the feedback loop still existed.
Where We Are Now
On some of the teams and projects I’ve been close to, the pattern looks like this: managers want fast results. Developers use agents to move fast. The feature works. The manager is happy. So the developer asks themselves: why would I review the code?
They don’t. They move on to the next task.
This isn’t because AI agents suddenly got better at writing clean, optimized code. They didn’t. What changed is two things: far fewer people are looking, and the code actually runs now. Early AI-generated code was broken in obvious ways. It had bugs you’d hit immediately, and that forced you back into the code to read it, understand it, and fix it. The broken output was accidentally a quality check. That’s no longer true. The code agents produce today mostly works. The feature does what it’s supposed to do. The tests pass. The manager clicks through the UI and sees no errors. So there’s no moment that forces the developer to look closely. When the output was broken, review was mandatory. Now that it runs, review feels optional. And under deadline pressure, optional things don’t happen.
In codebases I’ve worked on, this is exactly what slips through: the page re-rendering three times when it should render once، the three separate loops over the same dataset that could’ve been one, the component copy-pasted into five different files because the agent didn’t know it already existed.
The product works. Ship it.
The problems are still there. They’re just hiding under working software.
I don’t think this is entirely the developer’s fault. If you have ten tasks before a deadline and you stop to reason through every trade-off in every file the agent touched, you will not finish. The pressure is real. The incentive to just move is real.
The Trade-off Nobody Names
Here’s the honest version of what’s happening: many teams are trading codebase health for delivery speed, without acknowledging that it’s a trade-off at all.
That’s the part that bothers me. The trade-off itself isn’t wrong. Sometimes you need to ship fast and clean up later. That’s a legitimate call. But when you don’t recognize it as a trade-off, you don’t plan for the cleanup. The technical debt doesn’t get scheduled. The codebase quietly rots. And then one day the cost of adding a new feature is three times what it should be, and nobody can explain why.
You either keep the codebase clean and take longer to build, or you build fast and inherit a codebase you’ll eventually want to throw away and rewrite from scratch. Both paths are real. Both have costs.
The mistake is pretending the second path has no cost just because the demo looks fine.
The Rule I Work By Now
I can’t fix the incentives of a whole industry, so I settled on a rule for my own work instead: I don’t ship agent-written code I can’t explain. Not a line-by-line audit of every file, but enough reading that I could tell a reviewer why each piece exists. If I can’t, I either read until I can, or I delete it and let the agent try again under my direction.
And when a deadline genuinely forces me to skip that, I treat it as borrowing, not earning. The unreviewed code gets written down as debt, with a task and a date, instead of left as a silent hope. Making the trade-off is fine. Making it invisible is the part I refuse to do.





