Technical debt: debt and history
I’ve been beating my head against the technical debt metaphor a bit these days. Here’s one thing that oozed out.
Preface: I’m assuming there are two kinds of debt. One is frivolous debt; the other is debt-as-investment. When debt is an investment, you have a reasonable expectation that a bulk sum of money now will allow you to start something that will, at some future time, produce excess cash greater than the cumulative interest on the debt. For example, you borrow money to buy a car because it lets you choose from more—and more kinds of—jobs. More opportunity means greater lifetime earnings than if you rode the bus while saving for a car.
Business pressure to add technical debt to a code base usually is couched in terms of debt-as-investment: “If we miss that market window, we’re out of business, so it won’t matter how scalable the code is.” (Whether the expectations are reasonable, and whether the same parties pay for the debt as get the increased returns, I leave for another day.)
Frivolous debt is different. For example, John McCain and his wife recently had USD10,000-15,000 in credit card debt at 25.99% interest. There’s no reason for them to pay that stiff an interest rate, but what the hell: she made more than USD6,000,000 in 2006, so why sweat interest charges? More commonly, though, frivolous debt does matter, and it comes from ignorance. In code, for example, technical debt can accrue because people simply don’t know guidelines like “Tell, don’t ask” or “Don’t repeat yourself“.
In what follows, “debt” means “investment debt.”
The analogy to a growing GDP
We imagine that debt-free code is without history. Suppose that, during development, classes Foo and Bar split into Foo, Bar, and Baz, and some of Foo’s methods were merged into Bar. If you were coming on the project, I should be able to explain why Foo, Bar, and Baz make sense without referring to their history.
History only comes up where there’s technical debt. It makes you say things like “there’s duplication between Foo and Bar because we didn’t realize fact X until new story Y and we haven’t time to finish merging their behavior.”
I have a sense that telling that story isn’t just a way of making excuses: it’s actually important for being able to work with the code in the presence of debt.
But, because we have this history-free ideal, we don’t pay attention to that. Maybe embedding history in the code (somehow) is a way of increasing the debt load the team is capable of supporting in perpetuity.
So, where I had been thinking about how to reduce debt, now I’m thinking about how to make it matter less. Like there are two ways to solve the problem of an insupportable national debt: you can pay it down or you can grow the economy.
June 23rd, 2008 at 11:31 am
How do you account for stories like, “yeah this is a kinda weird way of doing this, but we found when we did it the ‘better’ way it was 30% slower. We added this extra complexity for a performance boost.” I don’t see how history can be avoided in this case, lest a journeymen developer later notices that the optimization could use a refactoring, which in turn leads to a performance hit, which leads to an optimization, and so on.
In this case is the debt borrowing from maintainability for performance? Either way, I think history serves an important role here other than as an excuse.
June 30th, 2008 at 9:18 am
I think the doctrinaire way of handling that kind of commentary is the one given in Parnas&Clement’s “A Rational Design Process: How and Why to Fake It”. Here’s what I wrote about it a while back:
In Parnas’s style, it would not be important how you came to know that a high-performance, high-complexity solution was needed. You merely needed to state the requirement, argue that the algorithm met it, and also argue that a simpler algorithm would not.