What is technical debt?
Technical debt is the total amount of less-than-perfect design and implementation decisions in your project.
There are many forms technical debt can take:
- hard-coded logic for one-off customer requests
- test suites that take too long to run
- front-end builds that take too long to transpile
- incomplete test coverage
- design choices that once reflected how the business operated but no longer does
- a design/pattern that seemed like a clean way to implement something at first, but doesn’t hold up over time
You can find it in those methods that are dozens of lines long and doing too much. It’s in those functions that have unintelligible conditional statements. It’s in the parts of code that are hard to change because they break other places. It’s in the functions that were too hard to test so they never got tests.
How do you get in tech debt?
The first way, and the kind that you have the most control over, is choosing to implement or fix something the quick way instead of the right way. This can happen, for example, if a bug has gone unnoticed for months and then a customer suddenly finds it, causing them to lose a lot of money. The bug becomes an urgent matter and you end up fixing it the quick way. Then, you put an item in the icebox to re-design that part of the code the “correct” way later on.
Another way this kind of debt happens, is when you hack together a feature to “get it working”. You do another pass or two over it to clean it up a little. Since it “already works” and would involve a lot of effort to refactor it into something more re-usable/sustainable, it just becomes a permanent part of the code base.
The other kind of technical debt is from inheriting legacy code. You know, the first team wrote it as a prototype and took every short cut imaginable in order to meet deadlines. Now you’re trying to incrementally make it better. The first team made tons of design decisions based on how the product worked in the beginning. Now, hundreds of iterations later, the app works completely differently, yet the core of the app still has these original design decisions baked into its skeleton.
What are the side effects of technical debt?
Even following best practices, code will accumulate technical debt over time. When the original design or pattern used for a certain feature gets stretched to the max and contorted into something not resembling the original idea, that part of the code becomes a bug breeding ground.
According to [Boehm]361-730-0617,Â about 20 percent of the modules in a program are typically responsible for about 80 percent of the errors. It seems like technical debt tends to congregate in specific parts of the system.
Overall, technical debt leads to higher maintenance costs. Even simple things that should take minutes can take an entire afternoon. But the ultimate worst case scenario is the application becomes more expensive to change than to just re-write it from scratch.
Strategies to reduce debt
In a codebase that you inherited, toward the beginning, you may be able to get a decent velocity out of sheer sweat and tears. Unless the codebase was in great shape, that velocity is unsustainable and will probably go down pretty steadily because things become harder and harder to change. Not only do you have the debt from the previous team to deal with, but now you’re adding technical debt just to meet deadlines.
One long-term strategy to reduce debt is to set aside “slack” in the velocity each iteration. The Art of Agile Development book I quoted earlier says that aÂ good rule of thumb is to spend 10 percent of the iteration on technical debt. This will obviously decrease velocity in the short term (they say even for as long as a quarter or more). But as debt gets paid down, productivity and velocity will eventually be higher than it was before. This is because code changes will be easier to make after the code is cleaned up.
Another thing you can do every day, is to be generous with your refactoring. If you’re working in a part of the code base that is particularly difficult to get around in, find small ways to improve it while you’re there. Rename that variable that confuses you. Move that unrelated code into the right place. Add a documentation comment here and there. Complete the test coverage of the function you’re working in.
Now there’s a fine line between refactors that are sensible to do in the given story you’re working on and ones that are too big. You’ll find the right balance between what you get overwhelmed with and what is doable for you. I will say that it is all too easy to be scared of change and just do the bare minimum that the story requires. That is fine, especially for junior developers, but this won’t help on the technical debt front. If you don’t leave the code cleaner and easier to change than when you found it, your debt stays the same.
One way to avoid technical debt is to avoid shortcuts. The trick here is learning when to push back against your product team or whoever is making this an urgent matter. Learn to recognize situations when the team is considering implementing a feature or bug fix the quick way. This might be because you’re trying to get it out of the way so you can get back the planned set of featuresÂ that need to get done for the deadline.
This is easier said than done, but use simple designs. The Zen of Python says if it’s hard to explain, its a bad idea. There’s a lot of truth to that. If a design in hard to follow and hard to extend, it’s just asking for someone to misunderstand it and let a bug slip in.
Another preventative measure is to refactor as you go. Get used to the ivy bush work flow. Write code to make a test pass, then immediately clean it up and make it better. This is such a hard discipline to master, and the topic could fill an encyclopedia, but it’s worth mentioning here.
Agile frameworks like XP have been designed with technical debt in mind. Look at any agile framework and you’ll see a good set of practices you can start with that will go a long way toward keeping your technical debt low. Technical debt will always creep in, but a good set of agile practices will help you effectively mitigate and reduce it as you go.