February 04, 2020

Technical debt - a tool used to negotiate a refactor for the mess you made 6 months ago.

It is often mentioned in the company of other negatively loaded terms, such as Legacy code, bugs and anti-patterns. Does that mean that legacy code is technical debt? Is badly written code considered that as well?

Even though all the terms are used to express the faults in a piece of software, there are big differences between them.

The term technical debt was first used by Ward Cunningham. He used it to explain the importance of refactoring to his business colleagues. More specifically - the need to set extra time aside to fix code written in a hurry. He compared rushing software to taking financial debt.

“With borrowed money, you can do something sooner than you might otherwise, but then until you pay back that money you’ll be paying interest. I thought borrowing money was a good idea, I thought that rushing software out the door to get some experience with it was a good idea, but that of course, you would eventually go back and as you learned things about that software you would repay that loan by refactoring the program to reflect your experience as you acquired it.”

I think that this is an excellent metaphor. It describes tech debt as a tool rather than the results of a team’s blunders.

Money and Bits

We may need a large amount of money in advance to buy a house. Similarly, we may need some extra time in order to hit a deadline. In the first example we can take a loan to afford the purchase. In the second one, no one can give us extra time. However, we can disregard some best practices so we can move faster and meet the deadline.

In financial sense - we’ve taken money so we need to repay them with an extra sum on top in the form of interest. In technological sense, we’ve taken time - so we need to repay it with extra time.

The Interest on Technical Debt

This Martin Fowler article continues the financial metaphores. Much like monetary debt, the technical one accumulates interest. We gain more speed at the time being but in the long term we need to sacrifice speed in order to refactor it.

This is the main argument against the use of tech debt - it’s slower than doing things right. However, sometimes we don’t mind being slower in the long term if we can get some extra features ready for an investor pitch.

There is another way to look at the interest that is acquired. When we accumulate technical debt and continue development as usual, every new change will come a tad bit slower because of the interest rate.

With every new feature we are paying off interest. But without paying off the principal and fixing the root cause, the problem won’t go away. The interest rate grows and even minor changes take a long time to come to fruition.

Bad Code is not Tech Debt

Technical debt is not a matter of age. Many startups rewrite their software once they achieve stability. But it’s not a product of badly written code either. As Uncle Bob says - a mess is not a technical debt.

The decision to take tech debt should always be strategic and rational - it should have a purpose. Writing untested, undocumented and confusing code without a reason behind it is a sign of bad engineering culture.

Are we writing bad code or creating tech debt? Well, do we plan to go back and refactor that component? Is there a reason for the code not to be written in a better way? Answering those questions can give us a good idea.

What’s the Difference?

Starting a business can be hard unless there is a way to get some form of investment. Startup founders delight in the fact that they can operate with large amounts of money up front. However, in the software world, engineers strongly believe that the best amount of technical debt is zero. Why the difference in opinions?

Because tech debt is invisible and it often gets left unpaid. That leaves the developers to deal with an ever growing interest rate in combination with business requirements.

If we don’t repay a loan we will have to face the wrath of the entity that provided us with it. If we don’t repay the technical debt there will be no direct consequence. Feature development slows down a bit and developers become grumpy but nothing gets repossessed.

There is no pressing need to go back and fix the problem. It’s most likely the opposite - now that this is done we can start looking into the future.

It also depends on company size and the product being developed. Taking on tech debt is a decision that each time makes on its own. The organisation is not fully aware of the condition of each microservice or tool that is developed under its hood.

Financial debt is not a decision made on a team by team basis. Technical one on the other hand is. Even though it was decided by a handful of people, its effects could impact the whole company. This is how a problematic component in the centre of a new feature could bottleneck a release.

Tech Grants

There’s another major difference between financial and technical debt. If we never have to change that same code again the we’d never have to pay off the debt. In those rare situations it’s not debt but more of a grant.

Outsourcing companies are prone to cut corners because they are not expected to support a product in the long term. If a project is going to be handed over in a few months, the tech debt becomes someone else’s worry.

Startups, in their hopes to find market fit, need to iterate fast. They have to take technical debt in order to see what features stick and what don’t. When you have a limited runway writing clean and maintainable code is a bit lower in the list of priorities.

Debt by Accident

In theory, tech debt should only be used strategically. However, there is an exception to the rule and creating technical debt by accident is a possibility.

Nowadays, when most companies are using agile methodologies, requirements and direction are not set in stone. Complete knowledge about the product up front is a rare occurrence.

When we make design decisions we can only take into account the current requirements. As they shift, so does the need for different system or component design. Changing the direction in which we develop means that the current environment is longer be ideal.

We try to think ahead into the future and make architectures that are flexible and resilient. But as time goes the common abstractions that we have in place won’t be enough. Some changes to the original design would be required to improve the way we implement new functionality.

Tech debt is not caused by the age of the software but there is a correlation between the two. The older the product the higher the chance its architecture will need some improvement to accommodate new features. That’s why even organisations with strong technical culture need to budget time for continuous refactoring.

Summary