DEV Community

Jeremy Wells for Aha!

Posted on • Originally published at aha.io

Technical debt isn't technically debt

The term "technical debt" has entered the standard lexicon of programming and software project development and has often been called out for being an incorrect metaphor. Yet it persists as a way to talk about the decisions made during software engineering. I believe it is over-used and often makes technical conversations more confusing.

Our team at Aha! has meaningful conversations about the trade-offs of software development without using the term "technical debt."

There are two common ways the term "technical debt" is used now. The first is when engineers are talking with someone who doesn't have the same technical context of a project. In this case, it may be used to smooth over the difficulty of communicating various ideas. Those ideas are typically:

  1. We can deliver feature 1 faster but that means feature 2 will take longer.
  2. This feature is going to take longer because of how we approached earlier features. You can see that those are conceptually similar other than the timing. The message is that the developer needs more time to work on these features. Calling it technical debt avoids the need to explain the finer details. You might also say:
  3. We’ve incurred technical debt so we need to clean that up before planning the next features.

This communicates that engineering is not happy with the code as it stands. We would like to work on it without needing to worry about delivering functional changes. The term is again used to avoid further explanation and scrutiny. It has entered the mindset of many people in software development that "technical debt" is something that just happens and needs repaying.

The other common usage is between engineers as justification for making certain decisions or for why the code is the way it is. Many times it sounds like an excuse. If an engineer is embarrassed about the state of the code or architecture, they may call it technical debt to signal to other engineers that the problems arose due to external factors.

I’ll address the problems with these usages, but first let’s consider what technical debt should actually mean.

What is technical debt

Let's remove all the built-up subjective meaning that has crept into the term "technical debt." Similar to monetary debt, it is a balance between having what you need now and the total costs plus risk. That balance plays on our tendency to want results right now and to play down the long-term consequences. Consider the technical debt thought process:

You need to deliver a feature.

  • Option A: Deliver the current feature as fast as possible. Your subsequent features may take longer due to more necessary patchwork.
  • Option B: Take time to build a maintainable solution for the current feature. Your subsequent features may require less heavy lifting.

Which do you choose?

Not technically debt

I believe "debt" is simply the wrong way to describe this process. With monetary debt, your choice is clear and defensible: I need this money right now or I don’t. Ideally, the loan terms and outcomes are presented upfront.

This technical choice is not so cut and dry. Is Option B better? How can we be sure that investing extra time now will pay off later? For financial assets, calculating risk is a well-established process. Software development isn't as predictable. Future product and business decisions may alter your plans altogether. A shifting roadmap may waste your big investment.

Feasibility also plays a part. Software is built in layers — some of which we don't control. Architectural concepts emerge over time and change to meet the needs of the system. Is this choice apparent and real? Is the engineer working on the problem in a position to choose? I’ve been down enough dead-ends and thought experiments to say that often an apparent choice is a false one. Those experiments might inform later changes but that does not make the current implementation a debt.

Managing uncertainty

Option A may slow down your subsequent features, but it may not. You may have truly found a faster solution. Option B may save you hassle in the long run, or it may not. Your time spent upfront may be wasted if the roadmap changes drastically.

With all of the issues around estimating software projects, it is impossible to predict with certainty. The next feature might not be developed, the requirements might change. Traditional debt has fewer surprises. You don’t take out a loan on a car, discover it was a boat and find out you actually need to take it skiing.

Consider this choice instead:

You have one feature that you will deliver first and one follow-up feature.

  • Option A: Deliver the first feature faster with non-ideal code or technical choices to make the second feature easier.
  • Option B: Take longer to deliver the first feature and perform less rework for the second.

With either option, you haven't created a debt. You've simply made decisions about the timeline. This is called planning and all good plans take some finessing.

When relaying updates to cross-functional teammates, simply explain the problems and give realistic timelines based on what needs to happen. Blaming a delay on technical debt only hinders transparency and keeps your teammates in the dark.

With your development team, technical debt should not be an excuse to hide behind. Decisions are made, mistakes happen, external forces are at work. Discuss what happened and determine your next steps. Your project cannot be bankrupt by technical debt.

Investing time

Rarely do we have all the time we need to make our code perfect. But that's okay. You may have heard that "Premature optimization is the root of all evil in programming," from Donald E Knuth. Certain ideas about code and software are worth tempering — such as making code flexible or thinking about inflection points. But too much can lead to premature optimizations, over-abstractions, and needless configuration. The code itself is easy to change. If you don’t need a reusable abstraction now, add one later.

You've probably heard the saying, "Don't Repeat Yourself (DRY)" — and how applying that too early or aggressively can also introduce friction. Remember these rules of two and three.

For small code, use the rule of three:

  • I’m implementing this for the third time — I should introduce a generic way to handle this pattern.

Why three? Because you copied and pasted the first solution and made changes. On the third go, you understand the changes and are ready to solve it differently.

For larger concepts, use the rule of two:

  • I made this model and it worked well, but now I want to use it again and it doesn’t quite fit. I should restructure this to be useful.

You’ll want to reuse the concepts that drive other parts of the codebase. They might not be in a good state for reuse, so now is the time to analyze and create layers and abstractions.

The fact that you didn’t do that before is not technical debt. This is software development.

We work fast at Aha! and we use our products to build our products. Our team is happy, productive, and hiring — join us!

Top comments (2)

Collapse
 
ccoveille profile image
Christophe Colombier

I read your article and I loved it, but there is something I dislike about it.
I may have misunderstood something, so l let me explain.

First, I loved how you described these 3 points:

Those ideas are typically:
We can deliver feature 1 faster but that means feature 2 will take longer.
This feature is going to take longer because of how we approached earlier features. You can see that those are conceptually similar other than the timing. The message is that the developer needs more time to work on these features. Calling it technical debt avoids the need to explain the finer details. You might also say:
We’ve incurred technical debt so we need to clean that up before planning the next features.

I agree with that definitions. Even if there is one point missing in that list: what about maintaining software.

For me, the most part of software life cycle, is to maintain a feature over the time.

OK, you bring a nice feature, marketing is happy, sales too. People tap on developer head "good boy", but then start the real life: maintaining what was just released.

The first part is about a few weeks: people cheers up
the second about years: people scratch their head or cry.

I maintain pieces of software for years. I'm not saying to show off my experience, but to explain what I do for living.
Some are more than 10 years old. Some are only a few months old.

For me, every single piece of code is potential technical debt. Even a piece of code you worked on last week.

As a maintainer, my goal is either to kill bugs, to remove feature, to migrate feature to a new stack or to refactor code.

We are all living in a legacy world, created by the people who worked here before, including us.

Image description

Yes, it's part of the development life cycle.
Yes, it's all about technical debt.

Technical debt exists, you can pass away when bringing a new feature. It's a choice. The fact it exists is not an excuse to do not deliver features.
Sometimes, we decide to refactor, sometimes we don't.
As you said, it's about planing.

No matter what you do, you are adding a piece of code that people will have to maintain.

Image description

My problem with your article is this:

Consider this choice instead:
You have one feature that you will deliver first and one follow-up feature.
Option A: Deliver the first feature faster with non-ideal code or technical choices to make the second feature easier.
Option B: Take longer to deliver the first feature and perform less rework for the second.
With either option, you haven't created a debt. You've simply made decisions about the timeline. This is called planning and all good plans take some finessing.

That's almost a motivator way to see things. "There is no problem, everything is an opportunity". But you only changed the way to say things. And presented the first option as positive, while ignoring your developer feedbacks about the fact the code is either uneasy to maintain or will be even more difficult to maintain.

I agree with this:

When relaying updates to cross-functional teammates, simply explain the problems and give realistic timelines based on what needs to happen. Blaming a delay on technical debt only hinders transparency and keeps your teammates in the dark.
With your development team, technical debt should not be an excuse to hide behind. Decisions are made, mistakes happen, external forces are at work. Discuss what happened and determine your next steps. Your project cannot be bankrupt by technical debt.

But I still don't understand the purpose of your article.

Technical debt is a part of software development, but I'm not sure about your approach to stop calling it "technical debt".

As if calling a dog, a "grown up puppy" would it, make it more acceptable 😀, a puppy is still a dog.

Collapse
 
jemmyw profile image
Jeremy Wells

Thanks for your comment. I've had this exact argument and I could not find a middle ground. I don't think needing to maintain something is a debt. You have to maintain your car, but that's not a debt. A debt is borrowing money to buy a new car. Maintenance is just an essential part of many things we do and I don't think its useful to associate the cost of maintenance with the opportunity that debt can create in a business context.

Badly written code that is hard to maintain is just that. Your business might look at the maintenance cost and figure that it can't cover the ongoing maintenance, but if it took a loan to hire some extra devs to improve the code quality then it would become profitable. That's now a real debt. The code is just what it is.