In my last QCon presentation in São Paulo, I gave the audience my take on refactoring strategies. You can check out my slides from that talk on Slideshare (in Portuguese, but you should be able to follow the pictures and code samples).

One of my main points is that you need to learn how to shift between two levels of thinking when you engage in refactoring, each requiring you to develop different sets of skills. I called them: Mechanic vs. Strategic.

Mechanics is important because you should be doing small, incremental, and opportunistic refactorings all the time while you develop code. It’s not a coincidence that TDD has an explicit step of refactoring after you tests are green. In order to improve the design of your code you should master how to perform these small steps well. A lot of the modern IDEs – especially in static typed languages – provide great support for this and you should learn how to use them well.

On a strategic level, you have to be able to take a step back and understand the big picture of what you’re trying to achieve. While it’s important to know how to perform individual refactorings, it’s more important at this level to know how to compose them in order to achieve the design improvement you desire.

One of the examples I used to demonstrate this, was inspired by a blog entry that Paul Hammant published a few weeks before my talk. I got some feedback that it was hard to follow the code changes on the slides, so I decided to record a small video of me tackling his refactoring experiment.

In the following video, the strategy is not overly complex: extracting a few methods from a single controller into separate controllers. However, you will see how the order of steps you use can affect the refactoring, making your job harder or easier. You should also be able to pick up a lot of the mechanics along the way:

You can find some of my attempts in branches on this Github repository. If you want to try it out for yourself, or to take this experiment further, these are some of the things I would explore:

  • The Upseller class should have it’s own tests, and I missed the @Component and @Scope("request") annotations that would probably be caught by integration tests.
  • Using Mockito or some other mocking framework to mock collaborators, and adding more behaviour to the domain model objects since using toString for testing is not something I would particularly do in real life.
  • Perhaps I could’ve used git stash instead of git reset to save the initial refactoring steps and later reapplying it assuming the merge would be simple.
  • The path I took is not the only one and you can probably explore different refactoring steps and different approaches to get to the goal.

I would like to thank Paul Hammant for coming up with this experiment and for encouraging me to publish this screencast. Please send me some feedback if you think this was useful or helpful for you!

Post to Twitter