Let’s start with a question…
What would your perfect code-base feel like?…
I can hear a melodious chorus of bird song as I sit cross-legged in the forest. I open the source code in my laptop’s IDE. Instantly appearing on screen, the code’s logical structure holds no surprises. With a flick of a key-combo I can find where any feature is implemented. When I read the code, it communicates its purpose to me clearly and concisely. Tests are comprehensive and act as documentation and a safety net, allowing me to make changes with confidence. I welcome last-minute changes to requirements.
…And what about your worst nightmare?
A single monitor glows within the dimly lit office. After 27 commands and 3 visits to stackoverflow.com, the development environment is installed. Within my IDE, spaghetti-code lurks in clumps so big and dense they start to take on the properties of a supermassive black hole. If-statements are nested so far that they extend beyond the width of the screen and stretch off to infinity. I summon the courage to change the colour of a button, which involves modifying 14 classes, 5 config files and signing an insurance waiver. I hit “compile” and wait. The staccato echoes of the wall clock’s tick-tick-tick echoes in my head: 8:02pm and 13, 14, 15 seconds. Done. The program? Crashes on start-up. Tests? None. Deadline? Tomorrow. A tear rolls down my cheek and lands with a plop in my mug of cold tea.
Another way of viewing this nightmare is technical debt that’s gotten out of control. Much like a credit card, if you keep up with the re-payments and your balance is low, then that’s ok. But if the amount you owe keeps growing it can become unmanageable.
Similarly, the more you let code slide towards the nightmare vision, the harder it will be to change.
There’s a lot we can do to limit the amount of technical debt we introduce. Some examples are comprehensive automated testing, continuous refactoring and paying attention to design principles (e.g. SOLID)
I’m lucky enough to work with colleagues that take technical debt seriously.
But we still get new technical debt. And I think we always will.
That’s because: whilst code may stay the same, the world around it does not. For example:
- Nice new language features are introduced meaning existing code can be made more concise.
- Having an “aha!” moment where a better design reveals itself.
- Finding out you can remove some of your code by using a new library that does the same thing.
Getting A Grip
So if some technical debt is inevitable, how do we stop it from building up? In the past I’ve generally come across two approaches:
1. The Boy Scout Rule
We know technical debt is there, so we tackle it as part of our “normal” work. When we’re in the code making a change, we’ll tidy up around us:
Leave things better than you found them.
This is often called the “boy scout rule” (although people of all genders are equally capable of applying it). It’s a powerful technique and I would recommend it to anyone. But I don’t think it’s enough on its own. Areas of the code that are changed regularly will be improved, but if code that’s never touched needs a refactor what will make that happen?
2. Perfect Planning and Prioritisation
We know technical debt exists, so we’ll make an exhaustive list of it all. We analyse, size and prioritise each item.
Making a list might make us feel better but there are some downsides:
- Not long after the list is written it’s already out of date.
- The work required puts people off adding to the list.
- The value of these technical tasks can be hard to calculate, so often things with more tangible benefits are prioritised instead.
This all adds up to one thing: technical debt building up.
Best of Both Worlds
Both of the approaches I described have good parts:
The Boy Scout Rule results in the code base continuously improving and encourages breaking work into smaller, more manageable chunks.
Perfect Planning and Prioritisation seeks to make technical debt visible, raising awareness. It also gives people a sense of progress when they complete an item.
Is there a way of having the best of both worlds? Some sort of compromise?
A Compromise — Five Minute Jobs
My team has been trying out such a compromise for a few months:
We apply the Boy Scout rule when we’re in the code but also maintain a list of “five-minute jobs” to tackle other technical debt that we know about.
A five-minute job:
- Doesn’t have to literally take five minutes, but it must be quick. Half a day at an absolute maximum.
- It must be uncontroversial, clearly improving the code and thus reduce technical debt if implemented.
- Last but not least, five-minute jobs are not scheduled or estimated. We build slack into our sprint to allow developers to pick them up when they see fit.
Five-minute jobs are short, clear and unplanned.
Overall, the consensus is that it’s a positive change to our process.
- Technical debt feels under control.
- Developers enjoy the freedom to tackle the technical debt that slows them down.
- We’re still regularly delivering new features (in fact, the reduced technical debt makes it easier).
But there are some “gotchas”. We need to be careful that the team can understand what each five minute job is from its description. And we’ve found that some tasks are just too big to make into five-minute jobs and need breaking-down or planning.
The idea of five-minute jobs didn’t come to us in a flash of inspiration but was instead a product of experimentation and adaptations of existing ideas. So it’s not surprising there are some familiar concepts at its core.
Smaller = Easier
When problems are broken down, we often find them easier to solve.
We get a nice sense of achievement ticking small things off regularly, as opposed to labouring on a big task.
The Power of Incremental Change
Small improvements seem insignificant on their own. But over time they add up and can be powerful.
Reducing work to such tiny tasks made estimation no longer relevant because delivery is so predictable. There’s lots of discussion related to this in the no-estimates debate.
Letting teams have slack (also known as “spare capacity” or “contingency time”) is important. In our case it allowed us to “pull” five-minute jobs whenever we were ready, much more efficiently than if we’d planned it all up-front. This reminds me of concepts like flow and resource utilization (illustrated by this video by Henrik Kniberg).
Giving talented, motivated people the right tools and trusting them to do the right thing can make great things happen.
How do you handle your technical debt? Maybe you’d like to try a few five minute-jobs of your own?
Or perhaps you’ll follow the example of the agile manifesto:
We are uncovering better ways of developing software…
…and come up with your own approach?