I’ve rescued a lot of tangled codebases. For years the tangles were human-made. Now most of them are AI-made: “vibe coded,” generated by feel, shipped because the demo worked. And that code fails in predictable ways. The same five failure modes show up in almost every rescue.
If you can spot them early, you can fix them while they’re cheap.
1. No architecture, just accretion
AI writes the code in front of it. It doesn’t hold the whole system in its head, so it doesn’t draw boundaries — it accretes. Everything ends up able to touch everything, which means every change ripples somewhere you didn’t expect.
Tell: you can’t describe the system in three boxes and some arrows. If “where does this logic live?” has no clean answer, there’s no architecture — just sediment.
2. Duplicated logic, slightly different each time
Ask an AI to add a feature five times and you’ll often get the same rule implemented five times, each subtly different. Now a bug fix has to be applied in five places, and you’ll miss at least one.
Tell: grep for a domain term. If the same calculation or validation appears in multiple files with small variations, you’ve found it.
3. Tests that test nothing (or aren’t there)
Generated tests love to assert that 2 === 2. They mock the thing under test. They pass, and they protect nothing. So every deploy is a held breath.
Tell: delete a line of real logic and run the suite. If it still passes, your tests are decoration.
4. Sprawl
Single files thousands of lines long. Functions that do nine things. Components that fetch, transform, render, and handle errors all in one breath. It works, until you need to change one of those nine things.
Tell: open the biggest file in the repo. If it scrolls past what any human would hold in their head, that’s where the next bug is hiding.
5. Mixed-stack chaos
Python here, Node there, two frameworks, three state libraries — none of it chosen on purpose. Each piece was the path of least resistance in some prompt, and now they have to live together.
Tell: count the languages and frameworks. Then ask: did anyone decide on each one? If the answer is “it just ended up that way,” you have integration cost no one budgeted for.
The point isn’t “AI bad”
It isn’t. The same AI that made this mess is genuinely useful — I use it every day. The problem isn’t the tool; it’s shipping its output without the architecture that makes it survive contact with reality.
Vibe coding gets you to a working demo fast. A vibe architect keeps it working after the demo. If you’ve recognized two or three of these in your own codebase, you’re not in trouble yet — you’re early. That’s the cheapest time to fix it.
That’s the work we do. If your codebase is showing these signs, book an architecture call and we’ll talk about where to start.