Managing Technical Debt
Debt seems to be on every-one’s mind these days. You can’t open a newspaper anywhere in the world without seeing some article about some country’s national debt. As we all know, countries aren’t the only entities that have debt. Most households have some level of debt, whether it is a mortgage, student loans, or some level of credit card debt. There are many different aspects to this debt, but the one thing that seems constant is the idea that with debt comes interest. The longer you wait to pay it off, the more expensive it will be.
Think of it this way. When we want to purchase something, we have a choice. We can pay with money on hand, or we borrow money from our future earnings to pay for it. In the same way, whenever we work on code, we are either paying for it with “productivity” now, or we can borrow from future productivity. Both today and tomorrow’s cost money. The difference is, since we know there is a cost to changing something later in a system, we need to recognize the cost is higher to wait and fix it later.
So how do we work with this and not end up in bankruptcy? Many of the activities that we have come to associate with agile development will help to get us there. Here are some thoughts that I like to keep in mind:
Recognize Debt for What It Is
Since we are stating that debt is sometimes necessary, let us at least recognize which kind of debt it is. If the debt is because we are making a trade-off due to time constraints, we need to see this as a debt that must be paid off soon, like a high interest credit card. We have accepted a design flaw, or perhaps a defect, in order to get the story out, or the feature complete, in time for a release. This is not ideal, but sometimes necessary. On the other hand, sometimes we are going to incur more long term debt. Debt can be an investment. Perhaps we are looking at a major change, like moving to a cloud based environment, or a larger technology. Using the principals of “Do the Simplest Thing That Could Possibly Work” we might use a model that doesn’t take full advantage off the new technology, but gets us started. It works, so there is no pressing reason to go deeper, or to prematurely optimize, at this time. We will want to pay this debt off also, but we can do it on our own time, and the cost will be lower.
Keep the Interest Rate Low
We have recognized that we are choosing to incur an increase in the total cost by waiting on a particular feature or issue. Now we need to make sure that the cost is as small as possible. There are many mechanisms to do this. One of the most helpful is to have a large and strong body of automated unit and acceptance tests. This will be especially useful in ensuring that any design debt is kept to as low a cost as possible. Limiting our Work In Progress is another excellent way to keep the costs down. High quality code comes from folks who are not scrambling to get as much done as possible. Teams that can concentrate on getting the highest quality out there will be better suited toward keeping the cost of change down.
Consider a Payment Plan
Borrowing once again from the extended metaphor, consider creating a debt repayment plan. Just as we have all learned about personal debt, the best approach is to make the largest payments you can afford against the debt with the highest interest rate. You still need to make payments against any other debt in order to maintain good standing, but the bulk of your effort goes to paying off the most expensive items. While agile and lean make this much easier to manage, you can do this no matter what methodology you are using. When you are prioritizing your stories/features/whatever, also prioritize your defects or other technical debt related items. Then, whatever mechanism you are using for determining the capacity for the upcoming work, allocate a certain percentage toward paying off debt. This percentage should be large enough to make a difference, but not so large as to cause no new features or added business value.
As far as the “minimum payments” for the lower cost items, that is where refactoring comes into play. By continuously improving our design without changing behavior, we are incrementally paying down design debt. Also consider spending some time inserting new tests in legacy code. We tend to find “hidden costs” inside the legacy code, so creating unit tests around it will help us to pay those off as well.
Don’t Beat Yourself Up
Lastly, while we will always resolve to take on as little new debt as possible, there is nothing to be gained from beating yourself up for debt you already have. Take a deep breath, create your payment plan, and keep working on it. Second guessing is painful and a waste of time. We would be better off spending that time creating great software.