Technical debt directly affects the quality of the product you deliver to your customers. But the term itself takes on different meanings depending on who you talk to.
Ward Cunningham is attributed with inventing the term back in 1992. He defines it as being a disconnect between the product’s requirements and its actual implementation. The term then grew to encompass things such as crufty code, code that needs to be refactored, and missing technical features. The reality is that there is no single definition anymore — and that’s fine so long as everyone working on a project have a common understanding of what it means in their context.
Just like in the financial world, there is good and bad debt. Making minimum payments on a 20% interest credit card can ruin you in the long run. On the other hand, taking out a loan to buy a house is good debt as you wouldn’t be able to afford the house for decades without that loan. Operating with financial debt is normal and the same is true for technical debt. But just like the high interest credit card, there are strategies to manage that debt before it gets out of hand and can’t be repaid.
#1. Accept More Good Debt, Push Back on Bad Debt
Martin Fowler’s technical debt quadrant is a great way to determine what is good or bad debt:
Prudent debt is the type of debt you want to have. It’s usually something that will need to be fixed in the future but the current implementation addresses the needs of the application for now. This doesn’t mean we cut corners, or that we didn’t follow proper development practices. It means a simpler or smaller implementation than what was originally intended was developed.
It goes without saying that Reckless debt is the worst kind of debt and can get a project into trouble if left unchecked. Doing little to no design, skipping code reviews and not having a proper testing strategy are all examples of things that can quickly derail a project from reaching its full potential.
#2. Track and Measure Your Debt
Technical debt should be added to the product backlog or issue tracker as soon as it’s found. Tagging it as such makes it easy to identify how much debt we’re accumulating and paying back. Once an issue is tracked, find some time to sit down with the development team to sort out how much work is involved to pay back the debt.
#3. Don’t Write Sloppy Code
Uncle Bob argues that sloppy code isn’t technical debt and I can’t say he’s wrong. Even if you are deliberately taking on technical debt, you should write clean code that puts you in a position of strength when it comes time to resolve it. Following development best practices ensures that the code you have written is good enough until the technical debt can be addressed.
#4. Keep Feature Work in Balance With Technical Debt
The image above tells the tale of two radically different projects over the same period of time. The first delivers features at a slower pace while progressively paying off technical debt. The second focuses almost exclusively on feature work, letting technical debt accumulate. Needless to say that if this keeps up the second project will be less maintainable and harder to evolve as time continues.
#5. Set a Timeline to Address Risky Debt
As we saw earlier, some debt is riskier than others. Unfortunately it’s not always possible to only accept prudent debt. There are times when the best we can do is to limit the damage. In those (hopefully rare) cases, set a target date or deadline for when it will be fixed. Otherwise it will tag along from one release to the next, getting bigger and bigger until the only way to fix it is a complete rewrite.
#6. Make Technical Debt Tasks Small
Just like a large feature, it’s hard to estimate how much time technical debt will take to be fixed. The smaller it is, the more accurate the estimate will be. Breaking it down into smaller chunks will also give you more flexibility to choose when it’s addressed. One of my favourite strategies is to take on a small technical debt issue at the end of each development cycle. Since technical debt does not always result in feature changes it’s easy for QA to perform smoke tests that validate nothing broke.
#7. Put a Thorough Testing Strategy In Place
Fixing technical debt is only half the job. We also need to make sure that its resolution has not inadvertedly affected the application in any way. The best way to check this is to having a thorough automated testing strategy.
#8. Address Technical Debt as Part of Any Re-work You Do On a Module
Let’s say you’re working on a feature to change the contract for an API. That API validates the fields that are passed to it using hard-to-read validation rules. In another part of the code, you’ve started using a library to make that code clearer. Kill two birds with one stone by including as part of your estimate the time it will take to refactor the validations to match the other module.
#9. Find Another Team to Help Resolve the Technical Debt.
Your team is behind on its delivery, the pressure is mounting, and you “just don’t have the time” to address that pesky technical debt. Instead, try to find another team willing to take on the work for you.
This is also a great way to share knowledge within an organization as it gives development teams the opportunity to see what the other is working on. More product knowledge within an organization is always a good thing.
#10. Treat Technical Debt as a Blocking Task
You wouldn’t build a 3 ton stage on a beam that is only rated to support half that weight. The beam needs to be reinforced before any more work can be done. You can treat technical debt in much the same way. When building a feature on top of technical debt is going to put the system at risk, address it before any feature work begins.