An Approximate Measure of Technical Debt

19 Nov, 2008

Technical debt sucks, and it's a particularly common problem for the teams I work with. Technical debt affects everything they do. It disrupts plans, kills productivity, and creates defects. There is much wailing and gnashing of teeth, but strangely, these teams put very little effort into paying off the debt. (That's probably why they got into this mess in the first place.)

Part of the problem is that technical debt is, well, technical, so only programmers can see it. Managers and stakeholders can see the effects of the debt (the aforementioned wailing and gnashing of teeth), but they can't see the cause. So when programmers complain about the quality of the code, they often ignore them. "We're behind schedule already!" they cry, brandishing their whips. "Row faster!" And the poor programmers work as hard they can, taking more shortcuts, incurring more debt, and making things even worse.

Not that programmers are perfect. There are plenty of programmers who haven't a clue about good design, and they merrily rack up technical debt without even realizing they're doing it.

Measuring Technical Debt

I've been searching for a measure of technical debt (or its inverse, design quality) to help address this problem. Now, no measure is going to turn bad programmers into good programmers. Design quality is a human problem, and compilers eat spaghetti code just as easily as they eat clean code. But I think it would help if people could at least tell when they were making things worse. Unfortunately, traditional measures such as McCabe's cyclomatic complexity don't do a great job of exposing the large-scale technical debt I see most often.

I think I've finally found the right metric. It's a good one. Years of research has shown that this metric correlates closely with cost, effort, and bugs. I'm surprised nobody thought of it before now.


It's lines of code.

The Case For SLOC

I'm not kidding. SLOC isn't a perfect metric, but I think (source) lines of code approximates technical debt. (And in case anyone is confused, I mean that more lines of code represent more technical debt. More lines of code, more technical debt, worse code quality. SLOC = bad, got it?)

Think about it. If we define technical debt as high cost of change, then SLOC fits the bill perfectly. Estimation gurus have long known that lines of code are correlated to effort and defects. In fact, many estimation tools work by taking a size estimate (either in lines of code or its language-neutral equivalent, function points), then running it through an algorithm that estimates project length, effort, and cost.

If that isn't enough, what's the most common form of technical debt? Duplication! Duplication! Duplication! Either the blatant duplication of cut-and-paste coding (leading to duplicated lines of code) or the subtle duplication of poor abstractions (leading to repeated concepts and algorithms). Cut duplication and you improve code quality. As Martin Fowler said in 2001, "I have an increasing sense that pig-headed determination to remove all repetition can lead you a long way toward a good design."

And then there's Carter's Compass, coined by John Carter: "I know I'm on the right track when by deleting code I'm adding functionality." Who hasn't had the wonderful experience of adding features while reducing the overall size of the codebase? It's a great feeling. You just know you're doing good work.

Nothing's Perfect

SLOC is an imperfect metric, and one objection is that adding features increases lines of code. Although you can offset that increase by reducing technical debt elsewhere, a perfect program will have no technical debt to reduce.

I don't think this is a real-world problem, and perfect people won't need this metric. (Mostly because they don't exist.) Real, non-trivial systems have plenty of opportunities to reduce size even as features are added. Even if nearly all of those are worked out--and I'll believe it when I see it--then you can use the metric to check that the rate of increase says low.

Another failure case of SLOC as a metric of technical debt is that it could encourage other types of bad code: people creating monstrous one-liners, for example, or refusing to make small methods because of a language's boilerplate.

We can work around this problem by counting executable statements rather than actual lines of code. For example, the following Java code counts as one statement:

public class HelloWorld {
  public static void main() {
    System.out.println("Hello, World!");

The Spag

So here's my proposal. The unit of technical debt is the "Spag (Sg)," from "spaghetti." You can pronounce it "Spag" (rhymes with "gag") or "Spug" (rhymes with "bug") depending on your preference. (I almost went with the "Foote," in homage to Brian Foote's and Joseph Yoder's awesome Big Ball of Mud design pattern, but I wasn't sure if Brian would appreciate having a measure of code crap named after him. So Spag it is.)

To keep the numbers sensible, and to prevent obsessing over micro-optimizations, one spag equals 1,000 statements, and spags are never calculated to more than one decimal place. So the Java "Hello World" program above has 0.0 spags of technical debt, and a program with a million statements has a kilospag.

(A kilospag is spagload of technical debt, and if you've ever worked on one of those programs, you know just how bad it is.)

I think the best way to use the spag is to track trends in your codebase. It's particularly appropriate when you know you have problems and you want to see your progress, or if you need a visual way to show managers and stakeholders that you've been accumulating technical debt. ("Since you told us to 'meet the deadline or else,' our rate of spag accumulation has quadrupled. We need to start paying more attention to design quality and paying off this debt, or we're going to have serious productivity and quality problems.")

It's a fun idea with a silly name, but I also think it's got real potential. Try it out on your projects and let me know how it works for you.