Skip to content
All writing
ProductEngineering

Shipping MVPs That Survive

How to build and validate a product fast without creating tech debt you'll regret: scope to one core loop, fake what you can, choose boring tech, instrument from day one, and refuse to abstract before you understand the problem.

Roshan Lamichhane6 min read

There are two ways an MVP fails. It's too slow and precious to ever ship, so you learn nothing; or it's a throwaway hack that "works," gets real users, and then calcifies into a codebase everyone's afraid to touch. Both come from the same mistake: treating speed and durability as opposites you have to trade off.

They aren't. The trick isn't building less carefully — it's being ruthless about what you build, and disciplined about the small number of things that determine whether the MVP becomes a foundation or a liability. Here's how I approach it.

Scope to a single core loop

Every product has one loop that is the actual reason it exists. A note app: capture a thought, find it later. A trading tool: define a strategy, see how it would have performed. Your MVP is that one loop, done credibly, and almost nothing else.

The discipline is subtractive. For every proposed feature I ask: is this part of the core loop, or is it in service of it? Auth, settings, onboarding, an admin panel — none are the loop. They can be faked, deferred, or stubbed. The loop cannot; if the loop isn't compelling, no amount of surrounding polish saves the product, and you want to learn that in week one, not month six.

The one-sentence test

If you can't describe your core loop in one sentence a stranger understands, you haven't scoped it yet. "Users do X to get Y" — everything not on that path is a candidate to cut.

Scoping this hard feels uncomfortable because you're shipping something that obviously does less than you envision. That discomfort is the point. You're buying the fastest possible answer to the only question that matters: does anyone want the loop?

Fake what you can, build what you must

The fastest code is the code you didn't write. A huge amount of an MVP can be faked — not deceptively, but by doing manually what will later be automated, or hardcoding what will later be dynamic. The rule I use: fake anything whose value doesn't depend on it being automated.

  • Onboarding a customer? Do it by hand over a call. You learn more from ten manual onboardings than from an onboarding flow.
  • Need a "recommendation engine" to test whether people want recommendations? Hardcode a good list. You're validating demand, not the algorithm.
  • Nightly data refresh? A cron running a script beats a pipeline with retries and monitoring until you have data worth protecting.

What you must actually build is the core loop itself, and anything a user directly experiences as the value. Fake the plumbing, build the product. The corollary: don't build the thing that makes faking unnecessary until faking actually hurts. You'll feel it when it does — that's your signal, and it arrives with evidence attached.

Choose boring, reliable technology

An MVP is a bet on the product, not the stack. Every novel technology you adopt is a second bet running concurrently — one you didn't need to make. So I default hard to boring: the tools I know cold and that have a decade of answered questions behind them.

For me that's Postgres, a straightforward web framework, a background worker when I need async, and a managed host so I'm not doing ops. Postgres alone is a stand-in for an alarming number of "we need a specialized X" impulses — it's a perfectly good queue, a fine document store, and a search engine that's good enough for a long time.

# SKIP LOCKED turns a table into a safe multi-worker queue — no broker to run job = db.query(""" SELECT id, payload FROM jobs WHERE status = 'pending' ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1 """)

Boring tech is faster precisely because it's unsurprising. When something breaks at 2am — and it will — you want to be debugging your product, not learning the failure modes of a database you adopted last week for its landing page.

Spend your novelty budget on the product. Everything underneath it should be aggressively boring.

Instrument from day one

Here is the one place I never cut corners, because it's the difference between building a product and guessing at one. From the very first deploy, I want to see what users actually do. Not vanity metrics — the core loop's funnel.

You don't need an analytics platform on day one. You need to answer: did they reach the core action, did they complete it, did they come back. A handful of well-named events covers it.

// A few deliberate events beat a hundred auto-captured ones track("strategy_created", { userId, symbolCount }); track("backtest_run", { userId, durationMs }); track("backtest_viewed_results", { userId }); // the moment of value

That third event is the one that matters — it's where the user actually receives value. If people create strategies and run backtests but never look at the results, no survey will tell you why, but the funnel will tell you that, and that's where you go dig. Instrumentation turns opinions into questions you can answer, and shipping without it means flying blind while calling it intuition.

Talk to users; let it override the roadmap

Instrumentation tells you what is happening; users tell you why. You need both, and the qualitative half is the one engineers most often skip because it's uncomfortable and doesn't feel like building.

I try to talk to real users every week during an MVP, and I ask about their problem, not my solution. "Walk me through the last time you did X" beats "would you use a feature that does Y" — people are generous about hypotheticals and honest about their actual last Tuesday. The goal is to hear the problem described in their words, then check whether my core loop actually lands where the pain is.

Watch, don't ask

The single most useful thing is watching someone use the product without helping them. Every place they hesitate is a design bug you'd never have found by asking if they liked it.

When user reality and your roadmap disagree, reality wins. That's the entire point of shipping an MVP instead of a spec — to be corrected early, cheaply, and often.

Avoid premature abstraction

This is where "fast" MVPs quietly become the tech debt you regret, and it's a discipline problem more than a skill problem. The instinct to build the flexible, general, future-proof version is strong, and at MVP stage it's almost always wrong.

The reason is simple: a good abstraction is compressed knowledge about a problem, and at the start you don't have the knowledge yet. Abstract too early and you encode your guesses into the shape of the code, then spend months bending real requirements to fit a structure you invented before you understood the domain. My rules of thumb:

  • Prefer duplication over the wrong abstraction. Two or three similar code paths are cheap to read and trivial to unify later; a premature framework is expensive to unwind.
  • Wait for the third use before generalizing. The first two teach you what actually varies.
  • Keep the core loop's code obvious and boring. Cleverness there is a tax you pay on every future change.

MVP code that avoids premature abstraction doesn't have to be throwaway. Simple, direct, well-instrumented code is a genuinely good foundation — often better than the "properly architected" version, because it hasn't committed to guesses. The debt you regret comes from wrong structure, not from missing structure.

Closing

An MVP that survives isn't a lesser product built carelessly — it's a focused product built with discipline about where the discipline goes. Scope to one core loop. Fake the plumbing and build only the value. Stand it on boring, reliable tech. Instrument the funnel from the first deploy and talk to users every week. And refuse to abstract until the problem has taught you its real shape.

Do that and speed stops fighting durability. You ship fast because you built little, and what you built is simple enough to keep — which is the only version of "move fast" worth practicing.