When Not to Refactor Legacy Code
Not all legacy code should be touched. Learn when refactoring legacy code is a waste of budget and when leaving ugly, stable code alone is the right call.
Most legacy code should be left exactly where it is. The instinct to refactor everything ugly is expensive, risky, and usually pointed at the wrong files. Legacy code that is ugly but stable, rarely touched, and doing its job is not a problem you have. It is a problem you would create by touching it. Every refactor carries a chance of introducing a new bug into code that currently works. If the code is not costing you anything, that trade is pure downside. Knowing when not to refactor is a more valuable skill than knowing how to refactor, because it is what stops you from burning your budget on vanity.
Leave it alone when churn is low
The single best predictor of whether refactoring pays off is how often the code changes. Code you never touch cannot slow you down, no matter how tangled it is. A 2,000-line module written by someone who left years ago, full of patterns nobody uses anymore, is genuinely fine if you have not needed to modify it in two years and do not expect to. Its ugliness has zero carrying cost because you never pay it. I explain the underlying model in Churn vs Complexity: Find Your Real Hotspots: complexity is only dangerous when multiplied by change. Low churn zeroes out the product. Leave it.
The mistake is refactoring the code that annoys you rather than the code that costs you. The most annoying code is often the low-churn, high-complexity file, because you remember the one bad afternoon you spent in it. But one bad afternoon a year is not a business problem. The quiet file everyone edits daily is.
Leave it alone when it has no test coverage and works
This one feels backward, so hear me out. Untested legacy code that works in production is not a candidate for casual refactoring. It is the most dangerous thing to touch, because you have no safety net and no specification of what "correct" even means beyond "what it currently does." If you must change it, you write characterization tests first to pin down the existing behavior, then refactor. If you are not willing to invest in that safety net, you are not willing to refactor it safely, which means you should not refactor it at all. The correct move for scary untested code you do not need to change is to not change it.
Leave it alone when it is scheduled to die
Do not refactor code you are about to replace or retire. If a module is on the roadmap for a rewrite, a strangler-pattern replacement, or deletion because the feature is being sunset, every hour spent cleaning it up is thrown away. I see teams polish code that a planned migration will delete in two quarters. That is worse than doing nothing, because it also delays the migration. If the plan is to strangle it out, as I describe in the strangler pattern for legacy code, spend the effort on the replacement, not the corpse.
Leave it alone when the payoff is aesthetic
"This would be cleaner" is not a business case. Cleaner for whom, at what cost, saving how many future hours? If you cannot answer those, you are refactoring for taste, and taste is not free. The bar for touching working code should be a real, defensible return: this file is a hotspot, it slows every release, the fix pays for itself in weeks. Absent that, the honest answer is that the code offends you and you want to fix it, which is a want, not a need. I hold this line hard across my portfolio because I do not have spare hours to spend on prettier code that ships nothing. The default answer to a discretionary refactor is no.
When it actually is time
To be clear about the other side, refactoring is worth it when the numbers line up: high churn, high complexity, real bugs concentrating in the file, active feature work blocked by the mess. That is the hotspot quadrant, and it is where your refactor budget belongs. The discipline is not "never refactor." It is "refactor only where the data says the payoff is real," which is the whole point of running repo intelligence before you refactor.
This is why I run the numbers before touching anything. A tool like ReformCode surfaces the files that actually cost you, ranked, so the refactor conversation is about evidence instead of which code someone finds ugliest. The ranked list does as much by telling you what to leave alone as by telling you what to fix. Most of the repo, correctly, will be on the leave-it-alone list.
Ugly and stable is a perfectly good state for code to be in. Do not confuse discomfort with cost. If the file is not on your hotspot list, is not blocking work, is not a security risk, and is not slated for replacement, the right refactor is no refactor. Save the budget for the handful of files where it actually buys you something.