Obstacles to Refactoring
When I’m working on some code and decide that there might be a way to clean it up, I reach for my refactoring toolbelt. I think about the different refactorings I know and decide which ones might work. I then do a quick cost/benefit analysis on the before and after state of the code and decide which refactoring - if any - might be best suited.
At least that’s what I’d like to believe I do. This process is often very quick and almost subconscious, and probably driven more by habit than I’d like to admit.
Lately, I’ve noticed that there is another factor that plays into this cost/benefit analysis. It’s not just, “Will the code be better or worse after this refactoring?” That’s certainly an important factor, but there’s also, “How much work is involved in performing this refactoring?”
For single, atomic, Martin Fowler-style refactorings, my programming language and toolset impose a cost on every refactoring, and this cost plays into the tradeoff I make when choosing when and how to refactor. Let’s look at a simple Extract Method as an example. By “simple”, I mean that there are no variable references to untwist or anything of that nature.
If I’m working in Smalltalk, or in an editor that supports automated refactorings, I can extract a method almost as fast as I can think about it:
- Highlight the code to be extracted
- Invoke the Extract Method refactoring (menu or key-combination)
- Type a name for the extracted method
If I’m working in a text editor without such tool support, the sequence is more involved:
- Cut the code to be extracted.
- Type a call to the new method.
- Move to the insertion point for the new method.
- Type a method header for the new method.
- Paste the code.
- Clean up a bit.
Tool support, or a lack thereof, impacts the cost that I perceive for the refactoring.
If I’m working in C++, there’s yet one more step required:
- Add the method declaration to the header file.
The programming language also impacts the cost that I perceive for the refactoring.
These costs go up further if I decide to perform Extract Class. In most languages, extracting a class typically involves creating a new file (or two, in the case of C++). Of course, most Smalltalks don’t have that cost.
“Source code in files. How quaint.” –Kent Beck
Without good tool support, the need to create a new file introduces yet another obstacle to the refactoring.
These obstacles, taken one by one, don’t seem that large. But they tend to build upon each other, and I find that I make different tradeoffs depending on the language and toolset I’m using.
Does that make me lazy? Or just practical?
If I can keep this additional cost in mind in the moment, I can try to factor it out of my analysis and look at the refactorings based strictly on whether the code will be better or worse.
I can also use these obstacles to inform my choice of languages and tools. One of the reasons I love Smalltalk is because of the tool support and the resulting smoothness of the development cycle. For other languages, I’m generally an Emacs fan, but for Ruby I’ll tend to use RubyMine quite a bit because of the additional tool support it provides.
What about you? Do these obstacles to refactoring affect your minute-to-minute refactoring decisions? How about your larger decisions about languages and toolsets?