Dealing with Technical Debt

The phrase “technical debt” has become a commonly used phrase in software development. Technical debt was introduced by Ward Cunningham to describe the cumulative consequences of corners being cut throughout a software project’s design and development.

Imagine you need to add a feature/functionality to your software. You see two ways of doing it, the short & quick & dirty one that will make further changes harder or the clean way that will take longer to add.

Technical debit is analogous to financial debt; the technical debt incurs interest payments which come in the form of extra effort you have to do in the future because of the quick and dirty design choice. We can choose to continue paying the interest, or we can pay down the principal by refactoring the quick and dirty design into the better design.

“Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise.” (Ward Cunningham, 1992)

technical debt

Technical debt trends to accumulate over time

The time you spend below the line takes away from the time you are able to spend above the line in terms of delivering new value to the customers.

Impacts of Technical Debt

  • Too much time maintaining existing systems, not enough time to add value – fixing defects or n * patching the existing code.
  • Takes more effort (time & money) to add new functionality – you are not able to react to what’s happening on the market in terms of competitive products.
  • User dissatisfaction – usually the functionality you’re adding is not what they expect or they struggle with the products due the amount of defects.
  • Latent defects – a defect that is not known to the customer unless he faces an unforeseen situation but at the same time the developer or the seller is aware of the defect.

Business impact of Technical Debt

  • Higher TCO – total cost of ownership (purchase price of asset + cost of operation)
  • Longer TTM – time to market to deliver new value
  • Reduced agility – less ability to react to changes on the market

Consequences:

  • Innovator’s Dilemma – once dominant in the market, your competition can develop and deliver new functionality faster than you
  • studies reveal that every 1$ of competitive advantage gained by cutting quality / taking shortcuts, it costs 4 x times to restore the quality back to the product
  • The biggest consequence – it slows your ability to deliver future features, thus handing you an opportunity cost for lost revenue

The “source” of Technical Debt

Technical debt is comes from work that is not really Done or steps skipped in order to get the product out of the door (schedule pressure).

A “valuable” source of “undone” work is generated by not knowing what’s required and we add code that performs functionality that’s incorrect. In this scenario we potentially try to tweak the functionality with workarounds by doing some “code manipulation”.

Company culture aka “that’s not my job” can sustain the technical debt (QA vs Dev vs DevOps).

Forms of Technical Debt

  • Defects
  • Lack of automated build
  • High code complexity – hard to modify/test
  • Lack of automatic deployment – reduce human steps involvement
  • Lack of unit tests
  • Business Logic in wrong places
  • Too few acceptance tests
  • High cyclometric complexity
  • Duplicated code or modules
  • Unreadable names / algorithms
  • Highly coupled code

All forms of technical debt are associated with code modify / changes and testing – DONE requires testing:

  • acceptance criteria
  • details of the design/solution
  • automate all tests
  • write unit tests and code
  • review documentation
  • test integrated software
  • regression testing of existing functionality
  • fix broken code

It is important to take sprint capacity and perform the above steps every single sprint.

Paying Off Technical Debt

  1. Stop creating new debt
  2. Make a small payment regularly (eg. each sprint)
  3. Repeat step 2 🙂

1.Stop creating new debt

  • Clearly define what “DONE” work is!
  • Know “what” the functionality is – clear acceptance criteria
  • Automated testing
    • repeatable, fast – do it every time the code changes!
    • regression tests – at least every sprint to be able to run through all your regression tests and know that whatever used to work still works.
    • new functionalities – automatically testing so that can be very clear not only works when it’s new but next sprint when we’re modifying things around functionality, it’s still working as expected.
  • Refactor – not rewriting the application/product but increasing the maintainability of the code without changing the behavior. It can be changes to make code readable/understandable (variable/methods name change to match the true meaning), taking our redundant code and creating a method. Fixing defects is different to refactoring. Refactoring is best done very small steps, a little bit at a time and actually requires automatic testing to make sure that the refactor has not changed the behavior. See Martin Fowler – Refactoring – Improving the design of existing code
  • Automated process – build & release -> eliminate human errors = reduce the source of technical debt. Let computers do repetitive work where human can easily induce errors.

Note: Those are some examples and not an exhaustive list of things that can generate technical debt.

2. Pay off existing debt

  • Fix defects – preferable as soon as you find them. Get them at the top of the list, work them off (zero bug policy), get them out of the code, refactor the code, improve the structure, make the names meaningful, reduce complexity, remove duplicate code, improve the test coverage.
  • Test coverage – it doesn’t mean only lines of code, it means testing though the logical use of the code. Test coverage is expensive so it’s most important to test the product with actually being used – the coverage we care about most because it’s how customers are using our software.

technical-debt-healthy
The goal: to pay the tech debt a little bit at a time every single sprint and avoid “technical bankruptcy”!

3. Prevent tech debt

  • cross-functional teams
  • agreed upon definition of done
  • frequent user feedback
  • discipline, transparency – not taking shortcuts

Additional links:

  1. http://martinfowler.com/bliki/TechnicalDebt.html
  2. http://martinfowler.com/books/refactoring.html
  3. https://www.atlassian.com/agile/technical-debt
  4. http://blogs.ripple-rock.com/SteveGarnett/2013/03/05/TechnicalDebtStrategiesTacticsForAvoidingRemovingIt.aspx