What Is Technical Debt?
Every time some software is designed and written, decisions are made (consciously or unconsciously) about its quality.
- Does the code have unit tests?
- Does it have some automated system testing?
- Was TDD used to design that piece of code?
- If quality is not put into the initial release due to pressing timelines, is that code going to be revisited right after the release in order to increase its quality?
When code is written without some form of testing that can be done on it in the future, you are creating technical debt. In addition, if shortcuts or hacks are made into the code (not designing it well), you also increase the debt.
Think about taking out a loan from the bank. You immediately get a lot of benefits (money). Over time, you must pay back that amount plus interest. If you take too many loans or one loan that is too much, you may not be able to pay it back. Then you go bankrupt.
Technical debt is similar. When you don’t design software properly or don’t have automated tests that can verify it isn’t broken with future changes, you get the short-term benefit of working software. However, future development with that code is going to cost interest (increased time to finish something new) in the form of manual testing or increased time to determine how it works.
Building up more and more debt in your software over time can cripple your ability to add new features in the future.
How Does Technical Debt Occur?
Decreasing the usability of code can occur from ignorance or not having time to invest in its quality.
The unfortunate thing about this is some developers don’t even consider automated testing during their development. They aren’t aware that such practices exist.
Other developers might be aware of it, but they aren’t able to implement any of these practices due to the business environment. There’s too many things to do to dedicate time to making existing infrastructure better. In this case, I tend to do it anyway to some extent. It’s more of a “don’t ask for permission, ask for forgiveness”. In the long run, the business will benefit from it.
If something is ‘super hot’ and must be delivered very quickly, going back after the product is delivered and increasing its quality (right after it is delivered) can prevent that debt from piling up. But if there is never a time that the code is refactored, you live with that debt forever!
How to Fix Existing Technical Debt
Fixing existing difficult-to-work-with software has to be done with care. Before changing any code, it would be a good idea to wrap it with some form of automated unit or system test.
With those tests, you can have increased confidence that any changes to that code will not break existing functionality. You can refactor some, which may allow you to add some more unit tests to make the code better tested.
In the event that a major refactor is needed, I would lean more on system tests.
In order to do any of this, though, time needs to be invested in not creating new functionality, but cleaning up existing functionality. The business has to buy into this idea, or developers need to work on this without letting management know (the ‘don’t ask for permission but for forgiveness’ idea).
There’s a book out there called Working Effectively with Legacy Code by Michael Feathers that goes into the idea of fixing technical debt. It goes into a ton of detail with several different methods to break apart dependencies without breaking existing functionality. If you are interested in this concept, I highly recommend you get this book. It is extremely helpful.
Preventing Technical Debt
When you are starting from a fresh code base, you can help prevent technical debt from ever starting in the first place.
TDD can really help. This is the practice of writing a single test that fails before writing code to fix it. This verifies you are testing everything you write rather than leaving testing to the end.
Tests are part of preventing those code smells.
In addition, good software design helps. I read a quote in a book once that I really like. Good architects prevent making decisions as long as possible. This is because that allows your code to be very flexible. Flexibility allows the code to be reused a lot more.
For example, rather than forcing your code to use a GUI, perhaps it is a browser application. By creating a well-defined API, the end solution could be either approach. In addition, future usage of that software could be radically different thanks to good design.
Collaborating with other developers can also help with design. A lot of times we get so wrapped in one way of doing something. Other people may have a completely different perspective that is much simpler.
Both testing and good design helps prevent technical debt. TDD is one of the biggest drivers of quality I’ve seen in my career. Writing those tests first changes the entire way you think about designing the code… in a good way.
Technical debt is an all-too-common reality for many developers. It may already exists or it is future fear. In either case, there are methods for dealing with it.
If nothing else, just be aware of it. Every time tests are ignored or shortcuts are taken, a “loan” is taken out against future usability with that software. Being aware of it can help drive some business decisions in the right direction.
Prevention is the best approach.
The next best approach is solving it now. Ever heard the saying when was the best time to save for retirement? Ten years ago. The next best time? Now. The same goes for code quality.
The worst approach is to completely ignore it.
Don’t ignore reality. Make decisions that are fully informed.
Hopefully those decisions involve creating a future with clean code that is a joy to develop and use.