
Essay
Refactor or Rewrite? How I Chose the Right Path in a Real-World Project
A structured decision framework — and a small rewrite spike — that led to refactor first, rewrite later on an 8-year-old React app.
Should we keep fixing the car while it's moving or just build a new one from scratch? — Every developer, at some point in life
A few months ago, I was given a challenge in my project: We need to improve performance, modernize the tech stack, fix legacy issues, and enable new features — fast.
Naturally, the classic debate popped up: Do I refactor the existing codebase or rewrite the entire application?
I'd love to say the answer was obvious. But in reality, it took a structured approach, some investigation, and a few hilarious "what-were-they-thinking" code snippets to make the right decision.
Let me take you through that journey.
Step 1: What was the problem?
I was working on a mid-sized React application. It had been around for over 8 years, and it had grown like an unpruned tree:
- Business logic was entangled with UI logic.
- Deprecated dependencies were there like ghosts.
- State management was a mess.
We had technical debt, inconsistent patterns, and performance bottlenecks. Oh, and the ODs (original developers)? All had left.
Step 2: Assess the codebase honestly
I ran through a few questions to get clarity:

Rewrite or refactor: the strategic framework I used
I used this Rewrite vs Refactor Decision Framework:

Experiment time — a small rewrite spike
Before committing, I tried rewriting one feature — the user profile module — as a test.
What I found:
- I spent a lot of time understanding the implicit business rules hidden in the old code.
- I introduced subtle bugs because the legacy behavior wasn't well documented.
- Rewriting a small feature took longer than expected.
This led to a crucial realization:
The fastest way to rewrite a codebase is to first refactor it into something worth rewriting.
I chose: refactor first, rewrite later
We decided to refactor incrementally, with the following strategy:
1. Modularization
We broke monoliths into modules with clearer responsibilities.
// BEFORE - All-in-one React component
const Dashboard = () => {
useEffect(() => {
fetch('/api/user-data')...
}, []);
return (
<>
<h1>Dashboard</h1>
....
<table>...</table>
</>
)
}
// AFTER - Component decomposition
<DashboardHeader />
<DataTable />2. Introduce tests
Started with smoke and unit tests to cover the basics before touching legacy functions.
3. Strangler pattern
For major parts like authentication and file uploads, we rewrote those as separate common services.
What I avoided by not rewriting everything
- Loss of knowledge — The original character of the app would have been lost.
- New bugs — We'd be reintroducing old problems.
- Delayed releases — A rewrite could have taken 6–8 months with little value until the end.
When I would consider a full rewrite
- When the code is unreadable and untestable.
- When the domain has changed drastically.
- When no one understands the current implementation.
- When you want to move from monolith to microservices and the current architecture blocks you.
If the codebase was a house, and you can't enter without risking collapse — maybe it's time to rebuild.
Interesting things I noticed
- A
sleep(1000)in production to "fix" a race condition. - A service method named
doExtract()that handled 4 responsibilities. - A function with 1500+ lines of code, called
processEverything(). - A component that held the whole content of a page.
- Outdated unit tests.
- The same API called multiple times on a page for different components.
Whether to rewrite or refactor isn't just a technical choice — it's a strategic one. It must balance:
- Business priorities
- Developer sanity
- Risk management
- Time to market
My recommendation?
Refactor when you can, rewrite when you must.
Continue the conversation
These ideas live in MentorBridge practice — coaching, podcasts, and classroom sessions that turn students into professionals.
