vibe-coding

Vibe coding vs. agentic engineering: generating code isn't shipping it

Vibe coding is fine for prototypes and dangerous in production. The fix isn't less AI, it's process: a plan, a review, tests, and a human at the merge gate.

Andrej Karpathy coined “vibe coding” for a specific way of working: you describe what you want, the model writes it, you run it, and if it looks right you keep going. You don’t read the diff. You steer by outcome, not by inspection. The code is a black box that either behaves or doesn’t, and your only interface to it is the prompt.

That is a real skill, and for a real set of tasks it’s the correct one. A weekend spike to see if an idea has legs. A one-off script to reshape a CSV you will never touch again. A prototype you’re building to learn an API, knowing you’ll throw it away. A personal tool with one user who is also its author. In all of these, reading the code buys you nothing, because nobody is going to depend on it being correct. Speed is the only thing that matters, and vibe coding is the fastest way to move.

The problem is that the boundary between “throwaway” and “load-bearing” is invisible from inside the workflow, and it moves. The prototype that demos well gets a “can we just ship this?” The script that reshaped one CSV becomes a cron job. The moment other people trust the output, or it runs in production, the thing you skipped (reading the code, proving it’s correct) is exactly the thing you now need, and you have none of the artifacts that would let you get it back.

What actually breaks

Here’s a concrete case. You ask an agent to add a “delete account” endpoint. It produces something that looks completely reasonable:

@app.delete("/api/accounts/{account_id}")
def delete_account(account_id: int, user=Depends(current_user)):
account = db.get(Account, account_id)
account.delete()
return {"ok": True}

It compiles. The happy path works: you log in, hit your own account, it’s gone, the demo lands. Nobody read the diff closely because it did what it was asked.

There’s no check that account_id belongs to user. Any authenticated user can delete any account by incrementing an integer. This is not an exotic bug. It’s the most common class of web vulnerability there is, and it’s invisible in a demo because a demo only ever exercises the happy path with a well-behaved user.

The N+1 version of this story is quieter and just as common. An agent writes a loop that looks clean, issues one query per item, and runs fine against the twelve rows in your dev database. It falls over at ten thousand rows in production, weeks later, when nobody remembers the change. The code was never wrong on the machine where anyone looked at it.

Neither of these is a failure of the model. The model did what a fast, competent, slightly careless junior would do: solved the stated problem, didn’t ask about the ones you didn’t state. The failure is upstream, in a process that had no step between “code exists” and “code ships.” Vibe coding removes that step by design. That’s the point of it, and it’s fine right up until it isn’t.

Generating code is cheap. Knowing it’s correct is not.

The economics flipped. Writing a plausible first draft of almost any feature used to be most of the work, and now it’s close to free. What didn’t get cheaper is the part that was always the actual job: knowing the code is correct. That takes reading it, testing it, thinking about the inputs the author didn’t think about, and having someone other than the author look. None of that got automated away by better code generation. If anything the pressure on it went up, because the volume of code arriving for review went up.

So the useful mental model isn’t “the agent is an oracle that returns correct software.” It’s “the agent is a very fast junior engineer.” You would not let a new hire’s first PR merge unread just because it passed on their laptop. You’d put it through the same pipeline everything else goes through: a plan you agree with before code gets written, an implementation, a review, tests that actually run, and a human who decides it’s good enough to merge. That pipeline is what turns raw output into something you can trust. Call the result agentic engineering: still AI doing the writing, but inside a process that produces the evidence the writing is correct.

The distinction is sharp. Vibe coding trusts the code because it looks right and behaved once. Agentic engineering trusts it because it passed a set of checks you defined in advance. One is a vibe. The other is a gate.

Gates a machine can check, not a model that says it’s done

The weak version of “add process” is to ask the agent to review its own work. This mostly doesn’t help, for the obvious reason: a model that missed the missing auth check when writing the code will cheerfully miss it again when reviewing, and then declare itself done. “I’ve verified the implementation is correct and secure” is a sentence a model will emit whether or not it’s true. A completion signal that the same model produces is not evidence. It’s the same vibe with more words.

A gate has to be something outside the model’s own judgment. This is where Fletch draws the line. In Fletch you chain specialist agents into a workflow (plan, build, review, test) on a single branch, and every step has a gate that decides whether the pipeline advances. The gates are deterministic predicates the engine evaluates, not the LLM: a commit actually exists past the starting point, a named artifact was written, the project’s test command exits green, or you personally approved the step. A review step can loop back on a build step and make it try again, capped at a fixed number of attempts, until its gate clears. If a gate isn’t satisfied, the run pauses and waits for you. It does not advance because someone said “looks good.”

That last gate is the one that matters most, and it’s a human. Fletch runs each agent in a real isolated workspace, a private clone of your repo, so nothing an agent does touches your working tree until you decide it does. Work reaches you as a branch and a diff and a PR, with the test results and review attached, and you hold the merge. The auth bug from earlier doesn’t ship, because a test that hits the endpoint as the wrong user is a gate the engine checks, and because a person reads the diff before it lands. Neither of those steps is optional in the pipeline. That’s the whole difference.

Vibe code when the cost of being wrong is your own time and nobody else’s. It’s a genuinely good tool for that. When the cost of being wrong is someone else’s data, a production incident, or a teammate inheriting code no one ever read, keep the speed and add the gates. The agent can still write everything. It just has to earn the merge.