Saturday, June 30, 2007

When Should You Refactor?

Refactoring is the process of changing the structure of code without changing its behavior.  It should be used to ease the addition of features.  Because the outcome is code that "smells" better, sometimes people get confused and think that refactoring is an end to itself.  I disagree with that sentiment.  While we should strive to write code that smells good, we shouldn't spend extra effort after we finish beautifying the code.  There is always the possibility that no one will need to touch that again in which case the beautifying was wasted effort.  After all, the compiler doesn't care how pretty it is.

Remember this:  refactoring is not an end to itself.  Programmers are paid to add features or fix bugs thus making features work better.  Refactoring--by itself--accomplishes neither of these.  If you are touching code just to refactor it, stop.  Wait until you have a need to change something first.

Here are some rules of thumb for deciding when to refactor:

  • Even if code is ugly, if you don't have to change it, don't fix it.  You just risk breaking things.
  • If you are adding some functionality and it is hard to implement, refactor.
  • If you have to change the same code a second time, assume you'll probably be back a 3rd and 4th time and refactor.
  • If you don't have unit tests, think long and hard before refactoring.  Without tests, you can't know you didn't break anything.  Write unit tests first if necessary.

The fallout of this is that, when making the first change to a piece of code, you should resist refactoring.  If the change will be difficult without refactoring, then do it.  However, if there is a "hack" you can do to fix the bug or add the feature, prefer that.  Most of the time, ugly and quick is better than pretty and slow.  If you come back to the same code again, then refactor.

3 comments:

  1. You are correct, Steve.  Refactoring should not be done "for fun."
    However, I can think of a case where refactoring should be done well before there is something that needs fixing.
    Many folks view refactoring as a way to improve design before you add a feature.  There is also the need to improve design to improve it's maintainability, before a feature needs to be added.  This second case happens when you need to drive by the delivery date, and not by the code quality.  You (or another developer) may make short-term decisions, ones that you regret THE NEXT DAY.
    Now, you get the code out and the system is running, but you have incurred a serious debt.  You know that the very next change is going to be EXPENSIVE because someone is going to have to relearn your code, and refactor it, before they can add anything.
    So you present the fact that you made short-term compromises to the business, and that you incurred debt on their behalf to get it out quick.  You ask: do you want me to clean this up now, when it costs X, or do you want to wait for someone else to clean it up later, when it costs 2X.  
    If the business says "pay the debt now" then you should do it.  Even before there is a change to be made.
    Other businesses work this way too.  If I am building a restaurant, and I need to open the business on April 2nd, and some delay in the delivery of the furniture means that the floor won't get treated in time before we open, then you open on time.  Two weeks later, you close for a day to treat the floor... while the contractors are still around and willing to do the work.
    You can wait, of course, but an untreated floor won't last as long.  
    It's the same idea: sometimes compromises that are outside of "what you build" affect the quality of "what you deliver" based on a date.  Fixing those compromises may be less expensive to do right away, after you deliver the system but before you need to maintain it.

    ReplyDelete
  2. I'd just like to add a special case of the general argument by NickMalik.
    It makes sense to refactor code to make it more understandable, even if that particular piece of code will never be touched again. Even if the code itself will not change, it _will_ be seen again by later developers who need to understand the system in order to make changes to related pieces of code.
    John

    ReplyDelete
  3. Good points both of you.  Nick, you bring up the great point that sometimes it is better to refactor up front because you can do it more quickly than the next person.  Thinking of bad design as debt is a good way to think about it.  However, it's not always easy to know what is debt and what is just some ugly wiring that no one will look at.  Cleaning up something ugly that no one ever has to revisit is a waste.  
    John, you also make a good point.  Sometimes people will be reading the code even if they aren't changing it.
    The point is to have more reasons than just ugliness of code before you start to refactor.  It's like optimizing.  Make sure you need it before you do it.

    ReplyDelete