Iterate, don't one-shot. The answer is in the route table.
Most posts on iterate-versus-one-shot in vibe coding stay theoretical. Open a shipped AI app builder and the answer is encoded in the surface area: which endpoints exist, which endpoints are conspicuously missing, and what the trial budget actually buys you.
Iterate. mk0r's anonymous trial budgets 6 turns instead of 1, because telemetry showed users could not finish a single iteration loop at smaller numbers. The chat API exposes /undo, /redo, /revert, and /history endpoints. It does not expose a regenerate-from-scratch route. The product was rebuilt away from one-shot.
Verified against the appmaker source at src/app/api/chat (route.ts plus seven sibling endpoints) and the comment at src/app/api/chat/route.ts lines 21-23.
“Two turns was way too tight, users were getting blocked before they finished their first prompt cycle.”
src/app/api/chat/route.ts:21-22 (the comment that justifies ANON_TURN_LIMIT = 6)
The question, in builder terms
"Iteration over one-shot" sounds like a position to argue. Mostly it's a question about how you spend your prompt budget. One-shot is one prompt in, one app out, with no follow-ups. If you want to change something, you write a fresh prompt and accept a fresh app, byte-for-byte different from the previous one.
Iterate is one prompt in, one diff out, applied on top of the previous version. The agent edits files in place, not regenerates them. The two are not philosophically opposed. They are two ways of using the same tool. The real question is which one converges faster on what you wanted.
Most pieces I've read on this land at the same takeaway: iterate, because models drift on long prompts and you cannot predict every detail upfront. True. Also theoretical. None of them open up a shipped builder and show what the source says.
Open src/app/api/chat. Here's what's not there.
The chat API for mk0r lives in one directory. Every route the client can hit during an active session is a subfolder. Listing it tells you what the product was designed to support.
- route.ts one prompt, one turn, one commit
- /undo revert to the previous SHA
- /redo move forward in the timeline
- /revert jump to any prior SHA
- /history list every commit so far
- /cancel stop a turn mid-stream
- /mode, /model per-turn config
- not present: /generate, /regenerate, /restart
There is no "throw away the project and start over from this new prompt" endpoint. To start fresh you delete the project and provision a new VM, which is intentionally not a one-click action. The friction is the design. Once you have committed a turn, the cheapest move is always to iterate on top of it. The structural choice was made for you.
Why the trial is 6 turns, not 1
ANON_TURN_LIMIT lives at src/app/api/chat/route.ts:23 as the constant 6. The number was not pulled out of a hat. The earlier value was 2, and the comment two lines above the constant records why it changed:
// Keep this loose enough that a curious visitor can play with the product
// for a real session before hitting the gate. Two turns was way too tight,
// users were getting blocked before they finished their first prompt cycle.
const ANON_TURN_LIMIT = 6;
Six is the floor for a useful evaluation of the product. One prompt to seed, two or three to refine, one to undo a misstep, one to retry. Below 6, the visitor is judging the product on a first draft alone, which for any non-trivial app is the wrong sample size.
And the things you cannot tell from one shot, no matter how good the first draft is:
What only iteration reveals
- Whether the layout makes sense once you tap into a sub-screen
- Whether the empty states render
- Whether the model misunderstood your jargon
- Whether your follow-up prompts actually steer it
- Whether you can recover from a bad turn (you can: it's /undo)
- Whether the iteration cadence is fast enough to stay in flow
One iteration loop versus one one-shot
The iteration loop closes fast in a tool that commits per turn. Each step is bounded, the work between steps is a diff (not a regenerate), and the previous SHA is recoverable byte-for-byte if the next turn goes wrong. Compare that to the one-shot shape, which has no commit, no diff, and no way back to v1 without describing v1 to the model in prose.
Iteration loop
Prompt
One change, 20 words
Stream
Files edit in place
Commit
Per-turn SHA on /app
Decide
Keep, undo, or fork
Next prompt
Loop closes in seconds
One-shot loop
Prompt
Everything, 400 words
Wait
Full regenerate
Output
Byte-different result
Live with it
Or describe v1 back to model
The substrate that makes iteration cheap
Iteration without recoverable state is not iteration, it is gambling. commitTurn at src/core/e2b.ts:1759 runs git add -A and git commit inside the VM after every successful turn, then pushes the resulting SHA onto a historyStack on the session.
undoTurn at src/core/e2b.ts:1855 walks that stack back: it picks the previous SHA, runs git checkout <sha> -- ., then commits the result with message "Undo to <short-sha>". The undo is itself a commit, so the timeline never silently rewrites and undo of undo (redoTurn at line 1868) just walks the other direction. Because every step is a commit, the timeline survives the VM going to sleep and coming back.
The result: the cost of being wrong on any given prompt is one click. That changes the kinds of prompts users send. They take more swings. "Try a totally different layout", "now make it dark", "go back two and try again". With a one-shot tool, the cost of being wrong is the wall-clock time to regenerate plus the cognitive cost of describing the prior version back to the model. Users converge to the first acceptable output and stop swinging. The iteration budget has nowhere to go.
Where one-shot is still right
For one specific case: the app is small enough to describe in one sentence, and you do not care if the result diverges from your sentence. A meme generator. A static landing tile. A tip calculator. Quick and disposable, no follow-ups needed.
Anything with state, multiple screens, or a brand voice that needs to stay consistent across the app: you will iterate. Even a to-do list usually needs one follow-up to fix the empty state. The honest version of "one-shot" for those cases is just "iteration with one turn so far," and the trial budget gives you five more.
Want to see the iteration loop on a real app?
Bring an app idea, we'll iterate it live in mk0r and walk through the commit graph behind every turn.
Frequently asked questions
Should you iterate or one-shot in vibe coding?
Iterate. The short reason: a single prompt cannot describe an app well enough for the model to converge on the first try. The structural reason, at least in mk0r: the anonymous trial budgets 6 turns instead of 1, and the chat API exposes /undo, /redo, /revert, and /history but no regenerate-from-scratch endpoint. The product was rebuilt away from a 2-turn cap when the comment in src/app/api/chat/route.ts:21-22 noted that 'two turns was way too tight, users were getting blocked before they finished their first prompt cycle.' Iteration is what the surface area allows.
What is the actual difference between one-shot and iterate, in builder terms?
One-shot is one prompt in, one app out, with no follow-ups. If you want to change something, you write a fresh prompt and accept a fresh app, byte-for-byte different from the previous one. Iterate is one prompt in, one diff out, applied on top of the previous version. The agent edits files in place, not regenerates them. In mk0r the iterate path commits each turn to a git repo inside the VM (commitTurn at src/core/e2b.ts:1759), so a turn is undoable on its own and the previous version is recoverable byte-for-byte by SHA.
Why does the anonymous trial give 6 turns instead of 1?
Because telemetry showed users were not getting through their first iteration loop at smaller numbers. The constant lives at src/app/api/chat/route.ts:23 as ANON_TURN_LIMIT = 6, with the comment 'Two turns was way too tight, users were getting blocked before they finished their first prompt cycle.' The number is the floor for a useful evaluation: one prompt to seed, two or three to refine, one to undo a misstep, one to retry. Below 6, you are evaluating the first draft alone, which for any non-trivial app is the wrong sample size.
Is one-shot ever the right choice?
Yes, for one specific case: when the app you want is small enough to describe in one sentence and you do not care if the result diverges from your sentence. A meme generator, a static landing tile, a tip calculator. The Quick Haiku mode that lives in the codebase exists for that case, fast and disposable. Anything with state, multiple screens, or a brand voice that has to be consistent across the app, you will iterate. Even a 'simple' to-do list usually needs one follow-up to fix the empty state.
What is in src/app/api/chat that proves the iteration bias?
The directory contains route.ts (one prompt, one turn, one commit), and the subdirectories /undo, /redo, /revert, /history, /cancel, /mode, and /model. There is no /regenerate, /generate, or /restart route. The only way to throw away a project and start fresh is to delete it and provision a new VM, which is intentionally not a one-click action. Once you have committed a turn, the cheapest path is always to iterate on top of it.
Why does mk0r commit a turn to git? Other builders do not.
Because iteration without recoverable state is not iteration, it is gambling. commitTurn at src/core/e2b.ts:1759 runs `git add -A` and `git commit` inside the VM after every successful turn, then pushes the SHA onto a historyStack. undoTurn at src/core/e2b.ts:1855 walks that stack back via `git checkout <previous-sha>` and creates a new commit with the message 'Undo to <short-sha>'. The undo is itself a commit, so undo of undo (redoTurn at line 1868) also works, and the timeline survives the VM restarting. Without that substrate, 'undo' has to be 'please write back the version you wrote two prompts ago' and the model will get it close but not exact.
What about long, very detailed prompts? Are those a one-shot path that works?
Sometimes. A detailed prompt is closer to a tightly scoped iteration than a true one-shot, because you are doing the iteration in your head before sending. The catch is that long prompts hit the same drift problem as long context: the model loses track of details from the front of the message by the time it gets to the back. In practice, a 200-word prompt followed by three 20-word follow-ups outperforms a single 400-word prompt, because each follow-up gets the full attention of the model on a small change.
Does the iteration bias actually change the user's behavior?
Yes. Because /undo and /revert are real endpoints that map to byte-exact restore, the cost of being wrong is one click. That changes the kinds of prompts users send. They take more swings: 'try a totally different layout', 'now make it dark', 'go back two and try again'. With one-shot, the cost of being wrong is the wall-clock time to regenerate plus the cognitive cost of describing the prior version back to the model. Users converge to the first acceptable output and stop swinging. The taste muscle does not get the reps.
Iteration internals
Keep reading
Vibe Coding Iteration Taste Lives in the Commit Graph
The substrate that lets taste develop: per-turn commits, byte-exact undo, fork-on-undo. Where the muscle gets its reps.
The Vibe Coding Iteration Wall: Four Failure Modes
Regenerate-not-edit, no durable state, approximate undo, stale context. The four shapes of the wall, mapped to code.
One-Shot AI Prototypes: The Race That Starts Before You Type
Why mk0r's prewarm fires on page mount, not on Send. The pre-typing speed work behind the first turn.