'Technical debt' is a term we throw around constantly when building software. It's part of our daily lives. But what we mean when we say ‘technical debt’ has, in some instances, extended beyond the original metaphor, and that’s totally fine. That initial definition is immensely useful and we don’t intend to open yet another debate about whether it’s appropriate.
However, we do think it’s important to develop a shared understanding of the term and to extend its definition to encompass every aspect of what we mean by ‘technical debt’. We can then implement the right tactics to manage it effectively—even if we risk a broader definition over-stretching the original metaphor.
We’re going to offer the simple, yet (we think) complete, definition of technical debt that we’ve been working with at Stepsize. We help high-growth software companies measure and manage technical debt and we’ve found that we need to be a bit more concrete about exactly what it is we’re measuring.
Technical debt is code written yesterday that's a burden today.
At first glance, this might sound as if code becomes technical debt the moment it’s pushed to production. As if everything in your codebase is technical debt.
That's because it kind of is.
The code that engineers write to solve a problem is based on their current understanding of that problem. It sounds obvious, but it has profound implications. Even if the perfect engineers found the perfect solution to a problem and coded it perfectly, their understanding of the problem will evolve—and quickly.
But left unattended, their code won't.
This means that it’s soon no longer appropriate. It happens all the time, and much faster than you might think in a high-growth environment.
Because 'software exists in a world of uncertainty' (Martin Fowler, ‘Is High Quality Software Worth the Cost?’), you'll ship your Minimal Viable Product (MVP) or feature quickly, to learn from your users as soon as possible. And if they request a feature that you didn't quite see coming, that could raise uncomfortable questions about the entire framework you used to solve the problem you thought you were tackling.
Sometimes, you can simply add to your code or rejig it a bit to match your new understanding of the problem. But as you do this, your code accumulates cruft, and therefore technical debt.
Developers find poor quality code significantly slows them down within a few weeks.
‘Is High Quality Software Worth the Cost?’ by Martin Fowler
As Martin Fowler argues, it’s worth investing in code quality as it will speed up feature development. If our new understanding of the problem at hand has changed significantly, we need to take action, or the rest of the team will have a hard time understanding our code and productivity will suffer. In that sense, if it isn't well maintained or thrown away, code we wrote yesterday will inevitably become technical debt and slow our whole team down.
However, not all tech debt is at the same stage of maturity—and that's where the second half of our definition comes into play.
No matter how incredible our engineers and architects are, no one can accurately predict the future, especially in a high-growth environment. That's why we've replaced the waterfall method with Agile development; software isn’t built like cathedrals.
It's often the case that it can take a year of programming on a project before you understand what the best design approach should have been.
Technical debt quadrant, by Martin Fowler
But the best in the business do know how to handle such high uncertainty. They use unit tests when appropriate, continuously refactor code that has accumulated too much cruft, and they won't accept messy code as technical debt. Here's a free VSCode extension to help you do all that directly in your editor.
Often, the very real restrictions we face—project deadlines, budget constraints, and more—mean that we can only afford to pay back the most pressing technical debt that’s getting in the way of our most immediate and predictable goals—the code that's a burden today. This is what drove us to write about how to stop wasting engineering time on technical debt.
In such uncertain conditions, the only tech debt that we can really do anything about is that which gets in the way of our most immediate and predictable goals.
Given our broader definition of tech debt, the only real solution is to continuously address the most pressing technical debt as we continue to deliver features at pace for our customers. That's the tech debt that we know for sure will slow us down today, or soon.
Distinguishing between different types of technical debt—most notably the reckless and prudent kinds—is another important factor in doing this successfully. I have an article in the works on this very topic, so stay tuned!
I'd love to read your thoughts on this definition. Do you have a different one you use? Let me know in comments!