Guide

Vibe coding friction tradeoffs. Four costs nobody names.

Every page about no-signup, no-setup, no-code vibe coding sells the same thing: friction is bad, removing it is pure win. It is not. Each friction you cut has a cost, and on a tool that lives on the no-friction end of the spectrum, the cost shows up as a constant in the source. Here are the four mk0r is paying right now, with the file and line that votes for each one.

M
Matthew Diakonov
7 min
Direct answer (verified 2026-05-20)

Every removed friction in a vibe coding loop has a hidden cost. No signup means no identity to throttle, so the trial caps at 6 turns. No model picker on the free tier means power users cannot benchmark on a bigger model. No dashboard means the project lives in this browser's localStorage and is unreachable from a second device. A 1 hour sandbox TTL means a session you walked away from is paused when you return. The honest pitch is not "no tradeoffs," it is "we picked which tradeoffs to eat."

Verified against the appmaker source at src/app/api/chat/route.ts:23, src/app/api/chat/model/route.ts:5, src/app/(landing)/page.tsx:33 and :64, and src/core/e2b.ts:33.

6 turns

The only abuse gate on a no-signup loop is the turn counter. 6 is the smallest number where the visitor can close a real iteration loop and the largest where a single bot cannot eat a paid plan in a morning.

ANON_TURN_LIMIT at src/app/api/chat/route.ts:23

The frame nobody draws

Most posts about vibe coding friction are arguments for cutting it. Accounts kill the maker loop, setup screens kill the maker loop, dashboards kill the maker loop, and so on. All true. The part the posts skip is that each cut has a second-order cost. A signup form is not just a barrier, it is also the place where you ask "is this a person or a script," where you let someone save what they made, where you remember their model preference. Take the form away and those jobs do not go away, they migrate. They end up either redistributed across the rest of the product, or simply not done.

Looking at this in mk0r is straightforward, because every redistributed job lives at a specific line of source. The friction is gone, and the cost it used to absorb is now a constant somewhere. Four of those constants are below.

No signup, capped at 6 turns

ANON_TURN_LIMIT = 6 (src/app/api/chat/route.ts:23). The only abuse gate on an anonymous loop is the turn counter. We picked 6 because below that the visitor never closes a first iteration cycle, and above it the trial bleeds compute.

No model picker, free tier pinned to haiku

FREE_MODEL = "haiku" (src/app/api/chat/model/route.ts:5). With no account, no preferences to remember, so the route refuses any other model for unauthenticated users. Power users cannot benchmark on the free tier.

No dashboard, the textarea is the project

Landing page mounts a single textarea, not a project picker. mk0r_session_key in localStorage is the only thing tying you to a sandbox. Open a fresh browser and the project is unfindable.

No "save" button, 1 hour TTL on the sandbox

E2B_TIMEOUT_MS = 3_600_000 (src/core/e2b.ts:33). Removing the persistence UI means we cannot ask a user to manage retention, so the default lifespan does the deciding. After an hour the sandbox pauses.

No env setup, eager prewarm

fetch("/api/vm/prewarm", { method: "POST" }) on landing-page mount (src/app/(landing)/page.tsx:64). The setup wizard is replaced by a fire-and-forget POST that pays compute on every visit, whether the visitor types or not.

Cost one. No signup means anti-abuse moves to the turn counter.

The landing page never asks for an email. The session is created on first paint: a SESSION_KEY = "mk0r_session_key" in localStorage, set to crypto.randomUUID() if it is missing (src/app/(landing)/page.tsx:33 and :49). That key is the only thing the server has to identify the visitor before they hit a wall. Which means the only thing protecting the cluster from a single tab churning through prompts forever is a per-key counter.

That counter is ANON_TURN_LIMIT, which lives at src/app/api/chat/route.ts:23, with the comment right above it: "Two turns was way too tight, users were getting blocked before they finished their first prompt cycle." The current value is 6. The actual gate logic ten lines down reads the anonymous user's anonTurnsCompleted field from Firestore and returns 403 sign_in_required if it is at or above the limit. No signup, but the 6th prompt has the gate behind it.

The honest version of "no signup" is "no signup for 6 turns." A tool that wanted the literal claim to hold would have to either run no model at all on the free path (Claude Artifacts goes this way, with no anonymous access at all) or accept open-ended cost on every anonymous visit. We picked the cap. The cap is the cost of the missing form.

Cost two. No model picker means the free tier is locked to one model.

Preferences are something you remember about a person. With no signup, there is no person. So FREE_MODEL = "haiku" at src/app/api/chat/model/route.ts:5 is not a marketing choice about which model is "best" for prototypes. It is the only model an unidentifiable visitor can pick, because the rest of the route refuses any other choice for users where isAnonymous is true (lines 17 to 24).

The cost is that a power user who tries mk0r anonymously and finds the output disappointing on their idea cannot test "would Sonnet have nailed this?" from the same surface. The fix is to sign in. The reason the fix exists is that signing in creates the profile that the picker needs in order to remember which model you chose. Removing the signup removed the storage for the preference, which removed the picker, which collapsed the free tier to the smallest model that still feels live. Faster preview, narrower ceiling.

Cost three. No dashboard means the project lives on this device.

The landing page is not a dashboard. It is a textarea. A returning visitor who never signed in does not get a project list to choose from, because mk0r's server has no email or user id to look up. The only link between the visitor and a sandbox is the localStorage key on the browser they used last time.

This is fine on a desktop browser that you keep around. It is broken the moment the visitor switches devices, clears storage, or opens the site in incognito. The project they were happily iterating on yesterday is unreachable today, because there is no surface to discover it from. A dashboard would solve this, but a dashboard is a sign-in surface. Removing the sign-in meant removing the page that would have indexed the user's past work. We made the first visit flat and lost the resume path for anonymous users in the same edit.

Cost four. No save button means the sandbox decides when to pause.

E2B_TIMEOUT_MS = 3_600_000 at src/core/e2b.ts:33. One hour, in milliseconds. Why a hard timeout? Because removing the "save my project" UI also removed the surface where we could have asked "do you want to keep this around?" The user is never given the prompt, so a default has to make the decision instead.

One hour is long enough to run a real demo session and short enough that the next time you open the project, you are explicitly choosing to resume rather than passively inheriting a stale environment that drifted while you were at lunch. The cost is real: walk away from a half-built app for ninety minutes and the sandbox is paused when you come back. There is a reload path that brings history with it, so the timeline is not destroyed, but the live process is. Removing the friction of "manage your projects" pushed the lifespan decision into a constant.

Where the cost goes, end to end

The four costs do not show up to the visitor as four costs. They show up as a single sequence: the browser hits the page, a session id is minted, a sandbox is warming before anyone has typed, the first prompt runs on a default model, the sixth prompt hits a sign-in wall, and an hour later the box pauses itself. Each step in that sequence is the absence of a friction the visitor never saw, plus the constant that picks up the slack.

The no-friction loop, with the cost at each step

BrowserLanding pageChat routeSandboxGET / (first visit)mk0r_session_key = crypto.randomUUID()POST /api/vm/prewarm (cost: idle compute)POST /api/chat (first prompt)FREE_MODEL = haiku (cost: no choice)anonTurnsCompleted >= 6? (cost: hard cap)stream turnpreview readyE2B_TIMEOUT_MS = 3.6e6 (cost: 1 hr pause)

Read top to bottom, the sequence describes a frictionless first visit. Read with the "cost" labels, it describes the four bets the product made to keep the first visit frictionless. Other tools picked other bets. A tool that wanted a real model picker on the first prompt would have to put a signup before the picker. A tool that wanted unlimited anonymous turns would have to either lose the model or absorb a much bigger compute bill. The choices are real product positions, and the source files are where you read them.

What the no-friction surface is not for

Worth saying directly: the costs above are tolerable because the no-friction surface is the on-ramp, not the workshop. If you want four ongoing projects, a model picker, the ability to walk away for a week and come back to a live sandbox, and a saved history that follows you across devices, sign in. The signed-in path puts every removed friction back, in exchange for the small friction of an account. That is the right trade for that user. The trade we are making on the anonymous path is for the other user, the one who came from a tweet and would not finish a signup form even if they were paid to.

The traditional coding world does not need this distinction because it has been opt-in since forever: you install a thing, you accept the setup, you keep the project. Vibe coding tries to put a real model inside a loop that opens before the user has decided whether they care. That is what makes the friction choices interesting, and it is what makes them costs in the first place.

The summary

No-friction vibe coding is not free. Every removed step is a cost the rest of the product has to absorb, and the absorption happens as a constant. mk0r's four are 6 turns, one model, one device, one hour. The honest pitch is not that we built a tool without tradeoffs. It is that we picked these four to eat so that the first visit could be a textarea instead of a form.

If you want to see the four in motion, open mk0r.com and start typing. You will see the prewarm fire in the network tab before you finish your first sentence, you will get a preview from haiku, and you will hit the 6 turn cap exactly where the comment in the source says you should.

Want to walk through these four tradeoffs on a real app?

Bring an app idea. We will open mk0r together, watch the prewarm, hit the 6 turn cap, and talk through which tradeoffs you would have picked differently.

Frequently asked questions

What is the most under-discussed friction tradeoff in vibe coding?

Anti-abuse. Every vibe coding tool that ships a real model behind a no-signup loop has to pick a number for how many turns an anonymous visitor gets before something stops them. mk0r picked 6 (src/app/api/chat/route.ts:23). Below 6 the trial blocks before the first iteration loop closes. Above 6 a single bot can chew through a paid plan's worth of compute in a morning. The choice is invisible to the visitor unless they hit the wall, and the marketing page does not list it, but it is the load-bearing constant for the entire frictionless story.

If iteration matters more than one-shot, why is the anon trial only 6 turns?

Because anti-abuse is the other side of the no-signup decision. 6 is the smallest number where a curious visitor can actually close one iteration loop: one turn to seed, two or three to refine, one to undo a misstep, one to retry. The friction we removed (signup, payment, identity) is the only thing that normally throttles abuse. Without it, the only gate left is the turn counter. So the number lives on the floor of "a real evaluation" and the ceiling of "what one anonymous browser can spend before we have to stop them." Higher and the unit economics break, lower and the no-signup promise becomes false.

What does "no dashboard" actually cost the user?

Orientation. The textarea on the landing page is the project. The session key that ties you to a sandbox is in localStorage on this browser, on this device, under the key mk0r_session_key (src/app/(landing)/page.tsx:33). Open the site in a private window or on your phone and the project you started this morning is unreachable. A dashboard would let you list past projects and resume them. Removing it kept the first-time experience flat, but it means a returning user with no account is starting over. The trade is honest, but it is a trade.

Why is the sandbox killed after one hour even though I have not asked to stop?

E2B_TIMEOUT_MS = 3_600_000 at src/core/e2b.ts:33. The 1 hour TTL is a direct consequence of removing the "save my project" UI. With no save button, no retention prompt, and no email to write to, we cannot ask the user to manage the sandbox's lifespan, so the default has to manage it. One hour is long enough to demo, short enough that the next time you come back you are intentionally choosing to resume rather than passively inheriting a stale environment. The cost: a session you walked away from for lunch is paused when you get back.

What does the prewarm POST actually cost?

Idle compute. /api/vm/prewarm fires from useEffect on the landing page (src/app/(landing)/page.tsx:64), so a sandbox starts warming the moment the page mounts, before the visitor has typed a single character. The benefit is that by the time they finish their first prompt, the box is ready. The cost is that compute spins for visitors who never submit anything, and for bounce-rate sessions that pull the page just to look at it. The traditional friction we removed ("click the New Project button to start a sandbox") would have made the bounce-rate visitors free. Removing it shifted that cost onto every page view.

Why can't a free user pick a bigger model?

Because preferences are something you remember about a person, and there is no person to remember. FREE_MODEL = "haiku" lives at src/app/api/chat/model/route.ts:5 and the route refuses any other model for unauthenticated or anonymous users on lines 17 to 24. The honest reason is that a user with no account has no persisted profile to attach a model choice to, no payment to attach to a more expensive model, and no plan to expose the menu to. Picking the model on the user's behalf is the only choice the no-signup architecture leaves. Power users feel this immediately: they cannot test whether a bigger model would have solved the bug they just hit.

Is removing all this friction worth the tradeoffs?

It depends on the audience. For a curious visitor who came from a tweet, yes, by a long way. They see a textarea, they describe an idea, they get a preview, they iterate a few turns. No account form, no card on file, no choice paralysis. For a power user who wants to compare models, save four ongoing projects, and pick up where they left off across devices, no, they need to sign in. The cleaner framing is that the no-friction surface is the on-ramp and the signed-in surface is the workshop. The tradeoffs cluster on the on-ramp because that is where the absence of friction is the product.

Where does the term "vibe coding" come from, exactly?

Andrej Karpathy posted "there's a new kind of coding I call 'vibe coding'" on February 2, 2025. Collins English Dictionary picked it as Word of the Year for 2025. Karpathy has since started using "agentic engineering" for the professional, deliberate version of the same loop, but the original "vibe coding" sense (describe, watch, iterate) is the one this page talks about. The friction tradeoffs only matter at this end of the spectrum, because at the agentic-engineering end the user has already opted into setup and an account, so the costs of removing friction never enter the picture.

mk0r.AI app builder
© 2026 mk0r. All rights reserved.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.