Issue 27: What I throw away when I vibe code
The fastest prototypes I make are the ones I'm most careful to label "not final." Here's why.
Hi designers,
It’s Monday again! And I want to talk about something that’s been sitting with me since a call last week.
I’d vibe-coded a little flow the night before — a settings screen with a couple of connected states, nothing fancy, mostly to see if the steps made sense in the right order. It took maybe twenty minutes. And on the call, someone looked at it and said, so warmly, so encouragingly: “Oh nice, this basically works — can we just ship it?”
I’ve been thinking about that sentence ever since. Because it’s kind of the whole thing, isn’t it? “This basically works, let’s just ship it” is, I think, the most expensive sentence in our work right now. Not because the person saying it is wrong to be excited — the thing did look like it worked. That’s exactly the problem.
The cheap part and the expensive part
Vibe coding made producing an interface almost free. Twenty minutes, a few prompts, and there’s a real, clickable thing on the screen. But it did nothing — nothing at all — for the cost of deciding what the interface should be. The states, the hierarchy, what happens when the data is empty or the name is too long or the request fails. That work costs exactly what it always did.
And I think what happens is we feel the first cost drop and quietly assume the second one dropped too. It didn’t. The deciding is still on us. The prototype just got really, really good at hiding how much of it we skipped.
So the debt doesn’t get created when you vibe-code something. It gets created in that gap — the moment a fast sketch gets treated as a settled decision, by us or by the room, before anyone actually decided anything.
That’s the thing I want to hold onto this week: vibe coding is genuinely wonderful at exploring. The trouble starts when we let exploring quietly turn into committing.
What I actually do with the output
I’ll be honest — I almost never ship a vibe-coded thing as-is. Not because I don’t trust the tools (I love them), but because the raw output is a starting point, never the source of truth. Two ways I usually work:
Sometimes I stay in code and tweak directly with Claude Code — nudging the details, fixing the mismatches, making the real decisions the first pass glossed over. The generation gave me a draft to react to, which is lovely, but I’m the one deciding what stays. And when it makes sense, this is the route I take all the way — I’ll keep going in code, tighten it up, and get the prototype production-ready. This is genuinely part of my job now: vibe coding is one of the ways I help clients ship faster. So I’m not anti-shipping-what-you-vibe-coded at all — I do it. The difference is that by the time it ships, I’ve actually made the decisions, rather than letting the first draft make them for me.
Other times I treat the vibe-code as a sketch, pull it into Figma, do the actual design thinking there — spacing, states, the stuff I care about — and then vibe-code again from the version I'm happy with. Figma becomes the layer where things get decided on purpose. The prototype feeds it; it doesn't replace it. (I walked one of these loops all the way through — a FigJam diagram to a working Figma screen in a single session — in Issue 19 if you want to see it step by step.)
Both routes have the same little rule hidden inside them: the fast output never gets to be the final word. There’s always a step where I pull it back into a place where I’m making choices deliberately. I keep the flow and throw away the specifics.
Which is maybe the part nobody warned me about — the skill AI is actually asking of us isn’t prototyping faster, but knowing what to throw away.
The other kind of debt — the one in the room
Now, sometimes I do just want a quick prototype to test a flow. I’m not designing anything yet, I only want to know if the steps make sense. And here the debt isn’t in my file — it’s in everyone else’s head. A slick demo gets quietly filed as “done” by a stakeholder who wasn’t in my head while I made it.
So I’ve started thinking of “making sure people know this isn’t final” as part of the design work, not a disclaimer I tack on after. And the way I signal it is with fidelity itself.
The prototype is deliberately wireframe-ish. It has the branding, so it feels like ours, but the whole thing stays generic on purpose. And if there are mismatches — spacing that’s off, a component that doesn’t quite line up — I leave them. I don’t polish them out. The roughness is the message. It tells the room: this layer isn’t decided yet, don’t react to it.
But — and this is the part I’d gotten backwards for a while — that low polish only goes on the visual side. The content stays completely real.
Real content, rough pixels
This is the one I want you to steal if you take nothing else.
Content realism and visual polish are two separate dials, and they should almost never move together. Real names, real data, real awkward edge cases — a name that’s genuinely too long, a number that actually overflows, the empty state that actually happens. Never “[user’s name],” never lorem, never the tidy fake data that makes everything fit perfectly.
Because placeholder content lies. It hides every problem worth catching — it’s always the right length, it never wraps, it never breaks the layout. And people can’t react honestly to something that reads as fake. Real content is what makes the test real.
So the sweet spot I keep aiming for is: content dialed all the way up, visuals held low. It tests truthfully, and it still reads as unfinished. High-real, low-polish. That combination does two jobs at once, and for a quick flow test it’s just about perfect.
The bit I want you to hold onto
I don’t think the takeaway is “be careful with vibe coding,” and it’s definitely not “vibe coding makes bad UX.” It’s gentler, and I think more freeing than that.
Vibe coding is a wonderful way to explore cheaply — so explore with it, generously, without guilt. Just keep a clear line in your own head between the exploring and the committing. Pull the output back into a place where you decide on purpose. Keep your content real so the test is honest. And let the roughness of a prototype tell the truth about how finished it actually is — because AI made things look finished way faster than they become finished, and closing that gap is quietly our job now.
If you want the practical companion to all of this — the actual tool order I use, the build phases, exactly where Figma comes into the workflow — that’s what Issue 25 walks through. This issue is the thinking; that one is the how.
The gap between how-done-it-looks and how-done-it-is has never been wider. Minding that gap might be the most underrated design skill of the year.
That’s where I’ve landed, anyway. I’d love to know how you’re handling it — do you rebuild everything from scratch, or have you shipped vibe-coded work you’re actually happy with? Reply and tell me, I read every single one. :)
UX on the radar
🤖 Not all AI-assisted programming is vibe coding — but vibe coding rocks (Simon Willison) Why it matters: This is the cleanest version of the line I was trying to draw all issue. Willison pushes back on people using “vibe coding” to mean all AI coding, and pins it to the specific thing Karpathy meant — building fast without reading the code. Keeping the two apart is exactly what keeps exploring from quietly becoming committing.
🔍 Good from Afar, But Far from Good: AI Prototyping in Real Design Contexts (Nielsen Norman Group) Why it matters: NN/g put words to the thing I feel on those calls — an AI prototype looks so finished that feedback drifts to button sizes and colours instead of the structural questions that actually decide whether it works. Their fix is the same one I keep landing on: match the fidelity to the question you’re actually asking.
🧠 UX Debt: How to Identify, Prioritize, and Resolve (Nielsen Norman Group) Why it matters: If this issue got you nodding, this is the practical follow-on — how to actually name the debt, log it, and decide what to pay down first, so it doesn’t just live as a vague guilty feeling in the back of your head.
Until next time 👋
— Lisa



