Your app was built fast with Lovable, Bolt, Cursor or Claude Code, and for a while it grew fast too. Now every change breaks something else. Nobody can explain how the pieces fit together, and each new prompt seems to make it worse. Sooner or later someone says: “Let’s just start over.”
With AI tools, starting over feels cheap. An agent can produce a new version in an afternoon. This article explains why that impression is misleading, and how to make the refactor-or-rewrite decision on evidence instead of frustration.
Why “start over” is tempting, and why it often goes wrong
In 2000, Joel Spolsky called a full rewrite from scratch the single worst strategic mistake a software company can make, using the Netscape browser as his example. His core argument still holds: reading code is harder than writing it, so most developers judge existing code as messier than it is. The ugly parts often contain fixes for real bugs that took real usage to find. A rewrite throws that knowledge away, and the new version will repeat many old mistakes.
Martin Fowler makes a related point in his note on the Strangler Fig pattern. A replacement looks easy to specify, because “it should do what the old one does”. In practice, the exact behavior of the existing system is hard to pin down, and while the rewrite runs, users still want new features.
AI-generated code adds a twist. With a prototype from a builder tool, the hard-won bug fixes may be fewer. But the real requirements are also less written down. They live in hundreds of prompts and in the heads of a few people. A rewrite has to rediscover all of them.
Three questions before you decide
Before choosing, answer these questions in writing.
- What outcome do you need? Faster feature delivery, fewer incidents, lower hosting costs or passing an investor’s technical due diligence each lead to different answers. Fowler’s colleagues list “understand the outcomes you want” as the first activity of any modernization.
- What kind of mess is it? Spolsky separates three kinds: wrong structure, slow parts and ugly code. Structure can be fixed by moving code step by step. Slow parts can be optimized or rewritten locally. Ugly code is fixed with tooling, not a rewrite.
- Is the data model sound? If the database schema and access rules are wrong at the core, for example tenant data that is not separated, a deeper rebuild of that layer may be justified. If the data model is sound, most problems sit in code that can be replaced in parts.
Imagine a logistics SME with an internal dispatch tool built in Bolt. The screens are slow and the code is tangled, but the data model is clean and the business rules are correct. That points to refactoring and local rewrites, not a new system.
Make the existing behavior testable first
Whatever you decide, first capture what the app does today as automated tests. Start with the critical paths: sign-up, login, payment, the core workflow.
A recent experiment on Martin Fowler’s site shows why this matters when agents write the code. Naresh Jain had coding agents build the same API service with and without a “behavioral harness” of API contracts, examples and tests. A cost-optimized model passed the full external acceptance suite in 3 of 10 runs without the harness and in 10 of 10 with it. The failed runs did not look broken. Each still passed most of the checks, and the misses sat at timeouts, error codes and integration seams. The author is careful to note that the study covered one new service, not legacy code. But the lesson transfers: tests that come from agreed expectations, and not from the agent’s own reading, are what make agent output checkable.
For a rescue, those tests are your safety net. They show whether a refactor kept the behavior, and they become the specification if you later replace a part.
Replace in parts, not all at once
The Strangler Fig approach replaces a system gradually. You find seams where the system can be split, move one piece of behavior at a time into new code, and keep the old and new parts running side by side until the old part is no longer needed. The temporary code that connects them can feel like waste. Fowler argues that the lower risk and earlier value are worth it.
For an AI-built app, a seam might be the payment flow, the admin area or one background job. Each piece can be rebuilt, tested against the captured behavior and shipped on its own.
One more warning from Fowler applies directly: if the process that produced the mess does not change, the new code will end up in the same state. If prompts went straight to production without review, the rebuilt parts will degrade the same way. Changing that process is part of the work, and it is covered on our AI coding adoption page.
What this means for your project
A full rewrite is rarely the right first move. A practical order:
- Write down the outcome you need.
- Classify the problems: structure, performance, style or data model.
- Add tests on the critical paths before changing anything.
- Fix SEV-1 issues first, such as open data access or exposed keys. Our production readiness checklist and the note on Supabase row level security list what to check.
- Then replace the worst parts one at a time.
A rebuild can still be the right answer, for example when the data model is wrong at the core. The decision should follow an assessment of the code, not the mood after a bad week. That assessment is where an AI code rescue starts.
Unsure whether your codebase needs refactoring or a rebuild? Book a free intro call and we will tell you honestly which path makes sense and what it would involve.