Properly managing technical debt can make the difference between a successful and a failed software project. On the other hand, ignoring or failing to identify tech debt can lead to higher development costs and lower business rewards. With so much at stake, understanding and dealing with technical debt should be a priority both for software developers and high-level decision-makers.
This article is a beginner's guide to technical debt. We explain what the term means, how it impacts software development projects, and how best to measure different types of tech debts at your organization. We also provide a range of effective practices that help keep technical debt in check.
Technical Debt Definition
Technical debt (also called tech debt, code debt, or simply TD) is the consequence of taking shortcuts during software development to achieve short-term results. A developer delays specific work to meet a deliverable or deadline, but in doing so, "gets into debt" that they must pay back with future reworks.
Tech debt typically occurs when developers rely on easy-to-implement, sub-optimal coding or design solutions to rush production. The team then must return (at some point) to this product to add more functionality or revise the code. Here are some common reasons why companies make these tradeoffs between quality and speed:
- A strategic decision to test a market fit.
- Timeline or budget limitations.
- Poor software design choices.
- Inadequate coding skills.
- Flawed business decisions.
Technical debt functions like any other type of debt: you get convenience in the short run but must give payments plus interest later. Quick fixes and subpar solutions speed up software development, but addressing these issues in the future is expensive, constraining, and time-consuming. Some common symptoms of unaddressed tech debt are:
- Product bugs that lead to performance issues.
- Code quality concerns.
- Longer release cycles and time to market.
- Reduced team agility and productivity.
- The higher total cost of ownership.
- Negative user experience.
- Exploitable cybersecurity flaws.
- Difficulties with scaling and adopting new technologies.
Ward Cunningham, the author of the Agile Manifesto, was the first to use the term technical debt to describe the impact of accruing tech issues. He also used the term "cruft" to describe the swept-under-the-rug code issues agile teams need to fix or "give back" to erase the technical debt.
Learn the difference between DevOps and agile, two methodologies that emphasize speedy code production and often put a team in technical debt.
Types of Technical Debt
While technical debt is always impactful, different types of debt are riskier than others. The debt you incur can be either deliberate or inadvertent:
- Deliberate technical debt (also called active debt) occurs when a company consciously delays the resolution of an issue to achieve a set goal (e.g., shipping an MVP to users to raise funds or get a proof-of-concept).
- Inadvertent technical debt (also called passive debt) happens by accident or due to carelessness, such as choosing the wrong platform or making coding errors.
Martin Fowler's Technical Debt Quadrant breaks down the types of tech debt further by defining two types of both deliberate and inadvertent debt:
Reckless | Prudent | |
---|---|---|
Deliberate | When the team knows about the shortcomings but has no immediate plan on how or when they will fix the issue | When developers know they are piling up interest but decide to ship now and fix the bug later |
Inadvertent | When developers blindly implement a solution without realizing that they are getting into debt | When the team learns about implementation mistakes after the release |
We can also distinguish different types of tech debt based on what the team "owes", such as:
- Architecture debt.
- Build debt.
- Code debt.
- Defect debt.
- Design debt.
- Documentation debt.
- Infrastructure debt.
- Process debt.
- Requirement debt.
- Service debt.
- Test debt.
A strategic, measured approach to the software development life cycle (SDLC) is vital to ensuring a team takes on prudent technical debt only.
Is Technical Debt Always Bad?
Technical debt is not inherently bad. Most development teams cannot avoid occasional tradeoffs between speed, cost, and quality. Technical debt only becomes a concern when you:
- Do not know about the debt.
- Ignore the accused debt.
Like a sound financial debt, a smart technical debt can have tremendous benefits in certain situations. Strategic incurring of debt can help:
- Deliver MVP solutions early.
- Determine product/market fit.
- Confirm proof-of-concept.
- Get speedy feedback.
- Meet customer needs quickly.
The impact of technical debt on a project depends on how the team addresses its outstanding obligations. Technical debt can be a valuable tool if a company:
- Makes a conscious decision to get into debt.
- Has the resources and knowledge to pay the debt off later.
Tech debt is also inevitable. The ever-evolving nature of software development means every product will require maintenance and rebuilding over time.
Technical Debt Examples
Below are three real-life examples of both deliberate and accidental technical debt.
Example 1: Prudent and deliberate technical debt
- Technical debt: Inflexible framework.
- Reason for the debt: Management sets a short deadline to reach the market quickly and get the "first mover" advantage.
- Debt description: Developers choose a framework that is fast to build on but has known flexibility issues.
- Debt payback: Refactoring the app to a more flexible framework.
In this example, the team uses a framework with known issues to achieve an early launch. Once the company meets the goal, developers go back to refactor components and remove code problems.
Example 2: Reckless and inadvertent technical debt
- Technical debt: Low-quality code.
- Reason for the debt: Lack of coding experience.
- Debt description: Developers have poor coding skills, so the team writes low-quality code as they try to meet a tight deadline.
- Debt payback: Cleaning up and rewriting problematic code.
In this scenario, getting into technical debt is not a planned decision but a consequence of poor skill. The team ships a product with bugs that run up cloud costs and increase churn. The only way to pay the debt is to hire a more experienced developer to rework the code.
Example 3: Reckless and deliberate technical debt
- Technical debt: Wrong website platform.
- Reason for the debt: Developers prefer working with WordPress.
- Debt description: Developers choose to set up a high-traffic e-commerce website on WordPress because they prefer working with that CMS.
- Debt payback: Migrating the website to a new platform.
In this example, the team uses WordPress to build an ecommerce website. The CMS's inability to handle high traffic causes customers to abandon carts and turn to competitors. The only solution is to migrate the website to a more suitable platform.
Causes of Technical Debt
There are four types of root causes for tech debt:
- Business causes. Companies sometimes stand in the way of the best development practices. Pressures to release products faster or for less money are common causes of tech debt.
- Context switches. Sometimes, plans that initially made sense stop making sense over time. Tech stacks change and systems get outdated, so teams often take shortcuts to keep the system operational and (somewhat) up-to-date.
- Development causes. Development-based causes occur when insufficient resources, poor documentation, and inadequate testing are introduced to the coding process.
- People-based causes. Lack of experience or motivation, poor communication, distributed teams, and shifting resources are common causes of tech debt.
Here are some real-life examples of tech debt causes:
- An unreasonable deadline that pressures the team into a quick release.
- Low-quality software design decisions.
- Using an easier, familiar platform instead of an optimal one.
- Lack of coding skills.
- Poor up-front definition of project goals.
- Relying on quick and risky band-aid fixes instead of complete refactoring.
- Lack of product ownership.
- Poor understanding of software architecture.
- Insufficient testing (QA and QC).
- Urgent, last-minute changes to specifications.
- Writing code without referring to supporting documentation.
- Parallel development on multiple branches that require merging in the future.
- A long series of product improvements by different developers.
Some of these mistakes can take your company beyond technical debt and into more immediate threats. Our article on disaster recovery helps prepare for the worst scenarios.
Measuring Technical Debt
Here are the metrics that help assess the level of technical debt at your organization:
- New bugs vs. closed bugs: By calculating how effectively the team eliminates tech debt, you can assess the impact of developers creating notes when fixing bugs. If new bugs outnumber closed ones, consider making some adjustments.
- Cycle time: This metric measures the time between the first commit and deployment. Short cycle times indicate a low technical debt.
- Code quality: Code quality quantifies the overall state of code by calculating cyclomatic complexity, class coupling, lines of code, and the depth of inheritance. Higher code quality scores imply less technical debt within the product.
- Code churn: This metric reveals the number of times the team rewrites or replaces a line of code after the launch. Prolonged high churn in any code area indicates that iterations have mistakes or quick fixes.
- Technical debt ratio (TDR): TDR helps calculate the overall future cost of technical debt. This metric is a ratio of the cost to fix a system (remediation cost) and the cost of developing the system (development cost).
- Code coverage: This metric measures the percentage of code that gets executed when running a testing suite. Code coverage reveals how efficient the code is by assessing the number of unused lines.
You should also check out our article on DevOps metrics and KPIs to see what else your team should track across the SDLC.
How to Reduce Technical Debt?
There is no one-size-fits-all recipe for reducing technical debt. Some tactics that work for a small startup do not always work for a team of 100+ people fixing issues on an enterprise-level system. However, there are two general directions every company should take to keep tech debt under control:
- Minimize the creation of new tech debt.
- Pay back existing debt as efficiently and regularly as possible.
Let's examine the best practices and strategies you can use to manage current technical debt and minimize new bugs.
Track and Cut Out the "Cruft"
Tracking and removing technical debt is vital, as it can survive multiple development cycles. Dealing with craft is a five-step process:
- Inventory of your tech debt: Start and maintain a list of technical debts in your company. Keep track of all instances where the team knows the code is not clean and releases that require future rework.
- Categorize the cruft: Group tech debts into workable units based on their complexity, cost of fixing, and potential impact.
- Appraise the debt: Note the consequences of ignoring each unit. This descriptive metric helps with task prioritization.
- Make the list accessible: The cruft list should be visible to all relevant parties in your company (stakeholders, marketing, sales, SecOps, etc.).
- Remove cruft early and often: Schedule regular (and frequent) times for developers to pay off tech debt and keep the product clean.
This process should be continuous and a part of every release cycle. Ideally, cruft tracking and removal should be one of the KPIs of your development or DevOps team.
Do Not Hire Low-Quality Developers
Low-quality, cheap developers create more tech debt even when actively solving existing bugs. When you account for the lost revenue from delivering a less performant product, low-quality developers cost you more than what you would pay for an experienced team.
When hiring developers, you should embrace the following two mindsets:
- Value over price.
- Quality over quantity.
Smaller, better teams perform more effectively than larger, less skilled departments. Hiring two top-tier developers is typically a better option than going with ten underperforming coders. Once you have two reliable engineers to dictate the direction and supervise releases, you can start bringing in junior-level staff members for in-house training.
Write High-Quality Code
This practice ties in with the one above; ensure the team writes quality code by measuring the following metrics:
- Code complexities (cyclomatic and cognitive).
- Lines of code.
- Class coupling.
- Arity.
- The depth of inheritance.
- Halstead complexity measures.
- Maintainability index.
- Time to write n lines.
Keeping an eye on these metrics helps ship low-debt software products. Remember to reward staff members based on the quality of their work and not on the amount of written code.
Consider also creating a coding style guide. By documenting ideal coding practices, the team will find it easier to write cleaner syntax and spend more time reviewing the code.
Use Automated Instead of Manual Testing
Manual testing is not a long-term option for controlling tech debt, which is why your team should instead look to set up and rely on automated testing. While establishing and maintaining automated testing takes time and effort, the lack of manual tests will ensure you uncover cruft more precisely and consistently.
Keep a Transparent Record of Changes
The development team should keep an ongoing record of all changes in a repository. If a problem occurs, developers can easily trace its source and document the debt.
This record is also helpful for distributed teams and highly complex projects requiring careful changes (such as migrating to the cloud or modernizing legacy software).
Create a Tech Debt Team
Not every developer or stakeholder should make decisions on tech debt. Decisions should come from qualified team members who understand the project and have experience with product tradeoffs.
Consider establishing a dedicated team within your company to handle tech debt-related decisions. This team should:
- Have in-depth knowledge of your product.
- Be transparent with the reason why the team is taking on debt.
- Have experience with making strategic tradeoffs between quality and speed.
- Outline an optimal remediation approach.
- Explain the debt's purpose to upper management in a clear, non-technical manner.
- Create a roadmap that explains who, when, and how the team will repay the debt.
- Ensure developers adhere to the plan and address the technical deficit within the agreed timeline.
Not every opportunity is worth getting into debt for, so the dedicated team should also evaluate where the option lies on the Tech Debt Wall.
Set Aside Time and Resources to Tackle Debt
Prioritizing good documentation and quality code is vital, but you also need to ensure your team has enough time and resources to deal with tech debt. Debugging products and solving issues is resource and time-consuming, so developers will not be able to solve debt-related problems if they are under non-stop pressure to deliver new features.
Experts recommend that Scrum teams allocate 15 to 20% of their sprint cycle to refactoring code and addressing debt-related bugs.
Ensure Regular Code Refactoring
Your team should perform regular code refactoring (or application refactoring) to ensure continuous payoffs of tech debt. Refactoring is a process of restructuring messy code segments to make them more:
- Understandable.
- Maintainable.
- Reliable.
Rewriting components in a software product removes redundancies and improves performance, both of which are vital to tech debt payoff. You should also consider improving the team's code review procedures and introducing code linting (automated checks of source code for programmatic and stylistic errors).
Adjust Your Company's Definition of "Done"
Develop a repeatable formula that instructs how developers conduct experiments or add new features to the product. The procedure should include a standard for manageable tech debt, so the team should not consider any product as "done" until the software meets the set requirements.
Strike the Right Balance Between Speed and Quality
As long as your team understands the need to "repay" their technical debts, taking strategic shortcuts and occasionally prioritizing speed over quality can provide a significant competitive advantage. Quick time-to-market and rapid proofs-of-concept are essential in today’s market, so use technical debt wisely. Ensure that the team actively manages this balance to avoid future obstacles.