Back to Blog
Development

Refactor, Rewrite, or Leave It Alone

You open a file. It's ugly, or it's slow, or it's a little scary, or it's just old. You have to decide what to do with it, and you really only have three choices: refactor it, rewrite it, or leave it alone.

Most engineers reach for the wrong one, and they do it by temperament rather than by reason. The eager ones rewrite, because a clean slate feels better than someone else's mess. The cautious ones never touch anything, because the last time they did, something broke at 2 a.m. Both are sometimes right and often wrong.

The senior move isn't a preference. It's knowing which of the three the situation in front of you actually calls for. After thirty-five years of opening other people's files, here's the field guide I wish I'd had at the start.


The Most Underrated Option: Leave It Alone

Working code you didn't write and don't fully understand is still working code.

That sounds obvious until you're staring at something genuinely bad and your hands are itching to fix it. But here's the asymmetry that should stop you: the bugs that code has today are known. The bugs your change introduces are not. You would be trading a quiet, understood liability for a fresh, unknown one, and calling it an improvement.

"If it ain't broke, don't fix it" gets mocked as the motto of people who are afraid of their own codebase. Mostly, it's right. Leave code alone when it works, it's stable, you're not actively changing it, and a defect would be expensive.

The thing to be honest about is the ego trap. Engineers want to improve code because they can, not because they should. And a lot of what looks like ugly code is ugly precisely because it absorbed years of edge cases, hotfixes, and "oh, except when..." moments that you have long since forgotten. The ugliness is scar tissue. Cut it out and you reopen every wound that made it.


The Default: Refactor

For code you actually are working in, refactoring is the right answer almost every time.

Refactoring has a precise meaning, and it's worth holding to it: you change the structure of the code without changing what it does. Same behavior, better shape. Done properly, it's small, continuous, and safe, carried out under a test suite that screams the moment behavior shifts.

The discipline is simple to state and hard to keep. Work in tiny steps. Keep the tests green the whole way. Never mix a refactor and a behavior change in the same commit, because the day something breaks, you want to know which kind of change broke it. And leave each file a little better than you found it, the way a good campsite gets left cleaner than you arrived.

Reach for a refactor when three things are true: you need to change the code anyway, you understand what it does, and you can prove you didn't break it.

The trap here is the refactor that is secretly a rewrite. It starts as "let me just clean this up" and becomes a branch that grows for three months, touches everything, and never merges. If your refactor can't ship in small pieces, it isn't a refactor anymore. It's the next section.


The Rare, Justified Rewrite

Rewrites are seductive, and they are usually a mistake.

The definitive warning is twenty-plus years old and still true. In "Things You Should Never Do," Joel Spolsky argued that rewriting a working system from scratch is the single worst strategic mistake a software company can make, because you throw away embedded knowledge. Every weird line in that old code is a bug fix, an edge case, or a lesson someone learned painfully. Start over and you get to learn all of them again, the hard way, in production.

Two more forces work against you. The second-system effect means the rewrite quietly inflates in scope, because now you'll do it "right." And the "it's a mess" illusion fools you, because the mess often is the accumulated reasons, the ones you can no longer see from the outside.

And yet rewrites are sometimes correct. When the platform is genuinely dead, an unsupported runtime you can't hire for or secure. When the architecture is structurally incapable of what the business now needs, not ugly but incapable, where no amount of refactoring gets you there. Or when the honest cost of understanding the old code exceeds the cost of rebuilding it. (At the scale of a whole system rather than a single file, that same call is the subject of Legacy System Modernization: A Practical Guide.)

Here's the test I use. Can you name, specifically, what the rewrite will do that a refactor cannot? If you can articulate it, you might have a real case. If the best you've got is "it'll be cleaner," you have an ego rewrite, and it will hurt.


The Decision, in One Place

Strip away the war stories and the call is fairly mechanical.

Choose when Cost & risk What AI changes
Leave it It works, it's stable, you're not changing it, and defects are expensive Lowest Cheaper to understand it first, so leaving it is a choice, not laziness
Refactor You're changing it anyway, you understand it, and you can test it Medium Safer and cheaper: bulk mechanical edits, plus AI-written tests
Rewrite The platform is dead or the architecture is structurally incapable, not just ugly Highest Cheaper to build, but the embedded-knowledge trap is unchanged

The meta-rule that ties it together: prefer the cheapest reversible option. Leaving code alone is cheaper and less risky than refactoring it, which is cheaper and less risky than rewriting it. Earn your way up that ladder only when the situation forces you, never when your taste merely invites you.


How AI Moves the Line

Everything above predates the current moment, and none of it has been repealed. What AI-augmented development changes is the cost of each option, not the principles underneath.

Refactoring got safer and cheaper. The thing that always made refactoring dangerous was missing tests, and the thing that always made tests tedious was writing them. AI does both of the boring parts: it performs mechanical refactors in bulk and it writes the characterization tests that let you refactor without holding your breath. This is most of what building software with Claude Code looks like in practice. The oldest excuse in the trade, "I can't safely refactor this, there are no tests," is gone.

Leaving it alone got cheaper to revisit. A model can read a gnarly, undocumented file and explain it to you in a few minutes. So "we don't understand it" is now a much weaker reason to either avoid the code or nuke it from orbit. Understand first, then decide, used to be expensive. Now it's nearly free.

Rewriting got cheaper too, and that's the one to watch. I made the case in Custom Software Is Cheaper Than You Think Now that AI has genuinely compressed build costs, and it has. A rewrite that would have been nine months might be six weeks, which means more rewrites now clear the cost-benefit bar than used to.

But cheaper to build is not the same as safe to replace. AI can reconstruct the code quickly. It cannot reconstruct the embedded knowledge that was never written down, and it will rebuild your system confidently without the two hundred edge cases nobody documented. That's the brain-fry, broken-code failure mode I wrote about in The Real Cost of Moving Fast with AI, and AI makes it easier to hit, not harder, because the speed carries you past the warning signs.

So the net is double-edged. Revisit the decision more often, because all three options got cheaper and the old answers deserve a fresh look. But hold the line on the rewrite trap harder than ever, because the thing that made rewrites dangerous was never the typing. It was the knowing.


Knowing What to Leave Alone

The hardest skill in this job was never writing the code. It's knowing what to leave alone.

It takes a kind of discernment that mostly comes from having been burned: the serenity to not improve what doesn't need improving, the courage to rebuild the rare thing that genuinely can't go on, and the wisdom to tell the two apart. Most of the damage I've watched get done to software over three decades didn't come from too little change. It came from change applied in the wrong place, for the wrong reason, by someone who could and therefore assumed they should.

The senior engineer is just the one who learned, usually the hard way, that "I could make this better" is not a reason to touch it. When in doubt, choose the cheapest reversible option, and leave the scar tissue alone until it actually gets in your way.

Share on LinkedIn