When most people use the metaphor of technical debt to describe their choices in programing, they conveniently leave out the second half of the metaphor. I occured to me recently that this could be the key to actually make the metaphor work.
When you take on debt in the real world (car loan, home loan, student loan, credit card, etc), it doesn't come for free. The issuer of the debt attaches terms that describe how the debt should be repaid, when it should be repaid, and what happens if it isn't repaid. These are essential parts of the agreement between the lender and borrower.
When we take on "technical debt" (or at least when we take shortcuts and call it technical debt 🙄🤣), we're "borrowing against future productivity". I like this description from Richard Dalton1:
Technical Debt is not a catch-all term for bad code.— Richard Dalton (@richardadalton) March 28, 2019
It is deliberate borrowing against future productivity. You must know when you're doing it and why.
Bad code that bites you long after you've written it can't be retrospectively labelled Technical Debt. It's just bad code.
I like the framing of "against future productivity", but for the purpose of this post, we'll hand wave over the actual point of the tweet about retroactively labeling bad code. (In fact, later in this post, I'll be saying something close to the opposite of that 😅).
When teams use the technical debt metaphor, instead of discussing how it will be paid off, it is common to just document the debt for some future re-evaluation, i.e. some future sprint planning session. Imagine asking a bank to approve a loan and telling them that you'll come back to the part about how (or if) you'll pay it back!
So how do you talk about the terms for tech debt?
Nat Bennett writes about taking on new work in the Simpler Machines newsletter:
Ask, "How will we know that this does what we intend?" before beginning a piece of work, whether it's a line of code or an architectural design. Ask, "How can we build a prototype for this?" before you spend more than a few days working on a design document. If a technical discussion lasts more than a few minutes, ask, "How could we get some more information that would help us make this decision?"
This context in Nat's post is about testing strategies, but we can adapt the same mindset to normalize asking "how will we come back to this?" when we talk about technical debt.
Of course, we cannot know how how we'll pay back debt if we don't know what we're taking on (or worse, if we fail to identify when we're taking it on). That's the hard part. In fact, most of the time I've been involved in conversations about technical debt, it's when someone describes existing code needing to pay off debt, rather than the point at which the debt was incurred. This is interesting, because existing debt without a payment plan can live indefinitely. After all, nobody is asking to pay it off, and the uneasy feeling we get when we think about the mounting debt can be buried in the bug tracker.
In these situations, it's easy to shift the goal post and start reframing debt as the status quo. This mental trickery turns a conversation about a payment plan into a conversation about an "engineering project", which, as we all know, is pronounced "non-customer facing work", or, in some dialects: "no business value". In other words, sloppy usage and practice of the debt metaphor is a downward spiral into "everything is terrible and we can't have nice things."
But there is hope!
If we can fool ourselves into turning technical debt into engineering projects, we can also do the opposite! There are two ways to tune yourself into this mindset:
When you hear talk of engineering projects (often called infrastructural projects), see if you can trace the origins of the demand for this project. Ask: Why do we need to do this project now? What socio-technical decisions led to the need for this project? Was it preventable? How?
This is the part where I directly contradict Richard's tweet above. In theory, you can't retroactively call "bad code" (or bad decisions) technical debt after the fact. But in practice, I'm not sure it's useful to be dogmatic about it either. In practice, all sorts of things are labeled technical debt, and the label is rarely applied consciously at the point it's taken on. So why shouldn't we work backwards from the demand for engineering work to the point at which it could have been a conscious decision? Why shouldn't this retroactive labeling be a tool in our kits for justifying and accounting for engineering work?
When you hear the words "we'll come back to this" or "for now", ask "when" and "how". Press yourself and your team to make a plan and get project managers involved.
If you're looking for where tech debt comes from, look for the words "for now" and "workaround".— Mehul Kar (@mehulkar) September 12, 2019
If you're looking for why features are moving slowly, look for the words "long term" and the sanctimonious "the REAL fix".
This is easier said than done, but at some scales, I think it has promise.
Writing this post sparked some new ideas that are worth thinking about. Notably: taking on debt isn't bad per se. In fact, taking on debt is a common technique to increase total value. This is also a significant part of the conversation, both at the point of incurring it (will this increase future returns?), and when it's noticed (did this increase returns?). These are tricky questions and I don't know of a way to answer them. But the truth is that even in the world of finance, quantifying risk and return have some element of subjectivity. Financial models work not because they're absolutely true, but when they're applied consistently. If we are to take technical debt seriously, I think we can go a lot further with how we talk about it, and part of that is getting over the hump of thinking that code quality is too subjective to measure.