Vibe Coding Will Write Your Whole Website. It Will Not Tell You What to Build.
The honest case for building software by conversation, the costs nobody puts in the demo, and the structure that separates a fast prototype from something you can hand to a user.
TL;DR
- –Vibe coding is real leverage, not a toy. It collapses the distance between having an idea and having something a user can click.
- –The speed is not the problem. The problem is that nothing in the loop asks whether the thing you just generated is correct, necessary, or safe.
- –A clean typecheck is not a passing test. A green build tells you the shapes line up. It says nothing about whether the feature does what a real person needs.
- –Generated code is locally reasonable and globally chaotic. Ask for a card twelve times and you get twelve cards, all fine, none identical. Interfaces drift without a single bad commit.
- –Slop is not a bug — it is correct and worthless. A model returns the most probable output, and the most probable output is the average one. No test catches it. Only taste does.
- –The fix is not less AI. It is writing the specification before the prompt, hand-building one vertical slice, and putting a human gate wherever being wrong costs a user something.
What vibe coding actually is
The term was coined casually and stuck. You describe what you want, an AI writes the code, you look at the result, and you describe the next thing. You are steering by feel. You are not reading every line, and often you could not have written every line yourself. The vibe is the interface.
It is genuinely new, and the reaction to it splits along a predictable line. One camp sees a toy that produces plausible garbage. The other sees the end of software engineering as a discipline. Both are wrong in the same way: they are arguing about the code, when the interesting change is to the process that surrounds it.

What it is genuinely, unarguably good at
Crossing the gap between designing a thing and having a thing
The most expensive distance in product work is between a design file and something a user can click. Vibe coding collapses it. Ideas that would previously have died as “nice, but who is going to build that” become a route you can open in a browser the same afternoon. When you can build the idea, you argue about it differently: with the thing in front of you, instead of with a picture of the thing.
Repetition, which is most of an application
A real product is not clever. It is the same vertical slice repeated: a table, a form, some validation, a save, a refresh. Clients, invoices, appointments, inventory. Once that pattern exists, describing the twelfth version of it takes minutes. This is the part of software AI is close to perfect at, because it is exactly the part that is compressible.
Learning a stack while shipping on it
Reading working code, in your own repository, solving your own problem, is a better teacher than any tutorial. Generation gives a designer or a junior developer that text to read on day one instead of month six. The learning is real, provided you actually read it.
Vibe coding removes the labour of building. It does not remove the judgment. It just makes it much easier to skip.
Where it quietly costs you
1. The verification gap
Ask most people building this way how they verify their work and the honest answer is: the build passes. But a typecheck confirms that a function returning a string is not being handed to something expecting a number. It does not confirm that an invoice totals correctly, that an email reached the right person, or that one customer cannot read another’s data.
When you write code by hand, you carry a rough model of what could break, because you suffered through building it. When you generate it, that model never forms. The code works on the happy path you demoed, and the edge cases become somebody else’s discovery.
2. Knowledge debt, which compounds like the financial kind
Every codebase accumulates landmines: a file that must be updated by hand whenever another one changes, a command that corrupts the database on this particular setup, a credential that has to be lowercase because of how a bucket was provisioned two years ago. Nothing enforces any of it.
Generated code will not warn you about these. It will route around a landmine or step directly on it, with equal confidence, because it has no memory of your project. The person who knows is the person who has read the code. If nobody has, the knowledge does not exist.

3. AI is most fluent exactly where it should be least trusted
Fluency and correctness are not the same signal, and we are wired to read one as the other. A confident, well-structured, correctly-indented answer feels true. That is fine when the subject is a card component. It is dangerous when the subject is authorisation, or money, or anything a person will act on.
The asymmetry is what matters. A wrong colour token costs you a redeploy. A wrong access check costs your users their privacy, and you find out from someone else. The generator treats both requests identically, because to the generator they are the same kind of sentence. Only you know they are not.
The rule worth adopting
Wherever the cost of being wrong lands on a user rather than on you — money, personal or medical information, another person’s data, anything irreversible — that code does not get generated and glanced at. It gets read, line by line, by someone who understands what it is guarding against.
4. Entropy, dressed as productivity
Generated code is locally reasonable and globally chaotic. Ask for a card twelve times and you will get twelve cards, all fine, none identical: slightly different radii, padding, shades of grey. Humans do this too — I have audited a production codebase carrying 134 distinct greys — but generation removes the last piece of friction that used to make somebody stop and ask whether a component already existed.
The interface slowly stops feeling like one thing. Nobody can point at the commit where that happened, because there isn’t one.

5. Slop, which is not the same thing as a bug
A bug is wrong. Slop is correct and worthless. It is the output that passes every check you have, offends nobody, and means nothing: the hero section with a gradient and the words “Elevate your workflow.” The empty state that says “No items yet.” The comment above setUser(user) that reads // set the user. The three-paragraph About page that could belong to any company on earth.
Slop is not a failure of the model. It is the model working exactly as designed. It is trained to produce the most probable next thing, and the most probable thing is, by definition, the average thing. Ask for a landing page and you get the median landing page, assembled from every landing page. It will be competent. It will look like a template because it is, statistically, a template.
Generation regresses to the mean. If you supply no taste, the mean is what you ship.

This is why vibe-coded sites are so often recognisable at a glance, and why that recognition is fatal for anything whose job is to be distinctive: a portfolio, a brand site, a product trying to feel unlike its competitors. The code was never the problem. The site works. It just says nothing, in the same voice as everything else saying nothing.
Note what this implies about the usual remedy. Asking for a redesign — cleaner type, calmer palette, fewer gradients — produces better-looking slop, because the styling was never where the emptiness lived. It lived in the headline. Restyle a sentence that means nothing and you get a handsomer sentence that means nothing.
The trap is that slop is comfortable. It never looks broken in review, so there is no moment where anyone says stop. It accumulates the way entropy does, one reasonable acceptance at a time, until the product has no point of view at all. And unlike a bug, no test will ever catch it, because there is nothing to catch — the only instrument that detects slop is a person with taste, paying attention, willing to say “this is fine, and I hate it.”
The tell
Read the text your product shows a user and ask: could a competitor paste this onto their site without changing a word? If yes, it was written by the average of the internet, and it is doing no work for you.
How to make it better
None of this is an argument for typing everything by hand. It is an argument for putting structure around a loop that has none by default. Seven things change the outcome more than any prompting technique.
Write the specification before the prompt
Keep two documents. One holds intent: what this is, who it is for, what “done” looks like. The other holds the engineering contract: the stack, the procedures, the rules that are not negotiable. Start every session from those, not from your memory of last week.
The unexpected benefit is that writing the spec is where the actual design thinking happens. The prompt is downstream of it. If you cannot describe what done looks like, the AI will invent a version of done for you, and it will be plausible and wrong.
Name what you are deliberately not building
Write an explicit out-of-scope list, with reasons. Each line is a door the AI cannot wander through, and a decision you do not relitigate at midnight. Scope creep is uniquely seductive here, because the marginal cost of one more feature feels like one more sentence.
Hand-build one vertical slice, then let AI repeat it
Build the first version of the repeating pattern yourself, slowly, understanding every layer: schema, query, validation, action, page. That slice becomes the reference the AI imitates and the map you use to debug everything that comes after it. It is the single highest-leverage hour in the whole project.
Build a verification gate that is not the typechecker
Decide, before you generate, what would prove this works. Sometimes that is a test. Sometimes it is opening the flow and driving it end to end as a real user would. Sometimes it is a throwaway script that renders the output so you can look at it.
The rule is that something outside the generation loop has to be able to say no. If the only thing that can say no is a compiler, you are shipping compiled guesses.
Put the guardrail where the harm is, and make it not generated
Safety-critical text, trust boundaries, and permission checks should be fixed, reviewed, and deliberately excluded from the parts of the system that regenerate. A disclaimer that an AI rewrites on every request is not a disclaimer. A permission check that appears because it looked like the surrounding code is not a permission check.
Then put a human in the loop by design, not as a formality: a person who reviews the output before it reaches whoever it can hurt.
Read the code you shipped, eventually
Not all of it, and not immediately. But you cannot maintain what you have never read, and the day something breaks in production is the wrong day to start. Read one slice a week until the codebase stops feeling like somebody else’s.
Supply the taste, because the model will not
The defence against slop is specificity. Not “make it look modern” but “monochrome, no gradients, 0.08em tracking on headings, motion that implies weight.” Not “write an About page” but the actual sentence you would say out loud to a stranger about what you do. A generator handed a strong constraint will execute it faithfully. A generator handed a vague one will hand you the average.
The corollary is that deleting is now a first-class design activity. Generation makes producing free, which means the ratio of what you make to what deserves to exist has collapsed. Cutting the section that is fine, the paragraph that is competent, the feature nobody asked for — that is the work. It always was. It is just that nothing else is slow enough to hide it anymore.
The reframe that helps most
Stop thinking of the AI as a developer you are managing. Think of it as an extremely fast junior who has read everything and remembers nothing about your project. It needs the spec, the pattern to copy, and a reviewer. Give it those three things and it is remarkable. Skip any one of them and you get exactly what you paid for.
The honest scoreboard
The useful question is not whether to vibe code. It is which side of the line a given piece of work sits on, and being deliberate about the answer before you start typing.
✓ Generate freely
- –Layout, styling, and component scaffolding
- –The repeated CRUD slice, once one exists by hand
- –Migrations, seed data, throwaway scripts
- –Copy drafts, empty states, loading states
- –Anything you would happily delete and redo
⚠ Generate, then read every line
- –Auth, permissions, and tenant scoping
- –Money: totals, taxes, refunds, invoices
- –Anything a user acts on medically or legally
- –File uploads and other trust boundaries
- –Anything irreversible, including deletes
A note on the right-hand column
Nothing there says “do not use AI.” It says the output stops being a draft and starts being a proposal. You still write the WHERE clause that scopes a query to the current user. You just read the one it wrote first, and you know why it is right.
The interesting question was never whether AI can write a website. It obviously can, and it will write yours faster than you can read it. The question is what you are still responsible for when it does, and the answer has not changed at all: knowing what should exist, knowing what must not be got wrong, and being the person who checks.