Refactoring Revisited
Many people don't understand refactoring, and this leads to several anti-patterns.A couple of days ago I talked about refactoring. Based on some of the feedback I received, I think I may have made my point a bit too opaque. I think some people thought I was taking a stand against refactoring. That’s not at all the point I wanted to make, and in retrospect I think I may have made a few logical jumps when I wrote that piece. So today I want to explain what I believe “refactoring” means, why it’s important, and how my thought process lead to the topic of the previous post: Refactoring itself is worthless.
What is refactoring?
I like Martin Fowler’s definition:
a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.
I think this definition is pretty clear, and pretty precise. But just to drive the point home: Refactoring is not bug fixing. It is not improving performance. It is not adding a new feature. It is not applying a security patch. Refactoring is making structural changes to software, for the sake of making it easier to do later changes.
Putting this another way: When you compare a snapshot of your codebase before and after refactoring, there should be no difference in value to the end user. If there is a difference in value to the end user, then the thing you did was not an honest refactoring. This is the point I was trying to make in the previous point.
But why does it matter?
There are several reasons to change code. Refactoring is only one of them. And it’s often (nay, usually) useful to limit each commit or PR to a single reason for change. That is, it’s a good idea to not include a refactoring and a bug fix in a single commit or PR, for example.
But a broader reason this matters is that confusion about refactoring often leads to a few planning anti-patterns. In Agile software development, “Our highest priority is to satisfy the customer through early and continuous delivery of valuable software.” Refactoring, by itself, does not delivery valuable sotware to the customer. This is not a reason to avoid refactoring, but it ought to affect our attitude about refactoring.
Refactoring anti-patterns
One common anti-pattern I see related to refactoring is the idea that refactoring should be planned and scheduled separately in the product backlog. Now maybe some refactorings deserve separate backlog items. If you’re refactoring to replace one MVC library with another, maybe a backlog item (or epic) is appropriate. But this type of refactoring is the exception that proves the rule, in my opinion.
Most refactorings are (or should be) small, and are done to facilitate adding a new feature or fixing a bug. These types of refactorings should simply be done as part of the story to add the feature or fix the bug. There’s usually no reason to have a refactoring story that the user-facing story depends on.
A related refactoring problem I see is that of postponing refactorings, and even “asking permission” from product management to do it. This might seem appropriate, if refactoring provides no user-facing value. Shouldn’t we ask permission to divert our efforts from providing user value?
No. We shouldn’t.
The point is not to do refactoring, or to postpone it.
The point is to treat refactoring as part of the daily maintenence work we do.
Refactoring code is like sweeping the floor, or any other maintenence or upkeep task at any shop.
No customer pays a shop keeper to sweep, but if you never sweep, your ability to serve customers is severely hampered.
I believe that in a sense, many of us put too much emphasis on refactoring, by treating it as any other work. This leads us to compare refactoring tasks to user-facing tasks, and in such a comparison, we’ll practically always choose the user-facing task over refactoring. The irony, then, is that by over-ephasising refactoring, we push it down the priority list.
Healthy refactoring
So my attitude toward refactoring, and I think the healthy way to respond to refactoring, is to do it constantly, all the time, in small chunks, and as part of our daily work. Except in rare, extreme cases, refactoring should simply be assumed, and not exceptional—the same way that sweeping the shop floor is assumed. If you find yourself asking permission, or creating separate refactoring user stories, these are signs that you’re not doing refactoring properly.