Sunday, May 6, 2012

Why We Refactor

I have always enjoyed Mike Taylor's Reinvigorated Programmer, probably because he so obviously enjoys several things I also enjoy (Doctor Who, programming, Buffy The Vampire Slayer, sushi). I was perusing some of his posts from the last year and came across the following comment regarding the process of refactoring code:
"the purpose of all that shuffling is really only to get the deck clear so we can concentrate properly on the hard bits without getting distracted by crud".


I think this is missing the point. Refactoring give us tools for rigorously transforming code. There are several reasons why this might be useful. One reason is that the programmer before us was an incompetent buffoon  Another reason is that we work in and with teams, and collaborative work tends to get messy with time. We need to perform housecleaning so the technical debt doesn't cause us to go bankrupt.

These reasons are largely in line with what Mike is suggesting, I think. However, there's another reason to perform refactorings on a regular basis, and it is crucial to how we work as programmers.

The code you wrote six months ago (or six weeks ago) solved one particular problem pretty well. This is probably not the exactly problem you're trying to solve anymore. The requirements may have changed (accounting suddenly really _does_ want to run reports against itemized receipts), or maybe you just saw a better way to code the whole thing. Now you've got to pull all the affected abstractions up to the new world order. Classes start to fit together differently. Design patterns get replaced by different patterns, or disappear altogether. Engines suddenly find themselves servicing wholly new systems.

The way forward is with the refactoring tools. Instead of rewriting the system to conform to our new way of thinking, we can transform it. We can mold it so it continues to fit our needs. And we prefer this to a rewrite because it is less costly, we can deliver with only half of the transformation done (usually) without unduly impacting features, and the new designs practically write themselves.

I think "the hard bits" Mike references are exactly where refactoring shines. Making sure the performance profile is acceptable is a refactoring task. Ensuring the application is extensible through plugins is an exercise in refactoring. Extracting out the timer behaviors from the license module to be reusable is refactoring; so is reconciling the 3 different timers you just found in the system, because they were disguised as something else. Refactoring touches nearly everything we do, so we really need to make sure we do it well.

4 comments:

  1. Great points made here. Overall refactoring is a tool. Much like a hammer. In and of itself it does not solve a specific problem. What it does is provide a tool that does two things. It builds and it destroys.

    Refactoring rips out code that is unnecessary as the claw on a hammer does. Refactoring also builds stronger purpose in the code as business needs change in an ever moving world much like a hammer can drive a new nail to construct something. Be it a house, a cart, or any other thing that can be hammered together.

    ReplyDelete
  2. Hi, Chris,

    Well, not quite.

    "Making sure the performance profile is acceptable is a refactoring task."

    No, it isn't: that's redesign.

    "Ensuring the application is extensible through plugins is an exercise in refactoring."

    No, it isn't: that's redesign.

    "Extracting out the timer behaviors from the license module to be reusable is refactoring."

    Yes, it is.

    "reconciling the 3 different timers you just found in the system [is refactoring]"

    Well, maybe.

    The key is in the definition: refactoring is, "Restructuring an existing body of code, altering its internal structure without changing its external behavior."

    If a user sees any difference in your system before and after your, "Refactoring," then it wasn't a refactoring.

    Breaking methods into a separate class is refactoring (no user will ever know).

    Adding the ability to add plug-ins is noticeable by a user (hopefully) so it's not refactoring.

    / Shmoo

    ReplyDelete
    Replies
    1. Shmoo,

      Perhaps I was not clear on the plugins part. Doing the groundwork for plugins is refactoring. Adding the plugins feature is not refactoring. That is, changing the affected area to use a decorator pattern or some such is a refactoring. Adding the ability to instantiate a decorator object from a configuration at runtime is not refactoring, regardless of whether the user sees the change or not. (Well, you could make an argument that it is if the user doesn't see _any_ behavioral change.)

      Adjusting the performance profile fits quite nicely into your definition of refactoring. Unless the performance of your code is a user feature.

      You can do an amazing amount of redesign using just refactorings, which is the point I'm trying to make here.

      Delete
  3. I wouldn't disagree with what you've written here, except with the implication that it contradicts my statement "the purpose of all that shuffling is really only to get the deck clear so we can concentrate properly on the hard bits without getting distracted by crud". When you speak of changing code to use a decorator pattern as a prerequisite for adding the ability to instantiate a decorator object from a configuration at runtime, that sounds to me exactly like clearing the deck so you can do the real work without distraction.

    ReplyDelete