Why it’s bad to mix refactorings with behavior changes
It adds risk
Probably the biggest reason not to mix refactorings with behavior changes is that it makes it too easy to make a mistake.
When you look at the diff between the before and after versions of a piece of code, it’s not always obvious what the implications of that change are going to be. The less obvious the implications are, the more opportunity there is for a bug to slip through.
When you mix behavior changes with refactorings, the behavior change and the refactoring obscure each other, often making the change substantially harder to understand and allowing for a much greater opportunity for bugs to slip through.
Mixing refactorings with behavior changes also requires you to make your deployment deltas (i.e. the amount of change being deployed) bigger. The bigger the delta, the greater the risk.
It makes bug attribution harder
If I deploy a behavior change that was mixed with a refactoring, and then I discover that the deployment introduced a bug, I won’t know whether it was my refactoring or my behavior change that was responsible because the two were mixed together.
And then potentially I’m forced to do something painful in order to remove the bug, which is to roll back both my behavior change and my refactoring, even though only one of those two things was the culprit and the other one was innocent. If I had committed and deployed these changes separately, there’s a higher chance that I would be able to attribute the bug to either the refactoring or the behavior change and not have to roll back both.
It makes code review harder
When you mix refactoring with behavior changes, it’s hard or impossible for a reviewer to tell which is which. It makes a discussion about a code change harder because now the conversation is about two things, not just one thing. This makes for a potentially slow and painful PR review process.
How to approach refactorings instead
When I’m working on a behavior change and I discover that my work would also benefit from some refactoring, here’s what I do:
- Set aside my current feature branch
- Create a new branch off of
master
on which to perform my refactoring - Merge my refactoring branch to
master
(and preferably deploymaster
to production as well) - Merge or rebase
master
into my feature branch - Resume work on my feature branch
This allows me to work in a way that reduces risk, allows for easier bug attribution, makes code review easier, and generally saves a lot of time and headache.