Managing Technical Debt

An abstract underwater and overwater picture of an iceberg

Brad Ediger

April 11, 2024

The metaphor of software as financial debt has gotten a lot of play, and for good reason. Technical debt can be a strategic tool that offers benefits in the short-term, deferring some of the costs until later, but its effects compound over time. Each deferred cost causes more friction on day-to-day development. Like compound interest, these costs can snowball until maintenance becomes an organization’s primary activity, ultimately delaying progress and evolution.

DevOps practices can help reverse this trend. By targeting the technical debt that bogs down a team, organizations can create efficiencies that free up development teams to focus resources on larger, strategic problems.

Before you can successfully manage technical debt, it’s important to first understand how to define it. 
 

What is Technical Debt?

“Technical debt” is a squishy phrase and can mean many things to different people. For example, it can stand for poorly designed code, the wrong architecture, unsafe APIs, or missing documentation. Sometimes, it simply becomes a pejorative term for “decisions I disagree with.”

“Technical debt” also can act as an observational definition: technical debt is not recognized when it’s accrued, but it’s noticed when it stands in the way of something.

For the purpose of this piece, we define technical debt — also referred to as design debt or code debt — as the accumulation of outdated or suboptimal technology and processes that can hinder future development efforts. In nontechnical terms, it’s the cost associated with maintaining or fixing software that prioritizes speed of release over long-term quality. 

How to Manage Technical Debt

Addressing technical debt within an existing product requires understanding the problems, quantifying their impact, and prioritizing accordingly. It includes designing and implementing improvements, and evolving the development organization away from the practices that allowed the problems to flourish in the first place.

There are four steps to managing technical debt:

  1. Diagnose and quantify technical debt
  2. Prevent additional technical debt
  3. Prioritize and repay technical debt
  4. Measure, repeat, and evolve your approach

1. Diagnose and Quantify Technical Debt

Successfully managing change with subjective endpoints requires agreeing on a working definition of the change to be made. The words “technical debt” can serve as a rhetorical tool, but they never provide a standalone justification for why a change is good or bad; that must come from the team.

Software teams often have a thorough mental map of some of their problem areas — and the potential improvements needed to operate and maintain it. But some deferred maintenance only becomes apparent while trying to make new changes, or when evolving a working system. Teams should prioritize improvements based on their best understanding of the causes of maintenance costs, noting that their understanding is likely to change over time.

When cataloging technical debt, quantify it by connecting it to measurable outcomes:

  • Pay particular attention to the “toil” invested in operations, and be willing to react quickly to new information.
  • Use observability practices (introducing them as necessary) to quantify the impact on development time, product quality, or other outcomes your work may impact.
  • Work together with product and business stakeholders to frame the problems and solutions in the full economic context of the business.

Remember: Not all technical debt was caused by bad decisions. Even the best decisions can become liabilities over time if the environment changes. Competition or changes in user needs can quickly and dramatically shift an existing product/market fit. A critical dependency may fall out of vendor support or accrue newly discovered security vulnerabilities, turning an asset into a liability. Adopting the principles of blameless retrospectives can help create the psychological safety net needed to make good decisions about the future, without casting doubt on the past.

2. Prevent Technical Debt

Before trying to “repay” technical debt, we recommend taking a comprehensive look at the systems (people, practices, methodologies) that produced the problem in the first place. Thinking about prevention first — “how do we keep this from getting worse?” — keeps you from chasing a moving target.

Some recommendations are worth considering in any environment, regardless of the nature of the technical debt you’re up against. These include:

  • Comprehensive code reviews
  • Automated testing and CI/CD
  • Infrastructure as Code and other practices that drive reproducible deployment
  • Detailed documentation
  • Continuous education around architecture and coding practices

Shoring up these best practices will help you make the most of more targeted interventions later.

3. Prioritize and Repay

Start planning how to alleviate the issues you identified earlier using qualitative data. Balance the expected return from improvements against their costs while considering the team's time, budget, and availability for ongoing development and operational work. Take into account factors such as:

  • Product impact: Does the issue affect customer experience, correctness, quality, or other outcomes that degrade the end-user experience?
  • Developer impact: Does the issue make maintenance more difficult or risky? At the extreme, is the developer experience creating a morale problem?
  • Future-proofing: Would resolving this issue help lay the groundwork for future desired changes?

Once priorities are set, create a roadmap to resolve the issues you identified.

For large changes, we recommend deploying fixes in a “ratchet” approach, starting by fixing new standards first before going back and resolving the older, existing artifacts. This will help your team work toward high standards on new priorities (essentially “ratcheting up” quality over time), without getting bogged down in fixing everything at once.

Starting with prevention can also help you catch issues before they become migraines. By cleaning up the parts of your code that are changed most often, you can make the biggest improvements where they matter most. This helps avoid the "perfect is enemy of the good" phenomenon; i.e., the problem of trying to perfect everything before making any changes.

Of course, you are free to prioritize based on what’s most important to you. If known problem areas are consistently pulling your team away from operations and into firefighting, fixing those issues first will ultimately improve the product and save you time in the long run.

4. Measure, Repeat, and Evolve Your Approach

Fixing a product’s deficiencies is an ongoing journey. It’s important to get comfortable with both the process and building systems so that you can learn from them.

Use quantitative measures to track your progress and justify the time spent on efforts to reduce technical debt. Did you solve the problems you originally outlined in your business case? Did it yield the expected result? You may be able to directly point to an improvement in development velocity, fewer errors, or better customer satisfaction. These data points will help inform decisions made in the next round of debt service, so it’s worth investing in observability tooling that helps gather these numbers systematically.

Aside from numbers, there are human-centered qualitative outputs that matter even if they can't be easily quantified. Large amounts of technical debt can hurt developer morale, but when that burden is lifted, the results can be a bit anticlimactic — perhaps returning to “business as usual.” Creating deliberate ways to celebrate success can boost team morale, but it also helps highlight the team’s agency to identify and fix their own problems.

Retrospectives can gather the input needed to help you make positive changes and evolve your processes. As you run this cycle repeatedly, you may start to identify patterns that lead to better improvements in your processes.

Adopting DevOps Practices

Addressing technical debt requires a strategic approach and a development culture that fosters iterative, scalable change. DevOps practices provide teams with unified tools for managing their software and infrastructure, giving them the autonomy and motivation to focus on what matters. In environments with high technical debt, this allows teams to address debt reduction alongside other important and strategic work.

Having trouble making the case for addressing technical debt or adopting a DevOps mindset? Download Your Strategic Playbook for DevOps Victory to learn how you can advocate for both within your organization.

Brad Ediger

Head of Technology

A staple in the Chicago tech scene since 2005, Brad Ediger serves as our Head of Technology with a focus on growing 8th Light's profile as a software consultancy. He joined 8th Light in 2019 when he merged his independent consultancy with the company.