How you fix technical debt can determine how quickly you fix bugs and release new features. Read on to find out several ways to tackle technical debt.
This post is an expansion of a tweet I made the other day. It occurred to me that some folks might want some more detail on the topic of technical debt.
Technical debt is any extra work that arises when shortcuts are taken instead of applying the best overall solution.
Where does technical debt come from?
Wikipedia lists the following causes for technical debt. I’ve elaborated on several of them to increase clarity.
Business pressures: Business knowingly optimizes for time-to-market over code quality or completeness. Many early stage companies choose this tradeoff.
Lack of process or understanding: Businesses are unaware of the concept of technical debt, and make decisions without considering the debt implications. Shocklingly, many businesses believe code, once written, will/should work in perpetuity, even as business requirements change.
Building tightly coupled components: code is written in a way that is not modular/flexible and can’t adapt to changes in business needs. When components are tightly coupled, they are brittle and fragile, only capable of being used in a very specific context. Junior, lazy, or poor programmers are at risk for writing tightly coupled code.
Lack of a test suite: Failing to maintain a test suite encourages sloopy, quick, and risky band-aids to fix bugs.
Lack of documentation: Code is created without necessary supporting documentation. The work required to backfill documentation counts as technical debt. This is by far the easiest part to neglect in any codebase.
Lack of collaboration: Siloed information in your business means engineers have to track down multiple people to do their work.
Parallel development: When two or more features are being developed simultaneously, you’ll eventually have to merge that work together when it goes live. This merging process can create significant work. The longer the work is done separately, the further they diverge and the more work required to merge them together.
Delayed refactoring: When project requirements change, it’s often necessary to refactor portions of code written early in the project when requirements were slightly different. This refactoring is often delayed. Doing so creates future work. This happens in nearly every project and in my mind represents a base level of debt incurred no matter what.
Lack of alignment to standards: Ignorance or naivate to industry standards can create a large amount of technical debt if not caught early on. Also beware of changing industry standards. Not too long ago, XML was the de facto data type for many API’s. In the last 10 years, JSON has become the new darling.
Lack of knowledge: Developers without enough experience in the problem domain will write a suboptimal solution, unintentionally creating extra work for future developers.
Lack of ownership: Outsourced software code quality varies more than software developed in-house. Choose the wrong firm and you may end up with a codebase that requires significant refactoring.
Specification changes: All projects have evolving requirements but very rarely is time or budget alotted to compensate for these changes. And they have a nasty tendency of trickling throughout a project, invalidating or shifting abstrations slightly out of phase with their original purpose. Either you fix them in the moment or you defer this work his creates extra work down the road.
Poor technological leadership: Poor leadership comes in variety of forms but may include lack of focus/vision, mixed priorities, and/or poor communication. Lacking clear leadership, any and all of the issues in this list are much more likely to arise.
Getting rid of technical debt
The name is apt because technical debt is no different than traditional debt – like taking out a loan to buy a car. If you ignore your car payments, you incur fees and your debt grows larger. Technical debt is no different.
There are essentially three ways to reduce or eliminate technical debt:
Pay it off gradually: Make small payments and over time you’ll eventually be debt-free. But understand that every time you implement a new feature you’re taking out a new loan. It’s extremely hard to get out in front of technical debt.
Refinance: Refactoring some of your worst code to “not-as-bad-code” effectively reduces the interest rate. You still have debt but the interest rate is better.
Pay it off all at once: Dedicate time/resources to paying off all your debt in one go. For me, this is the most unrealistic of options as it’s rarely convenient to halt all progress on your software to address technical debt. Don’t expect anyone at the executive level to think this is a good idea.
In reality, the only two viable options in my opinion are #1 and #2 – pay your debt of gradually as you work on new features or refinance by doing small rewrites of code so at least the code is readable and fixable at some point in the future.
Pro tip, use tools like CodeClimate, reek, or Hound to maintain high quality of code. These tools can flag “smelly code”, call out code that doesn’t follow community guidelines, and rate/rank files so that you can see where to focus your refactoring energy.