To deal with technical debt, do
As a team, you don’t have to — and often cannot — address the technical debt immediately after it’s been acknowledged. You might not have enough time, or experience, or skills, or alignment with other teams that you depend on. Time pressure coming from the product is real, too: as an engineering org, we can’t remake implementation of a feature simply because its initial technical design was not perfect — unless we’re purposefully going bankrupt. In most cases though, we need to move on and keep creating value. But we also need to make sure that suboptimal decisions are addressed eventually, that is, the technical debt is paid down.
There’s a clear distinction between technical debt and cruft. When you, together with teammates, come back to a piece of code that has existed in the codebase for a while but been consistently causing errors, it’s easy to label it technical debt. But to be such, it has to have certain features:
Techdebt occurs when an engineering team is making a technical decision. For convenience, I distinguish between decisions and choices: a choice is something that is made mostly inadvertently, and often recklessly. For example, if someone is declaring a lambda predicate to filter a collection and chooses to name the variable
x instead of
item, it’s a choice: easy, reversible, done without much thinking. A decision, on the other hand, has a couple important attributes:
When documenting a technical decision, keep in mind that a good measure of quality of a decision is if you-in-the-future can overcome the hindsight bias and compare what happened to what you thought would happen at the time you were making a decision. Actual outcome is secondary. In the end, we make decisions to maximize the chance to arrive at the best possible outcome. Reality is never that predictable though.
If you’re making a technical decision that makes us borrow from the future, we need to make sure that you-in-the-future can recall the most important details. For teams that aren’t yet at the performing stage I recommend documenting the full process as if it was a micro-research project:
This requires certain amount of trust and alignment in the team, which might not be there when the team is norming, so it’s a job for either a senior engineer or an EM. The key point here is that the moderator must not have stakes in any of the options, but should be accountable for the end result.
Thus, the origin technical debt is documented.
I like to emphasize that techdebt should be marked directly in the code. Always be leaving traces! It brings clarity to an engineering team: most IDEs will show the number of TODO and FIXME items, and tracking this over time can be automated. With a little effort, you’ll cultivate fastidiousness in the team.
At some point, you as a team will start getting bothered by the amount of techdebt that needs fixing. This will likely correlate with the amount of unhandled errors, SLOs going down, and team’s velocity being more and more at variance with planning. Over time, you’ll learn to address this a bit in advance, before it starts hurting. A good start would be to have these metrics, keep your sponsor informed, and pitch a project to pay down techdebt to officially get budget (time) for it.
Decide before doing. As stupid as it sounds, unless you keep it in mind and articulate it to the team all the time, it’s hard to resist an urge to “analyze by doing”. ↩