Jake Worth

Jake Worth

Write Better Code by Knowing When Not To Refactor

Published: August 26, 2021 • Updated: January 04, 2023 2 min read

  • refactoring

Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior. —Martin Fowler

“Easier to understand and cheaper to modify”: those both sound pretty good! Shouldn’t we be refactoring all of the time? It seems like practice with no tradeoff.

When I review code, I often request that changes be removed. Even when the changes are objective improvements that support my personal preferences.

Refactors are not free. In this post, I’ll list a few times when I think a refactor is worse than leaving the code unchanged.

No Tests

Do you have tests? Can you run them? Do you trust them? If you don’t, don’t refactor.

A mentor once told me: “refactoring without tests is not a thing.” In other words, refactoring without tests is not refactoring. It’s taking something that works and rolling the dice until it breaks.

A type system, or a pre-compiled language, will catch some of the mistakes you’ll make while coding. But in a dynamic language like Ruby, without great tests, you’re going to break something.

Refactoring without tests is always a risk.

It’s the Only Change

Is it the only change to a file? If so, probably don’t refactor.

You may find yourself reading a file, start refactoring it, and then realize you don’t need to change it. Should you commit the refactor anyway? Probably not.

When I read a commit message that says “Fixes profile page crashing”, I expect an atomic commit, where everything in that commit supports the goal. Only by closely reading the Git diff can I learn that isn’t true. It’s just easier to assume that everything matters.

It’s Arbitrary

Avoid arbitrary refactors; they waste time and make important changes harder to find.

Here are some changes that probably make the code easier to read, but aren’t defensible by themselves:

  • ❌ Adding or removing whitespace
  • ❌ Breaking up long lines
  • ❌ Renaming variables in trivial ways (_e to _event)
  • ❌ Personal preferences that are not enforced with tooling or conventional to the language or framework

Why? For one, you’re stepping on the Git history. Many developers use Git to understand past decisions. Touch a line of code, and you bury that context under a new, low-value commit. For most developers I’ve met, that puts the context out of reach. Don’t do it casually.

Okay Refactors

So what are okay refactors? If all of these are true, you’re on the right track:

  • ✅ The file must be changed to improve the user or developer experience
  • ✅ There’s test coverage, type safety, or it’s a compiled language. Or the change is so simple that you can’t mess it up (few changes qualify)
  • ✅ The refactor improves the code. This can be subjective, but there are good and bad examples

If you meet all these criteria, go ahead and commit your refactor. One exception to this are tests; refactor them as much as you want.

What are your thoughts on this? Let me know!

Get better at programming by learning with me! Join my 100+ subscribers receiving weekly ideas, creations, and curated resources from across the world of programming.