We've all been there – slogging through a codebase you barely understand, minor changes becoming rabbit holes of complexity, and, despite your best efforts, dangerous bugs lurk around every corner.
The diagnosis is obvious: you're neck-deep in tech debt and it's time for the team to dig itself out of this hole. If only you were given time and space to clean up the worst code, all would improve, right?
But how should we convince the team to tackle the debt? There is always a backlog of competing priorities (Product features! On-call emergencies!), and tech debt that’s ‘obvious’ to engineers isn't necessarily compelling to all stakeholders. As an engineer, I've been the voice advocating for cleanup; but as a lead, I've also been the skeptical stakeholder, not yet sold on whether debt cleanup superseded other priorities.
To advocate for our work effectively, we must build a roadmap for tech debt cleanup that is compelling, incremental, and finite. Specifically, we should:
- Drop generic tech debt framing and get specific;
- Identify and communicate the business outcomes;
- Find incremental wins that build a flywheel effect;
- Locate the exit.
Drop generic tech debt framing, get specific
Start by ditching only using the ‘tech debt’ label. Every product manager, engineering manager, or tech lead worth their salt has had the experience of (correctly) rejecting projects to fix some technical detail engineers are annoyed by. When you say ‘we have a tech debt problem’, it's equivalent to saying ‘an engineer is unhappy about some picky detail (for example, the programming language used)...again.’ This is hardly enough information to base a decision on.
Metaphors like ‘tech debt’ can be powerful when both speaker and listener agree on the implications. Unfortunately, overuse of ‘tech debt’ has resulted in it not meaning much more than ‘bad code’. You could use a fresher metaphor with less historical baggage, but I have found the most rigorous, clear approach is to push past the metaphor and capture the specific value your cleanup would deliver (i.e. ‘our code is dangerous to operate, we should simplify the system to reduce incidents’).
After all, if you can't explain the concrete value delivered, directly aligned with your team's purpose and goals, maybe the tech debt isn't actually a major problem and you're just chasing the shiny and new.
Identify and communicate the business outcomes
We've decided to ditch ‘tech debt’ as our motivating description, so what should we say instead? As engineering leaders, our task is to clearly lay out the issues with our system, and the business value that fixing them would bring.
This might sound like a useless exercise just to appease managers, but I've found it forces critical thinking and helps crystallize the reasoning hidden beyond the generic ‘tech debt’ justification.
Here are a few common tech debt categories, and how I've rephrased them in terms of our team's objectives or outcomes:
- ‘The team hates working with this code’ translates to ‘Code that’s painful to work with is an engineer retention risk’. No team wants to be constantly losing experts due to burnout and having to train new members. You should be able to point at a historical pattern of departures.
- ‘The code is dangerous/confusing’ translates to ‘Code that's not safe to change is a reliability and uptime risk’. Pushing code shouldn’t be a gamble with your users’ experience, and dangerous code often takes a toll on product velocity.
- ‘It takes forever to become productive in our system’ translates to ‘Spending excessive time ramping up new engineers is a wasted opportunity’. It can cause brain drain, with engineers leaving for other teams just as they finally become productive.
- ‘Even simple features just feel hard to build’ translates to: ‘If our conceptual framing doesn’t match the way we’ve engineered the system, every feature will be 10x slower to deliver than it needs to be’. This usually looks like ‘why can’t we “just” make the system do X?’ and the answer is a long description of inessential complexity in our code.
The more clearly you can tie a complaint to problems like these, the stronger the case for addressing it is. Writing the motivations down explicitly forces clarity of thought.
Clearly laying out your argument makes it that much more likely that your work will get the green light and you'll be able to execute your vision.
Find incremental wins that build a flywheel
The execution of a plan to pay off technical debt is frequently challenging in its own right as you unwind years of confusion, mistakes, and other complexity.
My briefest advice to you is to be rigorous about discovering a plan of attack with a key aspect: incremental delivery of value so the work gets easier as you progress. Shipping regularly builds yourself a flywheel, making each step easier to achieve as you progress through your plan.
Incremental delivery is the gold standard for any long project. Projects that go many months between milestones are always at risk of getting funding pulled and leaving the job half done. You want to find regular, incremental wins that are well understood and that you can confidently execute.
If your debt is sufficiently challenging, you may find your ‘small wins’ are less certain and start to look like bets at a poker table instead. This is solvable, but it requires you to act more like a poker player or portfolio investor – i.e. placing many small bets simultaneously, seeing which approach is working best, and doubling down on that.
Regardless of the certainty of your approach, you must produce business value (the same value we identified earlier) regularly over time. The regular results justify continued effort and de-risks the project overall. A tech debt cleanup that doesn't improve the team's day-to-day life is, at best, a massive opportunity cost.
Shipping regularly also proves (or sometimes disproves!) your assumptions by testing them against reality. You might well find that some surprising revelation along the way means the wisest course of action is ending the project. Senior engineers predictably finish projects, but staff engineers also know when to pull the plug as new information comes to light.
Locate your exit
Even the best incremental plan that hits every milestone still needs one final ingredient: a clear goal at which you call the project done.
The battle against tech debt is never-ending, but good projects need conclusions. We should have accomplished substantial progress toward the business value we pitched and left our code better than how we found it.
The simplest (though least accurate) approach is to timebox the length of the work up-front. Unfortunately, this can result in projects ending just as their flywheel gains momentum, but it has the strong upside of capping the number of engineering hours you're committing to the effort.
My preferred method is to identify the main value we're trying to focus on delivering (reliability, speed to market, etc.) and stop the project once the return-on-investment seems to be no longer worthwhile. In a perfect world, you can even measure this directly (e.g. ‘We're aiming to have an average of <1 critical page per week vs. 5 per week currently’).
Regardless of our approach, the goal is to recognize up-front that the project should justify its own investment. Be bold in acknowledging when this stops and when it is time to declare victory and wrap up.
Tech debt projects are a tremendous opportunity for engineers to grow their project scoping, execution, and leadership skills. In nearly any engineering-heavy organization, senior engineers will frequently propose and lead these efforts.
Being rigorous about your motivations and the value of addressing tech debt is a valuable skill to build and improve upon. It raises the visibility of the day-to-day pain of interacting with old systems. Even if your tech debt project proposal is ignored, ‘working with the garage door up’ (i.e. publicly showing your process) allows everyone to learn from your own example.
And for tech debt cleanup projects that do get greenlit, reduce risk by producing value early and often, and wrapping up once the marginal benefit of increased effort tails off.
With any luck, you'll soon build a track record of reliably identifying and fixing the messiest corners of your codebase with engineering managers, product managers, and engineers alike.