Technical debt is a set of trade-offs in the code made by developers intentionally on a particular stage of application development. It accumulates when creating new product or feature, where implementation time is more critical than making the right design decisions. Technical debt is not bad code written merely to speed things up. It’s a set of conscious choices made by an experienced developer as a result of a negotiation with a project or product manager. Every project has some level of it, and it’s completely ok, but it’s important to reassess technical debt that your project has every so often and pay it off as soon as possible.
What exactly technical debt is
Few concepts are often referred to as technical debt. First of all, there are parts of the code or application that need maintenance. That's not something that can be fixed and will exist forever — things like optimizing database queries or upgrading servers and so on.
Then there are solutions that no longer fit the problems we have now which become a liability and have to be worked around. An example would be a new requirement to allow users to log in and sign up not only with email but also just with their phone number. The effort required for this change will depend on how straightforward it is to add another user identifier. These types of problems are most oftenly referred as debt compared to the others.
The big part of the technical debt is spaghetti code that causes fear because nobody understands how it works exactly. Often this kind of code is inherited or created by less-experienced team members who have already left the team. Such pieces often cause a lot of emotional impact, causing even senior developers to struggle to work with it.
And of course, there are dependency issues. Design decisions and technology used become liabilities as the time passes and needed to be upgraded. A single dependency can cascade through whole application blocking important updates. That's probably the second problem that most often gets referred to as technical debt.
Why you should care
Technical debt slows down development and sucks the life out of developers. In the worst cases, people might start leaving the team if the level of technical debt is unbearable, and it’s mostly senior developers who genuinely care and are very sensitive to such things. While slowing down overall performance of the team, it may also affect new features so that they will inherit problems that already exist, increasing effort to reduce the debt.
Of course, there are a few questions you need to ask yourself before assessing the impact of technical debt. You should consider current priorities and expected product changes in the next few months, is there a lot of them? Also, think about how productive your developers are and what kind of process you have. Do you plan to onboard new team members that will have to work with the trade-offs that are in place? Will it be hard for them to get a grasp of your code base?
Crunch mode
Chad Fowler, developer and book author, has a great article on crunch mode on his blog. Working under stress and unrealistic deadlines is one of the biggest reasons code in the application becomes less manageable. When someone is working all hours, they can’t be blamed for mistakes and bad decisions. Accountability is lost. Most of the times it’s a management problem, but sometimes it’s something everybody agrees to do and fix all bad choices after the deadline. Just make sure that it happens sooner rather than later.
It helps to keep a list of all bad decisions made during the crunch mode. In our team, we usually create Trello cards for refactoring when we know that specific piece was made to improve implementation speed and we need to come back and refactor it. We also make sure that all such cards are reviewed after the deadline so that all developers are aware of the hacks of each other. Keeping track of the code issues helps address them sooner.
Refactoring is the answer.
Refactoring is merely rewriting a piece of code so that it’s readable, maintainable and flexible enough. Sometimes it’s just removing hard-coded conditions or values to make code universal as it should be. Refactoring is something that you are safe to do only if you have decent test coverage in your app. Changing code that is already working is likely to cause errors elsewhere in the application so if you don’t have at least unit tests you will have to make sure that everything works by running tests manually.
Refactoring should be done in small pieces in parallel to main development work. The more you refactor at a time, the more bugs you can potentially introduce, so it makes sense to work on a small but self-contained code, improve it, test, deploy and make sure that no bugs were introduced. Then repeat with the next piece waiting for refactoring. Of course, this implies that you have a proper process in place — testing environment, preferably QA engineer and production environment with real users who use the app.
Technical debt is a real cost for the business, not just a fallacy that dev teams always use an excuse. It impacts your future development and flexibility and has to be paid inevitably when you are least prepared. Of course, if you are not sure if the project is long-term and what the future of the application you are building will be then technical debt would be one of the least things you must care about. But if you are aiming for the long run, you should probably find time to address technical debt regularly. I would say spending 80% of the time building new features and 20% of the time refactoring should be enough in most of the projects. Of course, then there are other things like tests and R&D, so you have to balance all that all the time.