Rewrite vs. refactor decision
The technical and organizational framework for deciding whether to rewrite a system from scratch or refactor it incrementally β what signals to look for and how to justify the decision.
What the Prompt Is Testing
A prompt such as, "Tell me about a time you chose between rewriting and refactoring," tests whether you can make a high-cost technical decision with evidence rather than taste. It is looking for:
- Technical judgment β Can you separate a code smell from a structural blocker?
- Risk management β Can you preserve behavior, plan a migration, and keep delivery moving?
- Trade-off communication β Can you explain cost, reliability, speed, and organizational impact?
- Ownership β Can you state what you personally analyzed, decided, and measured?
The question isn't "is this code bad?" Code is always bad. The question is: "Can the current codebase be improved incrementally to meet our needs, or has it accumulated constraints that make incremental improvement impractical?"
30-Second Opening
Lead with the decision, the evidence, your responsibility, and the outcome. Replace the brackets with facts from your own work:
In [system], we needed to [capability or change], but [specific constraint] made
the choice difficult. I was responsible for [analysis or decision scope]. I
compared [refactor option] with [rewrite or migration option] using [evidence],
chose [decision], and the result was [measured or observable outcome].
This is a speaking template. If you are describing what you would do, label it as hypothetical; do not turn a design exercise into a claim about past work.
When Refactoring Is Correct (often the starting point)
Refactoring is often the starting point because:
- Working software encodes discovered behavior that no design document captures. A rewrite will lose much of this.
- A rewrite may not create user-visible value on its own. Engineering time spent on replacement work is time not spent on features or reliability improvements.
- Rewrite estimates are easy to understate because the old system still needs maintenance while the new system rediscovers behavior.
- During a rewrite, the old system still needs maintenance. You're paying double.
Signs that incremental refactoring can work:
- The core data model is sound; the problems are in the service layer
- You can add tests to the existing code and use them as a refactoring harness
- The new behavior you need is additive, not a replacement of fundamental assumptions
- The coupling is local, not systemic
When Rewriting Is Justified
Rewriting is justified when the existing system has one or more of these properties:
1. The data model is wrong at a fundamental level If the data structure encoded in the database is wrong, every query, every business rule, every API contract is built on the wrong foundation. Adding features requires working around the wrong model, and the workarounds accumulate.
Example: A payments system built on a ledger model where debits and credits
are in separate tables and linked by a mutable "reconciled" flag. Adding
multi-currency support may be unsafe without a different data model β
changing the ledger structure incrementally would require careful compatibility
and reconciliation work.
2. The runtime environment is being retired Legacy language runtime (Python 2, Ruby 1.9, Java 6), framework (Rails 3), or platform (Heroku Cedar-14) approaching end-of-life forces a migration. Sometimes that migration is larger than the refactor that would have been needed anyway.
3. Incremental change is structurally blocked
Signals:
- Every PR takes 2+ weeks because test suite takes 3 hours to run
- Adding a new feature requires changing 8 separate files due to coupling
- The deployment pipeline is so fragile that releases require 4 people
- You cannot run the system locally; developers test in staging only
When the development environment is so degraded that incremental change is more expensive than rewrite, the cost-benefit shifts.
4. The team maintaining it can't understand it anymore "Tribal knowledge" debt. If the system can only be maintained by 2 specific engineers because no one else can reason about its behavior, the risk of those engineers leaving (or the bus factor) is a legitimate business risk.
The Incremental Rewrite: The Safe Path
When you do justify a rewrite, the safest approach is the strangler fig pattern:
You migrate one feature at a time. At no point is there a cutover where "everything switches at once." The new system earns trust through production traffic before it handles the full load.
A big-bang green-field replacement has a wide blast radius: rollback, behavior comparison, and learning all become harder at the same time. If a single cutover is unavoidable, state the validation, rollback, and staged exposure controls explicitly.
The Justification Document
If you're proposing a rewrite, you need a clear written case. The document should answer:
1. What is wrong with the current system, specifically?
(Not "it's messy." What failure modes does it create? what slowdowns?)
2. What have you tried incrementally, and why did it fail?
(Shows you didn't jump to rewrite as first resort)
3. What is the scope of the rewrite?
(What gets rewritten? What stays? What's out of scope?)
4. What is the migration path?
(Strangler fig? Shadow reads? Cutover? When do users notice anything?)
5. What does "done" look like?
(Concrete criteria, not "when it feels better than before")
6. What is the risk if the rewrite runs over?
(What's the plan if it takes 2x longer than estimated?)
Build a 2β5 Minute STAR Answer
Use one real decision and replace bracketed details with evidence from your own work. A design exercise can show your reasoning, but label it hypothetical instead of presenting it as a past rewrite.
Situation (30β45s): What was [system], what problem did it create, and what made the choice difficult? Include the [users, workflow, or business constraint] that mattered.
Task (20β30s): What were you responsible for deciding or influencing? State the goal and constraints: [support capability], [retire runtime], [reduce delivery risk], or [make the system supportable] by [time or milestone].
Action (1β3 min): Explain your analysisβwhat did you find, why was refactoring insufficient or sufficient, and what data supported the decision? Then cover how you got alignment, who was skeptical, the migration or refactoring approach, and what was harder than expected.
Result (30β60s): Where is the system now? Name the measured or observable result, such as [delivery time, reliability, migration progress, or support burden], and one thing you would do differently. If the work is still underway, say what has and has not been proven.
This outline takes about 3β4 minutes. Spend the most time on the evidence and trade-offs, not on defending a personal preference for rewrites or refactors.
Choose and Adapt a Real Story
Choose a real decision where you can show your own contribution:
- you identified a concrete delivery, reliability, data-model, or platform constraint;
- you compared incremental change with a larger replacement using evidence;
- you influenced the decision or owned a defined part of the work; and
- you can describe what happened afterward, including what remains unproven.
You do not need a story in which you chose a full rewrite. A refactor, runtime migration, staged extraction, or decision to defer a rewrite can all demonstrate the same judgment. Focus on why the options differed in your context.
If you have no direct rewrite decision, use this bridge: "I have not led a full rewrite. My closest example is [real change], where I compared [actual options] and owned [actual contribution]. Based on that experience, I would evaluate a rewrite by [hypothetical criteria]." Keep the final clause hypothetical and do not claim results you did not measure.
Follow-up Questions and How to Answer
"What evidence would justify a rewrite?" Name the structural constraint, the incremental approaches you tried or ruled out, the cost of staying put, and the migration risk. "The code is ugly" is not enough.
"Why not refactor the data model gradually?" Explain the compatibility, dual-write, backfill, or reconciliation risks and what boundary would make an incremental path safe.
"How would you protect users during the migration?" Describe feature flags or routing, shadow reads or comparison, validation metrics, staged traffic, rollback, and ownership of the old and new paths.
"How did you get alignment?" State which stakeholders had different incentives, what evidence you shared, and which decision criteria resolved the disagreement. Do not imply that consensus existed if it did not.
"What if the rewrite takes twice as long?" Give the fallback: reduce scope, keep the old path healthy, pause at a safe boundary, or return traffic while the team reassesses the economics.
"How do you know the decision worked?" Point to the agreed success criteriaβ[deployment time], [failure rate], [migration coverage], [support effort], or [runtime support]βand distinguish measured results from expectations.
Common Interview Mistakes
- Treating "rewrite" or "refactor" as a universal rule instead of a context-dependent choice.
- Calling the code bad without naming the behavior, constraint, cost, or risk that matters.
- Quoting an absolute statistic or outside authority instead of showing evidence from the situation.
- Ignoring the cost of maintaining the old system, migration validation, or rollback ownership.
- Overclaiming your influence, implementation role, migration completion, or business result.
- Presenting a hypothetical architecture or proposed rewrite as personal history. Use "I did" for actual work and "I would" for an adaptation.
Test Your Understanding
1. Why is refactoring often the starting point?
It preserves discovered behavior and lets the team reduce risk in smaller, measurable steps while the system continues serving users.
2. What makes a rewrite case credible?
A specific structural blocker, evidence that incremental options are insufficient or too risky, a bounded scope, and a migration and rollback plan.
3. What is the safest general shape for an incremental rewrite?
Route a limited feature or slice of traffic to the new path, compare behavior, measure it, and expand only when the evidence supports doing so.
4. How should you describe an unfinished migration?
Separate completed work, measured results, open risks, and next steps. Do not describe a target architecture as if it were already running.
Quick Recap
- Refactoring is often the starting point. A rewrite can lose encoded behavior and is easy to underestimate while the old system remains in service.
- Rewriting is justified when: the data model is fundamentally wrong, the runtime is being retired, incremental change is structurally blocked, or no one understands the system anymore.
- When rewriting, use the strangler fig pattern where possible β migrate one feature at a time and make validation and rollback explicit for any larger cutover.
- Write a justification document: what's wrong, what you tried, scope, migration path, done criteria, and risk if it overruns.
- Maintaining the old system while building the new one adds operational overhead β budget for it and make ownership explicit.
Related Skills
- Refactoring: improving design in small, behavior-preserving steps.
- Migration planning: defining boundaries, validation, rollback, and ownership for change.
- Architecture trade-offs: comparing data, runtime, delivery, reliability, and operational constraints.
- Technical leadership: building alignment with evidence and communicating uncertainty honestly.