Vibe coding vs engineering: where is the line?
A learner I mentor demoed a feature last month that impressed me: a working search-as-you-type screen, debounced, with a tidy empty state. Then I asked the question I always ask: “Walk me through what happens when the network is slow.” Silence. Not shy silence. Blank silence. They had not written that code so much as negotiated it into existence, prompt by prompt, accepting each diff without reading it. It ran. It looked right. And they could not tell me what it did.
That is vibe coding, and my honest first reaction was split down the middle. Part of me wanted to celebrate: a beginner shipped something real in an afternoon that would have taken me a week when I started. Part of me wanted to worry: they had a feature and no mental model of it, which means they have a liability they cannot see. This article is my attempt to resolve that split into something I can teach: a line between vibing and engineering that holds up when the stakes are real.
Vibe coding is a real thing, and it works
The term comes from Andrej Karpathy, who described it in a widely-shared tweet in February 2025: you “fully give in to the vibes,” you talk to the model, you accept the diffs without reading them, and when something breaks you paste the error back and accept whatever it suggests. His framing was playful and honest: he was describing throwaway weekend projects, not a banking core. The key trait is not “using AI.” Plenty of disciplined engineers use AI all day. The defining trait is that you stop reading the code. The model is the author and you are the user.
And here is the part the craftsmanship crowd often refuses to say out loud: it works. For a prototype, vibing is effective. I have built a throwaway script to reshape a messy CSV, a quick SwiftUI screen to check a layout idea, a one-meeting chart, all by vibe, all faster than I could have done by hand, none of it regretted. When the goal is to find out whether an idea is worth building, the speed is the point, and reading every line would be waste.
So the question is never “AI or not.” It is: for this particular task, how much do I need to understand what I’m shipping? That turns out to be a spectrum, not a switch.
Five levels, defined by your relationship with the code
“Vibe coding versus engineering” sounds binary. In practice there are degrees, and the thing that changes between them is not how the code was written. It is how deeply you are connected to it. I find it useful to name five levels, each one a deeper question you can answer about the code in front of you.
- Level 0, Pure vibes. You describe, you accept, you never read the diff. Your only test is “it runs.” Fine for a spike; a time bomb for anything else.
- Level 1, You read it. You skim the generated diff and roughly follow what it does. You would catch something obviously wrong, but not something subtly wrong.
- Level 2, You can debug it. When it breaks at 2am, you can open the file and find the actual line, because you understand the control flow well enough to reason about it.
- Level 3, You could rewrite it. Take the AI away and you could reproduce this by hand. The model saved you typing, not thinking.
- Level 4, You own the design. You chose the architecture, the boundaries, the data flow. The AI filled in the implementation inside a shape you decided. This is engineering with a faster keyboard.
Notice that Levels 3 and 4 still use AI heavily; they are not a rejection of it. The difference is that understanding lives in your head, not only in the chat history. Robert C. Martin’s line in Clean Code is the quiet justification for caring about this at all: he estimates the ratio of time spent reading code versus writing it is well over ten to one. If that is true, then code you cannot read is not a shortcut. It is a debt you have to repay every time you come back to it.
What level a task deserves: a risk matrix
Knowing the spectrum does not tell you where to be. A Level-0 spike and a Level-4 payment flow are both correct, for different tasks. The two variables that decide the right level are stakes (what happens if this is wrong?) and lifespan (how long will this code live and who maintains it?). Plot them and the advice falls out of the grid.
Best for vibing: the bottom-left corner, low stakes, short life. Prototypes, exploratory spikes, a chart you need for one meeting, a script you will delete on Friday. Nothing breaks if it is wrong, and nobody inherits it. Vibe with a clear conscience; reviewing it line by line would be the actual mistake.
Watch out: the two off-diagonal corners, because that is where good intentions go wrong. Low-stakes-but-long-lived code (a personal tool, an internal dashboard) is mostly fine to vibe, but future-you has to reopen it, so keep it legible. The dangerous corner is high-stakes-but-short-lived: a quick one-off migration script feels throwaway, so people vibe it and run it straight against production. Its life is measured in seconds and its blast radius is your entire users table. Short life is not the same as low stakes.
The top-right corner (high stakes, long life) is the one place the spectrum has a hard floor. Authentication, payments, anything touching user data or money, anything in a core flow that ten other features depend on. Here AI can still draft, but you cannot ship below Level 4. You have to own it.
Vibe vs engineer, side by side
| Dimension | Pure vibe (L0-1) | In between (L2) | Engineered (L3-4) |
|---|---|---|---|
| You read the code | No / skim | Yes, to follow it | Yes, you own it |
| Can debug it | Only by re-prompting | Yes, find the line | Yes, fluently |
| Could rewrite it without AI | No | Slowly | Yes |
| Tests | ”It ran once” | A few happy paths | Edge cases + failure modes |
| Speed to first result | Minutes | Fast | Slower, deliberate |
| Right for | Spikes, throwaways, demos | Internal tools, side projects | Production, money, user data |
| What it costs you later | Nothing (you delete it) | Some re-reading | Maintainable on purpose |
The dimension that quietly decides everything is the last row. Martin Fowler’s framing of technical debt is the right lens: shipping code you do not understand is borrowing against your future velocity, and the interest is paid every time you have to change it. For a prototype you will throw away, that loan never comes due, so vibe freely. For a production system, the interest compounds, and “the AI wrote it” is not a payment plan. Simon Willison, one of the more balanced voices on AI-assisted programming, makes a distinction worth internalising: there is real, disciplined work in using these tools well, and there is “vibe coding,” and the difference is whether you remain responsible for the output.
AI can write your code. It can’t own your understanding.
Back to my learner with the search screen. The danger of vibing for a beginner is different from the danger for me, and worse. When I vibe a script, I am skipping work I already know how to do; my mental model exists, I am choosing not to spend it. When a beginner vibes a feature, the mental model never forms in the first place. They ship the artifact and skip the learning that was the point of the exercise. They are not standing on the shoulders of the tool. They are hanging from it, and they will fall the moment it lets go, which it does the instant the bug is one the model cannot one-shot.
This is the part that does not fit on a slide: the struggle was never the obstacle to learning. The struggle was the learning. A debugger session where you finally understand why the request fired twice teaches you something a clean generated diff never will. Take that away and you get someone who can produce code and cannot reason about it, which, in a teammate, is more dangerous than someone slower who understands their work, because the confident-but-hollow version passes review and reaches production.
So as a mentor I have stopped grading the demo. A working screen tells me almost nothing now. I grade the walkthrough: show me the failure path, change a requirement live, explain why it is structured this way. That is the only signal that survived the arrival of tools that make the artifact free.
You can vibe what you could verify
After all the nuance, I want one rule that fits in a sentence and survives contact with a deadline. Here it is: you can vibe what you could verify. If you are capable of reviewing the output, reading it, judging whether it is correct, catching the subtle bug, then accepting it without reading this time is a time-saving choice you are qualified to make. But if you couldn’t have caught the mistake, then you are not saving time, you are gambling, and “the AI wrote it” will not be a defense when it breaks.
This reframes the whole thing as a question about you, not about the AI. The same generated diff might be safe for me to vibe and reckless for my mentee to vibe, because verification capacity differs. It is also why the rule scales with stakes automatically: for a throwaway script, “verify” means “glance at it,” and almost anyone clears that bar. For a payments change, “verify” means “understand it well enough to defend it in a post-incident review,” and far fewer do. The bar rises exactly where the risk does.
Run a task through these gates before you accept code unread:
Should I vibe this?
The last gate is the universal one. If you couldn’t have reviewed it, you didn’t save time. You took a risk you couldn’t price.
Vibe coding is not the enemy of engineering, and pretending it is only makes you slower at the prototypes where it helps. They are two settings on one dial, and the skill that matters now, the one I am trying to teach and still practising myself, is reading the task accurately enough to know which setting it deserves. Be generous with the vibes where nothing is at stake. Be uncompromising about understanding where something is. And never confuse a thing that runs with a thing you can stand behind.
Cited sources