When to rebuild instead of refactor.
We've made the wrong call on this twice. Here's the heuristic we use now.
We've made the wrong call on this twice. Here's the heuristic we use now.
In 2023 we inherited a codebase from a client who'd parted ways with their previous agency.
The app worked. It had users. It had revenue. It also had no tests, three different state management approaches in the same frontend, a backend that stored critical business logic in the database as raw SQL strings, and a deployment process that consisted of one engineer SSHing into a server and running a bash script they'd written from memory.
We said we'd refactor it. We were wrong.
Eighteen months and a lot of unnecessary pain later, we'd rebuilt it. The rebuild took eleven weeks. If we'd done that first, we would have saved nine months and a significant amount of client money.
That was mistake one.
The second mistake went the other direction. A client came to us with a mobile app that had been built in React Native. The app was two years old. They wanted native iOS and Android.
We recommended a rebuild in native Swift and Kotlin. The reasoning was sound: better performance, better platform integration, better long-term maintainability.
The rebuild took five months, cost twice the original estimate, and shipped to users who couldn't tell the difference because the original React Native app had been well-built and the use case didn't require native performance.
We should have refactored.
After making the wrong call in both directions, we built a checklist we use now. It's not perfect. No heuristic is. But it's much better than "this feels like a rebuild situation."
Rebuild if three or more of these are true:
Refactor if most of these are true:
Selective rebuild. Identify the two or three components that are genuinely unsalvageable and treat them as greenfield. Leave everything else alone.
This is almost always the right answer when the overall architecture is sound but one layer — usually the frontend, or one particularly bad service — is causing all the pain.
On a fintech project last year, the backend was solid: well-structured, tested, deployable. The frontend was a jQuery-era disaster that had been incrementally modified for four years and had accumulated enough technical debt to constitute a structural hazard.
We rebuilt only the frontend. The backend was untouched. The rebuild took six weeks and cost a fraction of what a full rewrite would have. Users noticed the improvement immediately. The backend engineers had nothing to change.
Before you decide anything, ask: what is the actual cost of the current situation?
Not the emotional cost. Not the "it makes me uncomfortable to read this code" cost. The actual business cost, in hours per week, of the current state.
If the engineering team is spending four hours a week working around technical debt, that's worth quantifying. Four hours a week over a year is 200 hours. If a rebuild would take 400 hours, it pays for itself in under two years. If the debt is costing two hours a week, the calculation changes.
Most rebuild decisions we've seen were made on aesthetic grounds — "this code is embarrassing" — rather than economic ones. Embarrassing code that works and doesn't slow the team down is not a rebuild candidate.
This is the part that requires honesty.
Telling a client "we recommend a rebuild" is easy if the relationship is strong. They trust your judgement. They understand the reasoning.
It's much harder when the recommendation is "you need to spend six months and a significant budget rebuilding something you already own." Clients hear: "the last agency you worked with wasted your money."
We always frame it the same way: "here is what the current codebase costs you per month in engineering time, here is what a rebuild would cost, here is when it would pay off." That conversation is about economics, not aesthetics. Clients can make an informed decision from economics.
The worst outcome is a rebuild recommendation accepted on trust, delivered late, and questioned when the bill comes in. Show the math first.
If you decide to rebuild, resist the temptation to rebuild everything at once.
The scope of a rebuild grows in proportion to how long you spend looking at the old codebase. Every engineer will find five more things that need changing. Most of them are right. Collectively, they'll turn an eight-week rebuild into a six-month one.
Decide what the rebuild must include. Write it down. Review every addition against it. Ship the minimum version first.
The refactors can happen afterward.