Back

How to Evaluate, Manage, and Avoid Technical Debt

The term technical debt refers to when developers write software that violates good architectural or coding practices resulting in structural flaws in the code that, if left unfixed, put the business at serious risk. Technical debt can also be described as “the trade-off between short-term and long-term value,” “deferred investment opportunities or poorly managed risks,” or “debt that accrues when we [developers] knowingly or unknowingly make wrong or non-optimal decisions.”

Technical Debt

The phrase was coined by Ward Cunningham in 1992 in an effort to explain to nontechnical product stakeholders the need for what we call now “refactoring.” The term technical debt is somewhat controversial because some developers believe the word “debt” gives a misleading representation about the financial side of the tech industry.

Components

Technical debt is the calculation of future costs attributable to known structural flaws in production code that need to be fixed (including both principle and interest). A structural flaw in production code is only included in technical debt calculations if those responsible for the application believe it is a ‘must-fix’ problem. Technical debt is a primary component of the cost of application ownership.

Principal

Principal is the cost of remediating must-fix problems in production code. At a minimum, the principal is calculated from the number of hours required to remediate these problems in production code, multiplied by the fully burdened hourly cost of those involved in designing, implementing, and testing these fixes.

Interest

Interest is a sum of the continuous costs, primarily in IT, attributable to must-fix problems in production code. These ongoing costs can result from the excessive effort to modify unnecessarily complex code to greater resource usage by inefficient code, and more.

Business Risk

Ignoring technical debt is a potentially dangerous decision. When must-fix problems in production code are ignored, it could cause damaging operational events such as outages, incorrect computations, lost productivity from performance degradation, and security breaches.

Liability

Liability is the cost to the business resulting from operational problems caused by flaws in production code. These flaws include both must-fix problems included in the calculation of technical debt as well as problems not listed as must-fix because their risk was underestimated.

Risk

Risk defines the potential liability to the business if a must-fix problem in production code caused a liability-inducing event. It is expressed in terms of potential liability to the business rather the IT costs that are accounted for under ‘interest’.

Opportunity Cost

Often to get one thing you must give up another. Benefits that could have been achieved had resources been committed to developing new capability must be put to the side in order to retire technical debt. Opportunity cost represents the tradeoff that application managers and executives must weigh when deciding how much effort to devote to retiring technical debt.

 

Health Factors Affected by Technical Debt

Technical debt directly affects the technical side, but if left unaddressed, can also be detrimental to the business side of a project. Violations can be categorized by which area of a project they most significantly affect.

Robustness– Stability of an application and the likelihood of introducing new defects when modifying it.

Performance– The responsiveness of an application.

Security- An application’s ability to prevent unauthorized intrusions.

Transferability– The ease with which a new team can understand the application and quickly become productive working on it (the onboarding period).

Changeability- An application’s ability to be easily and quickly modified.

Causes

The most significant contributor to technical debt is unrealistic timelines, often a result of clients turning developers’ estimates into timelines. Other possible causes are inconsistent implementation patterns, insufficient code coverage, and pure ignorance. There are four different types of causes, elaborated on below.

Deliberate

Deliberate causes are an intentional action usually caused by a constraint on time. A prudent action is different from a reckless one because in this case (prudent) there is already a plan in place for paying off the debt created by the violation(s).

Inadvertent

Inadvertent causes are unintentional in that the team is not aware that their recent developments are flawed. Reckless actions are a result of pure ignorance while a prudent situation presents the correct way after the fact (after already implementing the “wrong” way).

Reckless Prudent
Deliberate We don’t have time for design We must ship now and deal with consequences later.
Inadvertent What is layering? Now we know how we should have done it.

Cues

Technical debt is likely already present in most objects. Fortunately, there are many cues we can look for to identify it in our applications.

Social Cues

Social cues are presented by the user or the developer, and are usually the easiest to identify.

  • Developers not wanting to deal with certain parts of code
  • Inability to easily estimate time

 

Code Cues or “Code Smells

Code cues are present in the actual code and somewhat more difficult to identify than social cues.

  • “God objects”, or objects (or functions) that seem to do everything, have way too much responsibility
  • Inconsistent implementation patterns
  • “Dead code”, or unused code
    • Difficult to ensure code is never used
  • “Spaghetti code”, or code that is difficult to follow, complicated logic that cannot easily be altered or added on to
  • Copy + paste code (opposite of refactored code)

 

Testing & Deployment Cues

Testing and deployment cues are present during the QA process and represent the last opportunity to revert violations before pushing to production.

  • High friction in setting up testing environments
  • Inability to alter testing configurations (too specific)
    • Want flexibility
    • A good example of this is iOS KIF testing. Many of us (myself included) are guilty of numbering our test signatures (e.g. test_01_LogInButton()) so that the tests run in a specific order that we decide. This is a violation and creates debt because it decreases the tests’ reliability and accuracy. If one test fails, then all proceeding tests will fail inappropriately. To remediate this, ideally we should fill out beforeEach() and afterEach() to ensure that each test starts and stops at the same place. Easier said than done.
  • Frequent testing failures due to brittleness
  • Inability to simulate failure conditions
  • Difficulty in automation of deployment scripts

How To Measure Technical Debt

Once you’re aware that there’s technical debt in your app, measuring it can be a helpful way to plan out the remediation process and decide how high (or low) a priority it should take.

  1. Count number of violations.
  2. Categorize violations into low-, medium-, or high-severity.
  3. Make assumptions about time and cost specific to your case
    • e.g. Each fix takes 1 hour at a cost of $75
  4. Calculate the technical debt
    • On average, only 10% of low-severity violations, 20% of medium-severity violations, and 50% of high-severity violations require fixing
    • [(.1 x L) + (.2 x M) + (.5 x H)] x C x T x S
      L = # low-severity violations per KLOC
      M = # medium-severity violations per KLOC
      H = # high-severity violations per KLOC
      S = average application size (in KLOC)
      C = cost to fix a violation ($ per hour)
      T = time to fix a violation (hours)

How To Manage Technical Debt

Setting aside time to completely focus on remediating violations is not a pragmatic approach to handling technical debt. More realistically – especially because it’s easier to convince clients to take this route – you should chip away at your technical debt with each sprint or release by incorporating at least some debt-remedying stories into each planning session.

  1. Set strategic quality priorities – decide which health factor(s) is most essential to the application
  2. Establish reduction target and plans – create a plan to remediate violations affecting the essential health factor(s)
  3. Measure technical debt using above method
  4. Plan remediation actions – choose specific violations (as stories) to remediate
  5. Remediate violations – make the actual fix(es)
  6. Track results – remeasure after actions
  7. Report to client

Avoiding Technical Debt

Since completely eliminating technical debt is unlikely, our best option is to develop in a proactive sense so that our new code is not contributing to the already existing debt.

  • Spread awareness – the more developers and clients are aware of the effects of technical debt, the higher a priority it can take (and should)
  • Follow good architectural coding practices
  • Maintain high percentage of code coverage (we require 80% at Metova)
  • Use issue trackers to stay organized
    • e.g. JIRA
  • Pull requests and code reviews

Current State of Technical Debt

It’s basically impossible to “pay off” the current amount of technical debt. It is the contemporary developer’s duty to at least maintain the level of debt by not further contributing to it. Raising awareness of technical debt will significantly help encourage that. It is imperative that the business side (our clients) understand the effects of technical debt so that it does not grow bigger unnecessarily.

 

Sources and More

Some of these resources require you to enter a bit of information, but it is just to access the PDF. You will not receive emails from them.

Paying Down the Interest on Your Applications
Monetize Application Technical Debt
Technical Debt: From Metaphor to Theory and Practice
Pragmatic Technical Debt Management
What is technical debt?
How to Identify Technical Debt– podcast by Complete Developer Podcast (two web developers based in Nashville!)

 

 

Emmy DeLoach
Emmy DeLoach