This site will look much better in a browser that supports web standards, but is accessible to any browser or Internet device.

Anomaly ~ G. Wade Johnson Anomaly Home G. Wade Home

November 28, 2014

Some Thoughts on Refactoring

I recently heard an interview with Martin Fowler on the Ruby Rogues podcast. He pointed out a few things about the process of refactoring that I haven't thought of in a while. Based on that interview and what I've read and learned elsewhere about refactoring in the past few years, I decided there are a few points worth making:

Don't Change External Behavior

By definition, refactoring improves the design of the code without changing externally observable behavior. If you change the behavior, it's not a refactor. It's a bug fix, or a restructure, or a redesign, or a re-architecture. Too many people use the term refactor to mean "I futzed around in the code for a while, but didn't fix any bugs or implement any features." That is not the same thing as refactoring.

When you decide some code needs to be refactored, remember that you don't want anything outside the code you are changing to be able to see any difference. This is part of the reason why unit tests are so important to refactoring. Without good unit tests, how are you going to know if anything has changed?

Most Refactoring is Small

In the interview above, Martin Fowler explicitly stated that small refactorings are good. If you look at the definitions in the Refactoring book, you'll notice how small each change really is. You might wonder how any change could make a difference. Experience among a large number of programmers has shown that small changes can incrementally improve the code. It's slower than a big bang rewrite, but it's also much safer. Since each change is small, the risk of a major failure is also small.

Another advantage of small refactoring that many don't notice is that each successful refactor improves your understanding of the code. More understanding leads to better changes. It's reasonable to suppose that a series of changes based on increasing knowledge can result in better code.

When a Refactor Breaks

In the same interview, Martin Fowler also made a point that I don't believe I've ever heard before. If you refactor some code and it breaks the tests, don't debug your refactoring. Throw it away, instead.

If your refactoring changes are small, it won't cost you much time to throw it away. More importantly, if you throw away the change and try again, the time you spend on the change is well-defined and limited. On the other hand, we all know that debugging can be an unbounded, hard-to-estimate time-sink.

If you broke code with a refactor, that probably means at least one of the following:

  • You made too big a change in one step
  • You didn't actually understand the code as well as you thought
  • You didn't properly account for side effects of your change

If you think about it, the first one of those is obviously best solved by trying again, but aiming for a smaller change. The other two are most likely in the realm of unbounded debugging sessions.

Always make your refactoring changes as small as possible and be prepared to back up if something goes wrong. Good version control helps with this.

Refactoring and Tools

Some people will make the argument that an IDE with refactoring support is required to actually refactor code. This ignores the fact that those tools did not exist until programmers had proven that refactoring was helpful, which pretty much required them to work without tools. Many developers applied the recipes from the Refactoring book by hand for years without any tool support.

Don't get me wrong, tools that automate the mechanics of refactoring does simplify the process. Having the boring, mechanical parts of a simple refactor handled by your editor-of-choice definitely makes you more likely to make those changes. But, it's possible to refactor without any tools. The most important part of the process is that you think about how you are improving your design, not about which tool does the changes. Just as importantly, you should be able to apply refactorings that your tool does not support. The thinking behind the change is the important part.

Conclusion

The programming community has been aware of the concept of refactoring for years now and the practice has become much more wide-spread. Like any other practice in programming, it is often necessary to go back and re-assess what we know and how we practice. Hopefully, some of these points will help you do just that.

Posted by GWade at November 28, 2014 08:10 AM. Email comments
Comments

that refactoring is like fnissolg, everyone knows it is good to do it, but no one does it).The metaphor reminds me of the one that the suggest we use to explain to our managers that the longer we defer repaying technical debt the more interest we end up paying:Think of code that needs refactoring as a growth . Removing it requires invasive surgery. You can go in now, and take it out while it is still small. Or you could wait while it grows and spreads but removing it then will be both more expensive and more dangerous. Wait even longer, and you may loose the patient entirely.

Posted by: Yvette at November 9, 2015 02:35 PM