The 80-20 Trap of Vibe Coding (and How to Get Past It)

AI writes the first 80% of an app at unsettling speed: scaffolding, CRUD, standard screens, forms, navigation. The last 20% — fine-grained error handling, performance under load, edge cases, architectural consistency — resists prompting. And that 20% decides whether your project becomes a product people use or stays a good-looking demo.

The good news: crossing that 20% doesn't take a decade of experience. It takes a method. Here it is, condensed:

  • split the work into small, closed tasks you can verify one by one;
  • freeze your interfaces and data contracts before generating the code that plugs into them;
  • write tests on the critical paths (money, data, authentication);
  • make one request at a time;
  • personally read every line that touches money, data, or auth.

The rest of this post explains why the last 20% is genuinely different, what "instant technical debt" is, what Pieter Levels' very public experiment shows, and how to put the method into practice today.

Why the first 80% goes so fast

CRUD screens, login forms, paginated lists, dashboards: AI models have seen these patterns in every possible variation during training. You describe, they assemble.

So in a weekend you have something that looks like a product. Clean screens, a wired-up database, smooth navigation. That's not an illusion — it genuinely works.

The trap is what that speed does to your expectations. It makes you believe the rest of the project will move at the same pace. It won't. The last 20% isn't "more of the same" — it's a different kind of problem.

The 20% that resists prompting

Four families of problems come up over and over:

  • Fine-grained error handling. What happens when the payment API times out? When two users edit the same record at once? Generated code handles the happy path; production is made of unhappy ones.
  • Performance under load. The query that's instant with fifty test rows can crawl against real data. Nothing warns you until real traffic arrives.
  • Edge cases. The user who double-clicks "Pay". The huge file upload. The name with an emoji in it. Each one is trivial alone; together they are what robustness means.
  • Architectural consistency. Each prompt produces code that's locally correct but globally inconsistent: three error-handling styles, two naming conventions, duplicated logic.

What these have in common: none of them shows up in a demo. And all of them require a whole-project view — exactly what the AI loses as the codebase grows.

Instant technical debt

Technical debt normally accumulates over years: shortcuts under deadline pressure, code inherited from people who left, conventions that drift. Vibe coding compresses that cycle. Code generated in a single week can show the maintenance problems of an old legacy system: duplicated logic, inconsistent conventions, dependencies nobody understands, zero tests.

Call it instant technical debt. It doesn't happen because the AI "writes bad code". It happens because every generation starts from whatever the model can see in its context right now — not from the overall vision you carry in your head. Without guardrails, each prompt adds a layer slightly different from the last.

The practical consequence: the difficulty of a vibe-coded project isn't measured in time spent, but in how many layers were generated without a shared contract.

What fly.pieter.com shows

Pieter Levels built a flight simulator, fly.pieter.com, almost entirely through prompts. He documented the result publicly: roughly $38,000 in revenue in ten days. Proof that vibe coding works — commercially, too.

He documented the other side just as openly: past roughly 800 lines of code, his AI started reintroducing bugs he had already fixed and forgetting constraints he had stated earlier, as the context window saturated.

That's the 20% wall, exactly. The tool that got you to a demo in days starts undoing your own work as you approach production. The problem isn't you, and it isn't your prompting — it's a structural limit you route around with method, not with a magic prompt.

The method for crossing the last 20%

Five rules, in order.

1. Split work into small, closed tasks. "Add retry with backoff to the Stripe call, and log every failure" — not "make the app more reliable". A closed task has a scope, an end, and a success criterion you can check yourself.

2. Freeze interfaces before you generate. Write — or have the AI write, then validate — your contracts: types, data schemas, function signatures. Only then ask for the code that implements them.

// Contract frozen BEFORE generating the implementation
interface PaymentResult {
  status: "paid" | "failed" | "pending";
  orderId: string;
  failureReason?: string;
}

A frozen contract is an anchor the AI can't "forget": you paste it into every relevant prompt, and any code that violates it gets rejected.

3. Write tests on the critical paths. You don't need 100% coverage. Target the paths that touch money, data, and auth. A passing test is a constraint the AI can no longer break without you noticing immediately.

4. One request at a time. Kitchen-sink prompts ("fix the bug, add the feature, and improve the design") produce kitchen-sink code. One request, one check, one commit. You always know which change caused which effect.

5. Read everything that touches money, data, or auth. You don't have to read all the code. But those three areas — line by line, yes. If a line doesn't make sense to you, ask the AI to explain it, then to simplify it until it's obvious.

Where to start today

Concretely, in order, on your current project:

  • List your app's three critical paths (payment, user data, login).
  • Write or generate one test for each, and confirm they pass.
  • Extract your existing contracts (types, schemas) into a reference file you paste into prompts.
  • Rewrite your next big AI request as three or more closed tasks.
  • Read every line of code that touches money — today, not "soon".
  • Adopt the rhythm: one request, one check, one commit.

The 80% proved you can build. The 20% proves you can ship. It's slower and more demanding — and it's where your project is actually won.