Vibe coding’s real problem isn’t the code
AI writes really good code now. That’s not a controversial thing to say anymore. If you’ve been vibe coding for any length of time, you’ve watched an AI assistant produce something clean, functional, and frankly better than what a lot of humans would write on a Tuesday afternoon. The code works. It’s readable. It does what you asked.
So if the code is good, why do so many vibe-coded projects end up in trouble?
The answer is that code quality was never really the thing to worry about. Now that AI mostly handles it, what’s left is the part of software that was always the hard part, even before AI existed: the decisions. What to build. How to build it. What to use. What to skip. What tradeoffs to accept. Code is the output of those decisions; it isn’t the source of them.
And here’s the catch: when you’re vibe coding, those decisions are getting made constantly, by the AI, without you seeing them. They’re silent. The code lands in your project looking finished, and you have no idea what the AI chose along the way to get there.
That’s the real problem. Not the code. The silent decisions behind the code.
What a silent decision actually looks like
Imagine you ask an AI coding assistant to help you build a small app. You describe what it should do, maybe show it a rough sketch, and let it run. Within an hour you have something that works on your machine. Great.
What you don’t see is that the AI made dozens of choices to get you there. It picked a framework. It picked a database. It picked an auth approach. It decided how to structure your folders, how to handle errors, what to log, what to call things, where to put configuration, how to manage state. Every one of those is a real decision with real consequences, and the AI made all of them in seconds, based on what tends to work for most apps it’s seen.
Here’s a concrete one. Without any outside intervention, AI coding assistants will almost always reach for Supabase when your app needs a database and authentication. Supabase is genuinely good. It might even be the right choice for your project. But notice what just happened: you picked a hosted service, a pricing model, a vendor relationship, a particular flavor of Postgres, an auth system with its own conventions, a specific way of doing row-level security. You picked a whole stack. Or rather, the AI did, and you inherited it.
Three months later, your schema assumes Supabase’s auth tables. Your client code uses their SDK. Your security model is built around how they document things. If you ever want to ask “what if we used something else,” you can’t really ask it anymore. The answer is “rewrite a lot of the app.” Not because anything went wrong. Just because every other decision got built on top of that first silent one.
That’s the scary version of the problem, actually: the version where nothing ever breaks. Things just quietly settle into a shape you didn’t choose.
The louder version is the one I’ve seen up close. I was talking with a vibe coder once who couldn’t get their app to deploy to production. We dug in, and the underlying cause was that the AI had made a perfectly reasonable architectural decision early on. Reasonable for most apps. Just not for this one, in this deployment environment. The decision was invisible at the time it was made, perfectly defensible in isolation, and impossible to walk back without significant rework. The vibe coder didn’t do anything wrong. They just never saw the choice get made, so they never got the chance to flag the context that would have changed it.
Why silent decisions cause real problems
A few reasons, all of which compound:
The AI doesn’t have your context. It’s making decisions based on what’s typical, what’s common, what works for most apps. Your app isn’t most apps. It has a specific deployment target, specific users, specific constraints that live in your head or in the parts of the codebase the AI didn’t bother to read. A perfectly reasonable default can be exactly wrong for your situation, and the AI has no way to know that. You do. But only if you get the chance to weigh in.
Decisions stack. One silent choice on day one is usually survivable. The problem is that everything after it gets built on top. The AI picks a library; then it builds around that library’s quirks; then it designs the data flow around that structure; then it handles errors in a way that assumes that flow. By the time you hit a wall, the original decision has fifty things sitting on top of it. You can’t undo it without disturbing all of them.
You can’t debug what you didn’t see. When something breaks and you didn’t see the decisions that led there, you’re not debugging code anymore. You’re doing archaeology. Every fix is a guess because you don’t know which assumptions are load-bearing and which ones are arbitrary. That’s an exhausting place to work from.
Your app drifts away from what you actually wanted. Every silent decision is a small interpretation of your intent. Individually they’re fine. Cumulatively, the app you end up with may not be the app you thought you were building. Not because the AI did anything wrong, but because nobody was checking that all those small interpretations stacked up to your actual goal.
You stop being the owner. This one is the deepest of the bunch. When you can’t name the decisions inside your own project, you’ve stopped being the owner of it in any meaningful sense. You’re a user of your own codebase. That’s a fragile place to be when something goes wrong, when you need to explain the app to someone else, or when you need to take it somewhere new.
Notice that none of these are about the AI being bad at coding. The AI is good at coding. The problem is that good code built on top of choices you didn’t get to make is still a project you don’t really own.
What to do instead
The fix isn’t to learn engineering before you’re allowed to use AI. That defeats the whole point of vibe coding. The fix is much smaller, and you can do it today.
There are two habits that, once you build them, change everything. The first is to make the AI surface its decisions to you, instead of letting them slip past. The second is to ask the AI for options, so that the decisions become real choices you can actually make.
Get the AI to surface decisions
Before the AI starts writing code for a new feature or a new phase of your project, ask it what decisions it’s about to make. Not “what are you going to write,” but “what are you going to decide.” Those are different questions and they get different answers.
You’ll be surprised by what comes back. The AI will tell you it’s about to choose a state management approach, or decide how to structure your API routes, or pick a way to handle file uploads, or commit to a particular pattern for error handling. Most of these you didn’t even know were on the table. That’s the point. They were always on the table; you just never saw them.
Once they’re visible, you can do something about them. You can flag the ones that matter for your project (“I need this to be self-hostable later, so let’s not pick anything that locks us into a vendor”). You can let the AI proceed with the ones that don’t matter to you. You can ask follow-up questions on the ones you don’t understand. The decisions stop being silent. That alone changes the trajectory of the project.
Worth being specific about when to do this: at the start of any meaningful new chunk of work. New feature, new module, new phase. Not before every single line of code, which would be exhausting. Before the AI commits to a direction that everything else will sit on top of.
Ask the AI for options
Surfacing a decision is the first move. But just knowing a decision is being made isn’t always enough, especially if you don’t have the technical background to evaluate it. That’s where options come in.
Once you know a decision is on the table, ask the AI to give you two or three ways to approach it. Ask what each one means, what the tradeoffs are, what each one is good for and bad for. You don’t need to become an expert. You just need enough to recognize which option fits your situation.
Here’s what I want vibe coders to understand: you don’t need to fully understand the technical decision to own it. You need to have seen it. You need to know the options were laid out and a choice was made. That awareness is what converts a silent decision into one you own, even if your understanding of it is shallow.
Imagine the Supabase scenario, but with this habit in place. Before the AI commits to a stack, it says: “For the database and auth, I’d suggest Supabase. The alternatives would be Firebase, or a hosted Postgres provider like Neon plus a separate auth library, or self-hosted Postgres if you want full control. Supabase is fastest to get started; the self-hosted route gives you more flexibility later but takes more setup. Which direction do you want?”
You don’t have to be a database expert to answer that. You just have to think about your project for a second. “I want to be able to move off any specific vendor later” gets you one answer. “I just want to ship something fast and worry about the rest later” gets you a different one. Either way, you picked. The decision is yours now. You’ll remember making it. And when the app deploys, or doesn’t, or grows, or pivots, you’ll know what’s underneath it.
The combination of these two habits, surfacing decisions and then exploring options, is what gives you back ownership of your project. Surface to make decisions visible. Explore options to make those decisions meaningful. Decide based on what you know about your project that the AI doesn’t.
Slow down to speed up
Both of these habits feel like they’re going to slow you down. They are. That’s the point.
Here’s the thing nobody tells you about vibe coding. The fastest way through a project isn’t to let the AI sprint while you watch. It’s to spend a little more time at the beginning of each chunk of work making sure the right decisions are getting made for the right reasons. Take the time on options. Take the time to think about your project’s specific constraints. Take the time to write down what you’ve decided and why.
It feels slower at first. It is slower at first. But you stop hitting walls you can’t see coming. You stop having to throw away work because of an early decision nobody was tracking. You stop debugging by archaeology. You stop watching your project drift away from what you wanted. The whole thing moves faster, and more smoothly, all the way through.
This is the trade you’re being offered, whether you realize it or not. You can let the AI move fast and make all the calls invisibly, in which case you’ll be very fast right up until you’re not. Or you can slow down by a small amount at the front of every chunk of work, and stay fast for the entire project.
The vibe coders I see succeed at this aren’t the ones who learned to code. They’re the ones who learned to stay the owner of their own project, even while letting the AI do the technical work. They make the AI show its decisions. They ask for options. They pick. They write it down. They let the AI run. And when something goes wrong, which it always eventually does, they know where to look.
The code was never the hard part. The decisions were always the hard part. AI didn’t change that. It just made it possible to ignore, and that’s the trap. Don’t take the bait. Slow down at the beginning of each chunk of work. Make the AI surface what it’s about to decide. Make it give you options. Make the choice yourself.
That’s what vibe coding looks like when it actually works.
