It started as a weekend Flask app: one file, one SQLite database, one very proud founder. Sixty days later it had 400 daily users, a payment form bolted on with hope, and a bug where clicking "export" twice charged someone twice. The database file lived on somebody's desktop. Congratulations, the prototype is production now.
Every successful prototype ends up here, at the meeting where someone asks whether to patch it again or burn it down and start over. That is the prototype technical debt rewrite decision, and most teams make it with vibes instead of a framework.
Here is the honest answer. Rewrite the core loop when the core loop has changed or the data model lies to you; patch everything else. A full rewrite is almost always a trap, because you are rebuilding both the product and every invisible lesson baked into version one. Debt you took on purpose, to learn fast, is not a sin. Debt you discovered by accident, holding the business up, is the kind that burns.
Debt that earns vs. debt that burns
Not all tech debt is the same animal, and treating it as one undifferentiated shame pile is how teams make terrible decisions. Intentional shortcut debt is the stuff you chose: no tests on the admin page, a cron job instead of a queue, a config file instead of a settings UI. You traded polish for speed to learn something, and it paid for itself. This debt just sits there, harmless, until traffic grows.
Accidental load-bearing debt is different. The user table that also stores billing state. The "temporary" CSV import that became the only way data enters the system. The authentication logic copied from a tutorial that nobody now understands. This debt burns because every change routes through it, and every route through it risks the business. The first kind you patch forever. The second kind you plan around. If this sounds familiar from the early days, the rapid prototyping playbook that got you here deliberately creates the first kind and forbids the second.
The prototype technical debt rewrite decision, in three questions
When the meeting happens, skip the architecture opinions and ask three questions. Question one asks whether the core loop has changed: if users now do something fundamentally different from what version one was built around, the bones no longer fit the body. Question two asks whether the data model lies: if you need three joins and a prayer to answer "who is this customer," the schema is telling stories, and every new feature inherits the fiction.
Question three is the one you say out loud: has every feature started taking three times longer than it should? That multiplier is the interest payment, and when it compounds past your team's patience, you are already paying for a rewrite, just without receiving one. Track it honestly for a month: if a one-day change now takes three, write that down. Score two or more yeses and you rewrite the core. Score one and you patch. Score zero and you go back to work, because boredom is not a migration strategy.
Rewrite the core loop, patch the edges
The winning move is surgical. Identify the hot path, the two or three workflows users touch every day, and rebuild just that against a clean data model. Everything else, the admin pages, the weird export, the settings nobody opens, stays patched on the old code until it earns its own rewrite or dies of neglect. New traffic flows to the new core; old edges get strangled slowly behind it.
An illustrative timeline that works: three weeks to rebuild the core loop with real migrations, then a quarter of patching edges between feature work. Users see a faster product in week three. The team stops paying interest in week four. The discipline is refusing to widen the blast radius mid-project; the moment someone says "while we're in there," the schedule doubles. Run the migration itself in the off-hours, with a rehearsed rollback, because the core loop is the part of the business that cannot take a long weekend. For the hardening steps that sit between "it works" and "it survives," the v0-to-v1 checklist is the companion read.
The full rewrite trap
You have heard this story at a conference after-party, usually from someone holding a drink they regret. The team declares the codebase unsalvageable, estimates six weeks for a clean rebuild, and resurfaces six months later with half the features and twice the confidence. The old system, meanwhile, kept accruing bug fixes and edge-case knowledge the whole time, none of which made the trip. The double-click-charges-twice bug gets fixed in the rewrite, sure, but three bugs you forgot existed come back as nostalgic surprises. Version two launches dumber than version one, just better formatted.
There is exactly one exception: when the prototype validated the business but the domain turned out to be something else entirely. You built a booking tool, the market wanted a dispatch system, and almost nothing transfers, not the schema, not the flows, not the admin screens. That is not a rewrite. That is a new product with a head start on knowledge, and it should be scoped and priced like one.
Never apologize for version one
The last piece is cultural, and it matters more than the technical call. Teams that treat version one as an embarrassment make rewrite decisions out of shame, and shame is a bad architect. The prototype that got you 400 daily users did its job perfectly. It bought information with the only currency that matters, real usage, and the rewrite is the dividend.
When you take the plan to stakeholders, frame it that way: we are not fixing a mistake, we are cashing in what version one taught us. If you need the words for that room, demoing the plan to stakeholders covers the script. And if you are still at the stage where a quick build is the right tool rather than a liability, what a 48-hour build is actually for draws that line cleanly.
Patch what earns, rewrite what burns, and stop apologizing. Version one walked so version two could invoice. And when version three comes along, you will recognize the meeting.