Evaluating technical debt's interest rate
I've been thinking a lot about technical debt lately. There are some excellent articles on the topic that go into great depth into how to tackle and prioritise it already. I'd like to offer one complementary perspective on the topic.
The financial analogy
Let's explore the financial analogy of technical debt as a loan. A simple loan is made of a principal (the amount you borrow) and an interest rate. Every month, you pay off interest, as well as some of the principal. The interest you pay each month is calculated as the interest rate multiplied by the amount of the principal still owed. The higher the interest rate, the more interest you pay. The higher the loan amount, the more interest you pay. The longer you take to pay off the loan, the more interest you pay.
At the end of the loan, you'll have paid off the principal as well as a fair bit of interest (sometimes more than the principal). For example, a loan of \$1M with an interest rate of 6%/annum paid off with regular monthly payments over 30 years could cost \$2,158,382 in total (source).
A codebase can be thought of as a collection of loans, each with its own principal and interest.
The technical debt principal
The principal is the amount of code you have. That's right, any code you have to maintain is technical debt. If you manage to delete it all, you'll have paid off the loan and won't incur any more interest. This is why deleting code is such a satisfying experience for a seasoned engineer. It's also why measuring an engineer's productivity by the amount of code they write is absurd.
You may ask exactly how to measure "amount of code". It doesn't matter. Think of it as vaguely correlated to lines of code, perhaps. I'd be wary of measuring this in any exact way, since it would make it gameable. Technical debt isn't an exact science!
The technical debt interest rate
Now, how would you measure the interest rate? That's a bit more complicated.
We could try to calculate the interest rate of the overall codebase, but that wouldn't be particularly useful. What we want to know is which areas we should improve, and which areas could be left as is, at least for now. It helps to think of different areas of a codebase as roughly independent loans.
You may have implemented an experimental feature in a scrappy way with plenty of hardcoded values and copy-pasta. Would that feature's implementation have a high-interest rate? Perhaps, but that may be completely fine if the implementation is small enough, and you are committed to deleting all experimental code after a few weeks in production.
In fact, I tend to believe that interest rate isn't so much a function of the quality of the code itself, but of its impact on the rest of the codebase.
In particular, code that is built upon typically has a higher interest rate by nature, because any flaws will instantly "spread" to any code that depends on it, now and in the future. For example, a poor choice of programming language or framework will raise the interest rate of the entire codebase. Depending heavily on a slow or insecure library may also do that.
This explains why particular attention should be given to designing simple, elegant APIs and keeping code decoupled as much as possible. A clean API effectively isolates its clients from whatever is happening under the hood, keeping a lid on its technical debt. By that standard, spaghetti code naturally gives you high-interest, while a modular codebase with good abstractions keeps it low.
Similarly, any code that tends to be reproduced across the codebase should be considered very carefully, because new code will effectively inherit its interest rate. That's why good conventions are important. It's also why if your company has boilerplate projects that are used relatively frequently, they should be carefully maintained.
Technical debt in a nutshell
Let's summarise our analogy:
- All code is technical debt.
- Some technical debt has a high-interest rate. Unless it's small enough, work on either reducing the interest rate or deleting the code.
- Decoupling code behind simple and elegant abstractions helps reduce overall interest rate.
- Low-interest rate technical debt is fine. Leave it!
At the end of the day, technical debt needs to be managed well enough to enable the business to keep growing in the right direction (which typically means spending more time building features than addressing technical debt). It may be fine to incur high-interest rates as long as the team is growing fast enough, but it can also be a dangerous bet since the principal will grow as well.
Of course, this analogy has its flaws. For example, what's the currency of a technical debt loan? Should we measure it in engineering hours? If so, how does that relate to our definition of the principal as the "amount of code"? Do interest rates compound when interest isn't paid?
I'm curious to hear your thoughts on this topic. Don't hesitate to share them with me on Twitter!