Lately I've been talking quite a bit with people who aren't developers. They don't touch Drupal, and they don't need to. They're building their projects with other technologies through vibe coding, asking AI for whatever they need and seeing what comes out. And more often than I expected, the conversation ends up in the same place.
"I asked it to build one thing and it did. But along the way it touched something I hadn't asked for, and a third thing just fell apart. Now it doesn't work the way it used to, it's slower and there are errors everywhere."
That's almost always followed by the line that worries me the most: "it never ends, every time I fix one thing something else breaks".
At the beginning, mind you, everything was fine. They'd ask for a feature, the AI would build it, they'd give it a quick test and move on. There's no specific day when the project breaks, and no single change to blame. It piles up, and that's exactly why it's so hard to see coming.
The frog doesn't notice the water
You probably know the story (it's more fable than biology, but it works). Drop a frog into boiling water and it jumps out. Put it in lukewarm water and turn up the heat little by little, and it doesn't notice until it can't get out anymore.
Websites work the same way. No project starts out as a disaster. At the beginning there's a reasonably clear idea, a structure that makes sense and code that fits what the client asked for. But the client keeps asking for things. A new field here, an exception for a specific role there, an integration with a CRM that wasn't in the original plan.
Each of those requests, on its own, is one more degree. An if inside a hook that already had three others. A service that starts doing things it shouldn't. A function copied and pasted with a small tweak because refactoring the original would have taken longer. None of it sets off any alarms, because the site still works.
Code has to evolve at the same pace as the project. If the needs have changed and the architecture is still the one from day one, every new feature gets squeezed in. Until one day you realize that touching anything is scary.
Broken windows and clothes on the floor
The other metaphor I like is broken windows. If you have a spotless building and one window breaks, you fix it as soon as you can, because it sticks out next to everything else. But if half the windows in the building are already broken, one more doesn't draw any attention. You don't even see it as a new problem.
A bedroom works pretty much the same way. If it's tidy, you try to keep it that way. If the floor is already covered in clothes, the next T-shirt goes on the floor too. It barely makes a difference anyway.
Code is no different. When you have a project with a technical debt list that doesn't fit on one screen, known bugs that have been sitting there for months and warnings nobody reads, the next bad piece of code doesn't shock you. You add it to the list (if you're lucky) and move on. That list never gets done, because there's always something more urgent.
I see this a lot in projects I inherit. It's almost never that the previous team was bad. It's that nobody fixed the first window, and from there everything tends to get worse.
AI is the fan
With AI, all of this happens faster.
When an agent works on your project, it looks at what's already there and tries to follow the same pattern. That's what it's supposed to do, respect the conventions of the existing code. The problem is it can't tell a convention from a bad habit. If your code is clean, it'll generate clean code. If it's full of crap, AI is the fan, and it'll spray it all over the place.
In Drupal you see it straight away. If your custom module has a two-thousand-line .module file with business logic stuffed into hooks, \Drupal::service() calls all over the place and raw SQL queries, the AI will add the next feature exactly like that. If instead you have services with dependency injection, clear interfaces and hooks acting as simple glue, it'll usually follow that path.
And we're not talking about a bit of bad code here and there. AI generates a lot of code, very fast, so it multiplies whatever was already there. What used to take years to rot now rots in months. Often without anyone checking whether what it adds affects security, performance or how maintainable it'll be six months from now.
Better on top of something that already works
There's something I've heard several people talk about that fits right in here. Instead of asking AI to "build me a program that does X", they take a mature, well-maintained open source project that does something similar and ask the AI to modify it to fit what they need. And according to them, the results are quite a bit better.
It makes sense. When AI builds everything from scratch, the beginning usually goes well, but as soon as the project grows a little it gets hard to maintain and bugs start showing up everywhere. If you start from a solid base, designed by people who knew what they were doing and with years of fixes behind it, whatever the AI builds on top tends to come out fairly solid too. Same thing as before: AI copies what it sees.
What the AI doesn't know matters too. When it changes something, it doesn't have the full picture of what that affects or why it was done one way and not another. In a mature project, a lot of those decisions have already been made, and made well, so there's less room to mess things up. In a sandcastle that gets built as you go, any change can knock down a part nobody knew depended on another.
Those of us who work with Drupal live this every day without thinking much about it. You don't start from scratch, you start on top of a core and a set of modules that have been battle-tested for years. That base is what lets you work with AI in a fairly stable way and add features without breaking other parts of the project. As long as what you build on top doesn't ruin it, of course.
Patches on top of patches
With people doing vibe coding, it usually goes the same way. Something breaks, you tell the AI, it rewrites a chunk and gives you a patch. It works. A while later something else breaks, another patch. And so on until you have a house of cards held together with duct tape, where the slightest bump brings everything down.
This is where a concept I think is key comes in: ease of change. It's how easy (or hard) it is to change something in your project without breaking anything else. A project with good ease of change has every responsibility in its place, doesn't repeat the same logic in five places and, when you modify something, you know what else might be affected. A project without it is exactly what those people describe to me: you ask for one thing and two others break.
Duplicated code is probably what kills it the most. And AI duplicates a lot, because writing a new, similar function is easier for it than understanding the existing one and extending it. You fix a bug in one copy, the other four are still broken, and you don't even know they exist.
In these cases, another patch doesn't fix anything. What you need is to stop, look at the project with some perspective and refactor a good part of it so it makes sense for what it is today, not for what it was the day it started.
I know what some of you are going to say: "just ask the AI to do the refactoring". You can, sure. But we're back where we started. If you don't know what architecture the project needs, and you can't review whether the refactoring it proposes makes sense, you've swapped a bunch of small patches for one huge patch. Someone has to be able to tell when what the AI did is right, and the AI can't do that on its own.
"I'll just do it myself with AI"
There's a conversation that's becoming more and more common. A client asks you for a quote for a feature, you send it over and the answer is something like: "ugh, that's really expensive, for that price I'll just do it myself with AI".
It's a fair response, but at that point I like to ask a question. Imagine we both use AI. Four hours each, same feature. When we're done, which version do you think will be better implemented, with fewer bugs and more polished? The one from someone who's been programming for years, knows what to ask the tool and can see whether what came out is right or wrong? Or the one from someone who isn't really sure what they're looking at and is crossing their fingers?
And that's assuming we take the same amount of time, which usually isn't the case. I already have a workflow set up, I know where AI tends to screw up and I know what I need to check. Someone starting from zero will take quite a bit longer to get to something similar, and probably won't know whether they've got there.
A lot of the time the client sees it as soon as you put it that way. If it's an important feature, one of those that has to work, the math is simple: if you're paying for AI either way, it's worth paying someone who'll take less time and deliver something better built. And people who already knew how to program are now much faster with AI than before, so instead of shrinking, the gap has actually grown.
Mind you, I'm not saying nobody without experience should use AI to code. For a prototype, an internal tool or something where nothing happens if it breaks, go for it. The problem starts when that prototype ends up being the website a business depends on.
Signing your code
Not long ago, talking about all this, the idea of signing your code came up, and I really liked it.
It doesn't matter whether you wrote it by hand, an agent generated it or it's a mix of both. When you merge something that's going to production, you're putting your signature on it. You're saying it meets your quality bar and that you stand behind it. The tool you used to get there is the least important part.
And that changes how you work with AI. If you're going to sign something, you review it. You don't accept an eight-hundred-line diff because the tests are green (I've already written about why green tests aren't enough, in Spanish). If you don't understand what it did, you don't sign it. If it duplicated logic, you ask it to consolidate it before moving on.
Someone vibe coding without knowing how to program can't sign their code, because they don't know what they're signing. I don't mean that as a criticism, they just don't have the means to evaluate it.
What I try to do
I don't have any trick, but I do have a few habits.
I try to fix windows as soon as they break. The cleaner a project is, the easier it is to keep it clean, and the more likely it is that what the AI generates will be decent. That's why spending time on keeping a healthy codebase doesn't feel like a luxury to me. It's what makes working with AI pay off.
I also don't trust myself to remember to check. In Drupal that means PHPCS with the Drupal coding standards, PHPStan at a reasonable level, tests and something that gives me a snapshot of the overall state of the site. I use Audit as a closed loop, so agents can't mark a task as done if it makes the metrics worse. It doesn't replace reviewing, but it cuts out a lot of noise.
I put refactoring in the quote or in the planning, with its own time allocated. If the project has grown in a direction nobody expected, the architecture has to keep up, and if I wait until there's a gap, the gap never comes.
And everything the AI generates I review as if a junior in a big hurry had written it. Because in practice, that's more or less what it is.