When we save for the future, we tend to start by cutting out small and unnecessary expenses. Not buying $5 lattes, avoiding branded items at the supermarket, or getting in a couple of drinks at home before going out. These all seem like sensible ways to claw back a bit of cash at the end of each month.
However, most of us fail to take action on longer term opportunities for wealth creation. We pay compounding interest on bad debt, our savings get eroded by inflation, and we purchase liabilities rather than assets.
We mean well, but we struggle to save $100 per month while missing opportunities that could grow to $1,000,000 over a lifetime. We fix the wrong problems.
The wrong problems in code
As developers, we tend to fix the wrong problems in our code too. Take the following basic example:
This function returns true if each number in a given array increments by one from the number that preceeded it (e.g. 1, 2, 3), and false if not (e.g. 1, 3, 2). It's a little verbose, but with 15 minutes of refactoring we could transform it into something more clear and concise.
If we did refactor for clarity and brevity, it'd be tempting to think we'd spent our time well. However, taking into account the wider context of a whole codebase, we'd see the biggest issue isn't how the function is written, it's actually the function name. getsBigger is both misleading and ambiguous.
Any developer seeing getsBigger in use is likely to mininterpret its operation from its name alone. This wastes time, reduces the readability of the codebase, and increases the likelihood of accidental bugs through misuse. By contrast, the code within getsBigger is actually a non-issue. It works, it's reasonably efficient, and it's unlikely to need to change.
Most developers will opt to rewrite the function, but the best value improvement we could make to getsBigger would probably be to rename it to clearly convey its purpose. Something as quick as a find and replace across the codebase would mean every future developer could confidently use the function without needing to see how it's implemented. We'd be making a small change that pays off big over the long term, and we'd be fixing the right problem.
This is not laziness! Refactoring without considering value is the lazy way to go. Taking time to think about improvements that deliver long term compounding benefits takes effort, and the right refactor to make isn't always the obvious or large one.
Software development is very expensive. As an industry, we need to be better at recognising this, and making pragmatic tradeoffs between time invested, and value delivered. We need to resist the vanity improvements, and look at the larger context to see where the value lies.
How to choose what to refactor
How best to improve a codebase is highly subjective, but I've found a good way to keep myself improving the right things is to regularly ask myself these sorts of questions:
- How does this software affect the bottom line of the business?
- What's the roadmap for this software?
- How will the demands on this software change?
- How will the number of people using this software change?
- Based on its current trajectory, what will this codebase be like in 6, 12, and 24 months time?
- Which parts of this codebase churn, and which don't?
- What's the development team like that work on this project?
- What aspects of this codebase make it difficult to get work shipped?
- What parts of this codebase confuse developers?
- Which parts of this codebase require esoteric knowledge?
Thinking about these for your own work will help you identify better refactors to make, and more effectively fight the never-ending battle against pernicious technical debt on your software projects. Good luck, and happy refactoring!
If you enjoyed this blog post, you should follow me on Twitter, where I'll be sharing similar content in the future.