Francisco Tarulla

Francisco Tarulla

Hello Technical Debt World

Apr 29 2025
11 min

The term technical debt is one of the most interesting (at least to me) in software development. Interesting and important, I should say, since given enough time on a project, we’re going to have to deal with it.

The quote

First things first. What’s the story behind the term Technical Debt? Let’s travel back to 1992, when Ward Cunningham wrote The WyCash Portfolio Management System and shared the following:

Shipping first-time code is like going into debt.

Let’s analyze this quote in more detail because it contains a lot.

Shipping first-time code…

Here is the context of the quote:

Although immature code may work fine and be completely acceptable to the customer, excess quantities will make a program unmasterable, leading to extreme specialization of programmers and finally an inflexible product.

Let’s consider the two perspectives it presents. On the one hand, we have the client who is satisfied that their system works as expected. On the other, we have the development team’s perspective (which we focus on in this post), which proposes the idea of immature code.

As software engineers (using an agile approach to development) we learn about the problem’s domain as we implement the system in iteratives and incrementals cycles. This approach to software development results in immature code implementations (at least in early iterations, whether of a system as a whole or of a specific feature).

Why? Because software communicates our knowledge of the problem’s domain at a given time, so it’s expected that in early implementations our knowledge will be less than in later ones; that is, we acquire more knowledge as we move forward.

Let’s look at an example: a client hired us to implement an F1 racing simulation game. We’re at a stage of the project where we’ve already modeled the F1 cars and other details; now they’re asking us to implement the car that deploys to the track when the yellow flag is displayed. At first glance, this seems like a simple feature and a first idea for this new concept could be:

class YellowFlagCar
  getter speed_limit : Speed

  def initialize(@speed_limit); end
end

Here is our first-time code! Commit, Push … Deployed! 🚀

…is like going into debt

We have integrated our new concept into the system and everything is working fine, but our client (the expert in this domain) says that the car in question is called Safety Car (in fact, in the Indy 500 it is called Pace Car –different domains, different names for the same concept–).

The good side to this: we’ve just learned something new about the problem domain! The not-so-good side: our model doesn’t represent reality, and now neither does it reflect our updated knowledge about F1 racing.

This difference between our system and reality, though trivial, will become a problem for us in the short or long term (we are going to see why in the following sections).

Congratulations, we just acquired technical debt!

The danger occurs when the debt is not repaid

Ward Cunningham explains:

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.

Continuing with our example…

The recently acquired technical debt is, so far, simple: it’s fixed by applying a (code refactor) rename. And we should emphasize so far because, if not resolved soon, we are going to start accumulating interest (like any other debt!).

What does accumulating interest mean?

It’s reasonable to assume that the YellowFlagCar class has already been used somewhere in our code base. For example, we’ve probably written something like this:

yellow_flag_car = YellowFlagCar.new(Speed.new(200, :km_h))

Again, this can be fixed by applying a code refactor rename (and don’t forget the variable names!).

Let’s keep going forward without paying the debt by adding the following (related) functionality: each race has a safety car yellow flag car assigned to it, and also we need to implement a method for getting the car onto the track. We propose the following:

class F1Race
  getter yellow_flag_car : YellowFlagCar

  def initialize(@something, @other_something, ..., @yellow_flag_car)
    ...
  end

  def deploy_yellow_flag_car
    ...
  end
end

Let’s see how we continue adding to our technical debt: now we have a method named deploy_yellow_flag_car that is harder to find by the rename and, even worse, it continues to reinforce the idea of yellow_flag_car instead of safety_car. What’s more, the method will be called from other parts of our code base, and we can already get an idea of how this bad name will continue to spread throughout our system.

We are adding complexity to our system each time we add code related to YellowFlagCar, meaning interest continues to add to our debt every minute we spend working with immature code.

Not one but many technical debts!

This minimal and simple technical debt example shows us how easily we can add interest to our debt. Imagine how quickly our problem debt develops when, in a team of three or four members, each member adds technical debt in addition to adding interest on other debts.

If this situation remains unresolved (i.e. if we do not pay our debt and our codebase continues to not communicate our knowledge properly) then we will continue to add interest until the debt becomes almost un-payable!

At this point, we’ll end up with a system that’s difficult to understand and reason about, and therefore difficult to extend. We’ll also have to make an extra effort just to communicate with the client in a meeting: they’d talk about safety car, while the development team translates to yellow flag car (and vice versa).

It will be a legacy system before it is a legacy system.

Wrapping up (just for now)

Up to this point we have reviewed the definition of Technical debt and presented a simple example of acquiring technical debt. We’ve also seen how easy it is to continue increasing it.

In a future post, we’ll share best practices for honoring (ie. repaying) our debts.

Post Data

It’s worth noting that, in this article, we present only one type of technical debt, related to the original metaphor, which refers to the one naturally acquired in the iterative/incremental process of software development (including the learning process). In this type of technical debt, we add to it by writing code based on misconceptions (not only related to names but also the behaviour of the different elements in our software), while fixing the gap between our software and our domain knowledge is repaying it.

Also note that, in our example, we acquired the debt once in production without our prior knowledge. However, it could have happened that, even though we had learned the correct name for the concept before production, we prioritized moving forward and having client feedback. In any case, we now have a debt; it’s already in our codebase, ready to add interest.

Over time, since the conception of this metaphor, many authors delved into this concept. Just to name a few: Martin Fowler proposes dividing it into 4 different types in his article Technical Debt Quadrant; Uncle Bob tries to prevent the use of the metaphor for everything in A Mess is not a Technical Debt; Agile Alliance explains what technical debt is not in Introduction to the Technical Debt Concept while also proposing different debts: Quality debt, Feature debt, etc.

References