In a Thoughtbot article, Joe Ferris asks “how much should I refactor?“. The original XP practices have an answer of sorts: “mercilessly“.
Quoting Ron Jeffries:
One of the ExtremeProgramming practices is to RefactorMercilessly. When you find two methods that look the same, you refactor the code to combine them. When you find two objects with common functionality, you refactor to make there be just one (see ParameterizeMethod). Extreme projects do not use BigDesignUpFront. Therefore they upgrade their designs continuously. RelentlessTesting and ContinuousIntegration (UnitTests synchronized with changes disseminated to other engineers) permit changes that would introduce the risk of bugs in slower projects.[…]
Since we DoTheSimplestThingThatCouldPossiblyWork, sometimes what we do needs improvement an iteration or so down the line. We welcome these opportunities to make the system more like what it should be, and we welcome the fact that we do it in solid knowledge of what is really needed, not what we imagined in the past was needed.
The result of this is that the system is always as simple as we can make it, which means we can understand it better, which means we can change it more rapidly while keeping it reliable.
When I asked Sandi Metz how long a refactoring should take, she said:
Oh, like… a minute? …to tear a class apart into two classes takes a little bit of time, 10 minutes or so.
This is consistent with my own experience, and, I believe, with the intent of the original Red Green Refactor cycle. I think it’s also consistent with the view DHH expressed when the RubyRogues interviewed him: wait until the last minute, until you feel the pain, and then refactor. Immediately.
I’ve watched the meaning of the word “refactoring” drift over the past several years. I don’t know if this is specific to the Ruby community or a larger problem in the software industry. But on nearly every project I’ve been on, I’ve heard statements like this in iteration planning meetings:
Oh, this feature is going to touch the Frotz class. We’ll need to add some time to the estimation. The Frotz class is a huge mess, we really need to schedule time to refactor it.
Nearly every project has had areas which everyone recognized as painful. In fact, everyone had been recognizing those areas as painful for weeks or months.
If there’s an area of the code that everyone has recognized as painful for weeks, the time for refactoring has been and gone. That battle is lost. It’s not called “Red Green Red Green Red Green Spend A Whole Day Refactoring”. We’re not talking about refactoring anymore; we’re in Code Refurbishment territory now:
developers are actually talking about a much more extensive structural redevelopment technique that does not have a common term. These structural changes are often not a complete ground-up rewrite because much of the existing code will be reused.
The refurbishment process may consist of many small refactorings. But it’s likely to be more than that: refactoring always takes place in the context of test coverage, and often refurbishment involves, first, making the code more amenable to testing and then adding new tests.
Does terminology matter? I think it does, because I’ve also observed that true refactoring, and the easy, pleasurable coding experience that accompanies it, is becoming something of a lost art. When we talk about “scheduling some refactoring time for the next iteration”, we tell novice programmers (as well as ourselves) that refactoring is something that can be put off. And over time, we lose (or never discover) the Red-Green-Refactor rhythm.
When we chose agile methods, we made a deal with ourselves and our stakeholders: we’ll trade the supposed assurances of careful up-front design for constant, iterative just-in-time design. Refactoring is where much of that minute-by-minute design happens. When we have “skeleton closet” classes which have become everyone’s pain point for weeks on end, it means we reneged on the deal. Now we are doing the same periodic Big Rewrites that were the hallmark of BDUF. And our stakeholders become understandably leary of this “refactoring” business. Quoting Martin Thompson again:
The reason the business folk have come to recoil is that they fear we are about to head off into uncharted waters with no idea of how long things will take and if any value will come out of the exercise.
I think that for our own happiness and for the health of our projects, if we’re going to keep practicing this “agile” or “lean” stuff we need to make a conscious effort to restore refactoring to its original place, right in the heart of the rhythm and flow of coding. In order to do that, several factors are required:
- We must be hypersensitive to pain. If we’re noticing a lot of churn in one class over a period of months, that’s too late. And it’s not enough to be able to say “hm, these two methods look similar, maybe I should DRY them up”. We need to be able to make deeper observations such as: “I just changed three methods in order to support one new field. That’s Shotgun Surgery. What can I do to avoid that?”
- We must have comprehensive, reliable, fast automated tests. If we feel a moment’s hesitation before refactoring, either because the we’re not confident that the tests will keep us from accidentally changing behavior; we’re concerned our changes will require tests to be fixed as well; or we just don’t want to wait for the tests to run to verify our changes, that’s the death knell for our refactoring practice.
- We must have a license to refactor. The project’s team culture must establish a “safe space” for refactoring. It cannot be a dirty word, something we feel a little bit guilty about every time we take a minute to extract a method.
- We must have good tools. The people who came up with the idea of refactoring also came up with the Smalltalk Refactoring Browser, a point-and-click environment for automating common refactorings. If we are to make refactoring habitual, at the very least we need editors which make it easy to automate frequent tasks, and the willingness to learn to use them effectively.
I’d love to end this with a list of resources for mastering the art of refactoring. One that I always recommend to Ruby programmers is Refactoring in Ruby, a workbook companion to the Refactoring, Ruby Edition (those are Amazon affiliate links). Unfortunately, I’m out of time for writing. So I’ll open the floor: what book/article/screencast/presentation helped you understand refactoring better? Leave a link in the comments!
“A Refactoring Story” (https://www.destroyallsoftware.com/screencasts/catalog/a-refactoring-story) from Destroy all Software really helped me get on the right track.
Awesome, thanks for the recommendation!
Considering non-incremental/time-sucking refactoring to be refurbishment is a great way to look at the problem and could potentially help with the situation a lot of places get into where refactoring becomes a dirty word. That is most definitely not just a ruby thing. Great post.
Great article! I’ve just started reading “Growing object-oriented software, guided by tests”, and in one of the early chapters it describes refactoring as involving relatively simple and safe transformations that you can do iteratively to facilitate redesign, but makes the point that refactoring should not be directly treated as a redesign on its own.
In my own self-study on TDD, I found that one of the most horrible time-sucking activities I got in the habit of was gutting entire subsystems and then spending tons of time “refactoring under the red”. Once I noticed that pattern, I started to take a step back whenever that temptation came up, do a bit of redesign on a whiteboard about the high level changes that needed to be made, and then proceeded with lots of small, incremental changes to speed up the feedback cycle. That worked really well, and it tells me that even if a sudden major shift in design does force you to do “the big refactor”, there is a way to pretend as if you were making several small changes instead of a big one.
Another technique I’ve found that helps is when you know a big redesign is necessary, but you don’t know exactly how it will affect they system, you can start by making small incremental improvements to the objects you think the “big change” will effect. Sometimes that works so well that you end up pushing pebbles around instead of boulders, other times, it just clears away some debris to give you a cleaner shot at the core problem. Either way, it works out better than taking the problem head on and trying to make all your changes at once.
Thanks! And all very good points.
This one would be useful: http://www.amazon.com/Refactoring-Improving-Design-Existing-ebook/dp/B000OZ0N4Y
I like to think of it as remodeling (probably because I grew up with a father who is a contractor). And I agree that calling virtually any change to existing code “refactoring” is common in every environment where I have worked. And that is why managers fear the word.
Code refactoring, up http://i.imgur.com/wGUTG.gif