How to Maintain a Healthy Codebase while Shipping Fast
One of my greatest privileges building Stepsize has been hearing from hundreds of the best engineering teams in the world about how they ship software at pace while maintaining a healthy codebase.
That's right, these teams go faster because they manage technical debt properly. We're so used to the quality vs. cost trade-off that this statement sounds like a lie—you can't both be fast and maintain a healthy codebase.
Martin Fowler does a great job at debunking this idea in his piece 'Is high quality software worth the cost?'. Spoiler:
High quality software is actually cheaper to produce.
The lessons I'll relay in this article are drawn from the combined centuries of experience of these 300+ software engineers I've interviewed.
Why bother?
As Adam Tornhill and I recently discussed in our webinar, software has well and truly eaten the world. And look, if you're here, this will probably sound like a cliché to you. In this case, it's because it's true. Look around you, can you name one object that didn't need some form of software intervention to be manufactured, purchased, or delivered to you?
Software companies live and die by the quality of their software, and the speed at which they deliver it.
Stripe found that 'engineers spend 33% of their time dealing with technical debt'. Gartner found that companies who manage technical debt ship 50% than those who don't. These data points may seem a little dry, but we intuitively know they're true. How many times have we estimated a feature will be delivered in a sprint, only for it to take two? Now take a moment to extrapolate and think about the impact this will have on your company over a year, two, or its entire lifespan.
Is it not clear that companies who prioritise technical debt simply win?
A simple framework to achieve these results
Google around for 'types of technical debt', and you'll find hordes of articles by authors geeking out about code debt, design debt, architecture debt, process debt, infrastructure debt—this debt that debt.
These articles are helpful in that they can train you to recognise technical debt when you come across it, but they won't help you decide how to deal with each piece of debt, let alone how to manage tech debt as a company.
The only thing that matters is whether you're dealing with a small, medium, or large piece of debt.
The process for small pieces of debt
This is the type of tech debt that can be handled as soon as the engineer spots it in the code—a quick refactoring or variable rename. Engineers don't need anyone's approval to do this, or to create a ticket for it to be prioritised. It is simply part of their jobs to apply the boyscout rule coined by Uncle Bob:
Always leave the code better than you found it.
This is table stakes at every software company who have their tech debt under control that I've interviewed. It's mostly driven by Engineering culture, gets enforced in PRs or with linters, and it is understood that it is every individual contributor's responsibility to handle small pieces of debt when they come across them.
The process for medium-sized debt
The top performers I've interviewed stress the importance of addressing technical debt continuously as opposed to tackling it in big projects.
Paying off technical debt is a process, not a project.
You do not want to end up in a situation where you need to stop all feature development to rewrite your entire application every three to five years.
This is why these teams dedicate 10-30% of every sprint to maintenance work that tackles technical debt. I call the tech debt that is surfaced and addressed as part of this process medium-sized debt.
To determine what proportion of your sprint to allocate to tech debt, simply find the overlap between the parts of your codebase you'll modify with your feature work, and the parts of your codebase where your worse tech debt lives. You can then scope out the tech debt work and allocate resources accordingly. Some teams even increase the scope of their feature work to include the relevant tech debt clean up. More in this article 'How to stop wasting time on tech debt'.
For this to work, individual contributors need to track medium-sized debt whenever they come across it. It is then the Team Lead's responsibility to prioritise this list of tech debt, and to discuss it with the Product Manager prior to sprint planning so that engineering resources can be allocate effectively.
The process for large pieces of debt
Every once in a while, your team will realise that some of the medium-sized debt they came across is actually due to a much larger piece of debt. For example, they may realise that the reason the frontend code is under-performing is because they should be using a different framework for the job.
Left unattended, these large pieces of debt can cause huge problems, and—like all tech debt—get much worse as time goes by.
The best companies I've interviewed have monthly or quarterly technical planning sessions in which all engineering and product leaders participate. Depending on the size of the company, Staff Engineer, Principal Engineers, and/or Engineering Managers are responsible for putting together technical proposals outlining the problem, solution, and business case for each of these large pieces of debt. These then get reviewed by engineering and product leadership and the ones that get prioritised are added to the roadmap.
How to achieve this more easily
In order to be able to run this process, you need to have visibility into your tech debt. A lot of companies I've spoken to try to achieve this by creating a tech debt backlog in their project management tool or in a spreadsheet.
It's a great way to start, but here's the problem: these issues will not contain the context necessary for you to prioritise them effectively. Not only do you need to rank each tech debt issue against all others, you also need to convincingly argue that fixing this tech debt is more important than using these same engineering resources towards shipping a new feature instead.
Here's the vicious cycle that ensues: the team tracks debt, you can't prioritise it, so you can't fix it, the backlog grows, it's even harder to prioritise and make sense of it, you're still not effectively tackling your debt, so the team stops tracking it. You no longer have visibility into your debt, still can't prioritise it, and it was all for nothing.
We built Stepsize to solve this exact problem. With our product, engineers can track debt directly from their workflow (code editor, pull request, Slack, and more) so that you can have visibility into your debt. Stepsize automatically picks up important context like the code the debt relates to, and engineers get to quantify the impact the debt is having on the business and the risks it presents (e.g. time lost, customer risk, and more) so that you can prioritise it easily.
You can join the best software companies by adopting this process, start now.
Check out this video to see how Stepsize works👇