Sunday, April 19, 2009

Technical Debt

Some time in the last year, I learned the term Technical Debt. It's a brilliant metaphor for the more hurried, less-thought-out code that crops up in any project with a ship date.

The idea is simple: Just as you actively take on financial debt to give yourself new opportunities, you take on technical debt to ship a product out the door and make money from it. Financial debt is simple money, but technical debt is more complex. Any decision you make to ship the product at the expense of a clean code base qualifies. Every test you don't write is technical debt. Every piece of duplicated code that hasn't been refactored into common functionality is technical debt. Every todo comment is technical debt. Every bug you ship with is technical debt.

But too much technical debt, and you never pay down the principal, in this case the ability to quickly add new features or fix old bugs. That duplicated code? If you never refactor it, every bug fix in that code has a cost proportional to the number of places it's used. And that's if you actually remember where all those pieces are. That todo comment you never go back to? Some day you'll need to enhance the code around it, and that todo will add to the cost of the work. Add enough of these little debts, and the cost for any given feature multiplies.

Unfortunately, while it's easy to measure financial debt, it's tough to measure technical debt, which makes it difficult to use when arguing for time to fix things up in the code base. If you never need to fix a bug in that duplicated code, for instance, you'll never experience the extra cost of fixing it in every location. Of course, the idea that a chunk of code will sit forever untouched once it's written is ludicrous, but you can't sell the idea that something might be a problem someday outside of development teams.

There's also no good way to flag technical debt. Perhaps as programmers we need a more formal way — beyond todo comments — to note down that we made a deliberate choice to take on technical debt. But then we have to advertise to the larger world that we deliberately did something not in the best possible way. I know that won't sell.


  1. we need a more formal way — beyond todo comments — to note down that we made a deliberate choice to take on technical debtMy answer to this, as always, is to automate whatever you can.

    If your bug or task database supports it, have a script that gets run on submission that scans for TODO and any other flag words that you use and automatically puts something on the task backlog to address it. That at least gives you something to point to in sprint planning.

    I've also argued for alternating "feature" milestones with "quality" milestones - you'd have milestone periods dedicated solely to making the app run faster, with fewer bugs, and cleaner under the hood, without dropping in a bunch of new crap. I doubt production staff would ever sign off on it, but I think there's a case to be made.

    I also started work on a script that would look for nearly-repeated blocks of code within a single module and flag them as possible refactoring opportunities. I didn't get far enough with it to call it useful, and there are certainly a lot of other refactorable code smells that aren't as easy to algorithmically detect.

  2. I know you're not in a Java shop, but I set up Checkstyle to produce a monthly "bad smell" report that includes duplicate code checking: You might be able to steal some of its algorithms.

    It only seems to check for duplicates within a single class file, which is useful but not what I want in the long run. And I'm not sure how smart it is about code that's not an exact copy but is obviously the same code (e.g., if the variable names changed).

  3. Thanks! Looking at the Checkstyle docs it appears to do no fuzzy matching at all, which is definitely not what I want. I'll also take a look at Red Hill Consulting's Simian dupe checker, which the Checkstyle guys endorse.