Refactoring is Not Free

In my post Agile but Not Quite Yet Frans made the following comment:

One of the key mistakes embedded in agile development is that it assumes refactoring is free. Let me tell you: it's not, far from it. Especially in OO systems where refactoring of designs can lead to severe delays of the project as a whole, it's key to know as much information about the functionality you have to realize with the software you're supposed to be writing, UP FRONT, so you have the best chance to have the 'best' design in the first attempt.

With Frans not being the easiest guy on the planet to convince… and although he has a point…

Refactoring doesn’t add features nor fix any bugs. Refactoring improves the design of existing code in order to make it easier to change. So yes refactoring isn’t free when the application will never change, because you’ll never get the payback. Concluding change is the driving reason to refactor.

A couple of months ago we where up for the challenge to refactor a large portion of a badly performing application. The application had no supporting unit tests, cruddy code and Swiss cheese specifications (its developers only had a sense of what the application had to do). It is in this example where refactoring became costly. At start we tried to cover as much existing code possible (see Improving the Testability of Legacy Code using TypeMock.NET). Although the confident level increased we weren’t able to preserve the behavior of the code while refactoring small pieces at a time. Eventually we had clear signs that the progress we made just wasn’t enough. We then decided to rewrite it from scratch and than refactor it.

Thoughts and/or experiences to share?


  • Consider 2 alternatives:

    1) think for 10 minutes and write code based on the conclusions made in those 10 minutes. After an hour you realize you started wrong because you didn't think of X and you have to refactor the code you wrote

    2) think for 20 minutes and write code based on the conclusions made in those 20 minutes. After an hour you're done.

    I definitely will opt for 2).

    Every second you have to spend on refactoring is a signal you made a mistake earlier on, because if you would have made it better in the first place, the refactoring wouldn't be necessary.

    That's the core point of my reasoning. In practise it's not that black/white of course but it illustrates the point.

  • Frans.

    I agree that option 2 is better if you can guarantee that the extra 10 minutes of thinking will promise to avoid future change.

    Also, you're assuming that people can reliably create software without initial mistakes. I don't think that any amount of thinking can make that assumption always come true.

    The best protection against change is making change easy.

  • Not refactoring is also not free. The costs for unrefactored code increase as the code becomes more and more unmaintainable. Code duplication and other cruft from unrefactored code leads to longer times for feature additions, longer times for bug fixes, and more bugs in general.

    Test-driven development with constant small refactorings avoids the costs of bug-fixes, the costs of rewrites, and either avoids large refactorings, or permits large refactorings to be made easily.

    (Thanks to Kay A. Pentecost for reminding me of this.)

  • Trackback if Paul allows this comment

  • Exactly Keith. My reply above.

  • Of course, refactoring is not one ever said it was. Refactoring is an investment in your code. Failing to make that investment as you go along is extremely costly, as you discivered with the code example you cited above. If the developers had made the changes refactoring proponents advocate as they developed their system, it likely would not have become the mess you were forced to rewrite.

    Don't set up a straw man where good software engineering practices were not followed, then use that as an argument against refactoring. That is pure intellectual dishonesty.

  • Isn't refactoring really an old (and vey good) idea. COBOL programmers have been practicing this method for ages.

  • consider option 3:

    Think for as long as makes you comfortable, then start to code *but don't stop thinking*. Instead, stop every couple of minutes to reflect about the structure of your code and how it could be improved. After less than an hour (because you got some ideas on how to simplify the code on the way) you are finished and have produced code that is better than anything you could have envisioned up front.

  • Working with legacy code is hard. You might want to take a look at Michael Feather's book "Working Effectively with Legacy Code", in which he provides strategies for getting legacy code under test and refactoring it.

  • I own a copy of this book, great read!

  • “2) think for 20 minutes and write code based on the conclusions made in those 20 minutes. After an hour you're done.”

    I completely disagree with this statement. It’s arrogant to believe you can “think up” all the answers pre-code. Isn’t this why we rediscovered “small iterations” in project management? Waterfall doesn’t work, never did… Implementing a refactoring pattern to your development process isn’t free, but it is FAR LESS expense compared to dealing with a single method earning a cyclomatic complexity of 20+… You debug it when it breaks… I won’t!

  • You say:

    "So yes refactoring isn’t free when the application will never change, because you’ll never get the payback."

    Actually, you do get the payback, at every step along the way. If you do a refactoring today on a couple of classes, abstracting them to an interface, or consolodating similiar classes into a base class, then you will get the payback for that every day forward on the project.

    Not just you either, but every person who has to write the same sort of code. Now they've got an interface to work with, or a base class that they can inherit from, or a new class that performs some job they were thinking they were going to have to write themselves. Refactoring provides hidden benefit, because if you didn't refactor you would have written more code, duplicated more code, wasted more time, and had an even larger refactoring if you did it later :)

    Refactoring does pay. It just does it in a less obvious way than most people realize.

  • You're on the right track: Recognizing that you have "a badly performing application" is the first step towards fixing it. Recognizing that you have "a badly performing application" is the first step in the /business justification/ for fixing it.

    To comment on your question...

    I've had a lot of experience with refactoring bad code to make it good, and usually doing large chunks of rewriting is not necessary. In my experience, having a /vision/ for how you want things to be in the future is most important, as a source of guidance; as a direction. Then, iteration by iteration, you move in that direction.

    When the business users request a change that needs some of the new concepts and abstractions from your "future plan" road map, then this gives you a good reason to implement those parts of the vision/plan now.

  • I think James Smith misses a very key point, and perhaps that some of the other discussion of minor code refactoring misses the same point. The point I'm referring to is illustrated beautifully by Frans' two alternatives.

    The "easy availability" of refactoring is highly overused as an excuse for not doing appropriate design in the first place.

    James says "It’s arrogant to believe you can 'think up' all the answers pre-code" but this is a very misleading distortion which distracts people away from the point illustrated by Frans' alternatives.

    Take a look again: Frans describes a developer who COULD think of important design considerations (in 20 minutes). From this one can deduce that he wasn't writing about "the unknowable"!

    Instead he was talking about the thought, and design, and planning that could have been done but it wasn't done because the developer CHOSE NOT TO.

    I've heard the unknowable argument before and on many times this argument functions as a smoke-screen that hides the issue of design work that would have improved the outcome of the project and yet was neglected.

Comments have been disabled for this content.