How modern engineering teams approach refactoring
It’s common among engineering teams to favor code progress over code quality. New features often get priority because of high business pressure. Therefore, they only refactor code in case of emergency.
However, when you reach this state of emergency, it’s often too late to save your code without having to do a complete rewrite. Once your codebase ossifies, it takes exponentially more resources to fix the situation than implementing a continuous refactoring process would have. This means refactoring code continuously.
How can teams implement this process?
How do you know it’s time to refactor?
Right now. The best engineering teams spend 10-30% of each sprint addressing technical debt. Check out our article on tech debt budgets to learn how to create one.
But what are red flags to look out for to help us focus these refactoring efforts?
Poor code performance or failing tests
One of the most obvious red flags is test failing seemingly randomly. Tests failing inconsistently is a clear code smell. Poor code performance are also a clear indicator that some refactoring is due. These typically affect your user experience, so they’re worth prioritising ASAP.
Adding new functionality becomes harder
A feature your team thought would take a sprint ends up taking two, or three, or more. Generally speaking, codebase complexity is so high that it’s near impossible to predict sprints reliably. This is a clear sign that these parts of your code need to be refactored.
Increasing number of bugs
Your bug rate increasing every week is another clear red flag. You may even want to track the average number of bugs introduced with each new feature. That number creeping up isn’t a good sign. Even worse, you could end up in a situation where fixing a bug introduces new ones. That’s not a good spot to be in. Invest in refactoring before you get there!
The codebase is hard to understand
It’s hard to measure code readability, but using the right metrics to measure tech debt makes it easier. On top of that, the most experienced engineers on your team will recognise it when they see it – listen to them. If the time they spend understanding is an unusually large component of the complexity of shipping a feature, bake in some refactoring time.
Duplicate code, inconsistent best practices, and messy architecture
Ever seen a bad coding pattern spread across the codebase like a virus? This can happen even when fresh best practices are introduced. That’s because if you don’t refactor every instance of code that should be using this best practice ASAP, engineers will be confused as to which instance of code is correct, and duplicate it. The same goes for poor architecture decisions, or violations of the “Don’t Repeat Yourself” (DRY) principle – they spread like wildfire. The only cure to this virus is refactoring.
Refactoring process checklist
Here’s a handy checklist to follow when trying to create a plan of attack.
- Use your roadmap to focus on code you’ll be working on next
Don’t try to fix your entire codebase at once. Code refactoring is a gradual and continuous process. Pick an area of your codebase you would like to improve. Often, you’ll want to focus on improve code in parts of the codebase that your feature roadmap will have you working on.
- Focus even more by prioritising problem areas
Once you know where your roadmap will have you working on, identify the parts of the codebase causing the most productivity, quality, and morale problems.
- Make sure you have sufficient test coverage
Ensure you have sufficient tests to confirm the refactored code works as intended. Without this, you’re in the dark. Test-driven development (TDD) is the best way to guarantee you don’t end up in this situation, but failing this, you can also create tests prior to refactoring the code. Refactoring is an excellent opportunity to increase your test coverage.
- Align refactoring with business priorities
You absolutely want to prioritise the maintenance work that moves the needle for the business. The first two tips in this list are a great start, but you can make even smarter refactoring decisions by asking these questions:
- Is this piece of code part of your core business?
- How close is this code to causing an emergency?
- Do engineers work in this code often?
This is a continuous process you’ll want to refine over time. Here are a few tips to do just that.
How to improve your refactoring process?
Become great at creating tech debt budgets
We’ve written extensively about that topic. Be sure to pay back the tech debt that lives in the code you’ll be working on in the selected period. You don’t need to pay back all tech debt by refactoring all your code, it’s impossible. Focus on what will move the business forward.
Continuously keep track of what you could improve in your codebase with Stepsize.
We built Stepsize to solve this entire problem. Engineers use it to document and fix issues right in their editor. They replace meaningless TODO comments with issues linked to code and kept in sync with their issue tracker. It empowers them to be good boy/girlscouts and implement the perfect process to manage tech debt so they can ship better software faster.
Introduce new tools to measure tech debt metrics
We all love metrics! They allow us to track our codebase health and make better-informed decisions about code refactoring. For instance, measure the number of bugs and the time it takes to resolve those bugs. A higher bug resolution time indicates complex bugs and the need for code refactoring.
Application performance monitoring helps you to detect poor-performing code and measure the performance of core functions in your code. However, other categories of tools like Git analysis, engineering performance, or code quality tools are all part of a best in class stack to maintain a healthy codebase.
To sum up…
Effective refactoring isn’t an event, it’s a continuous process. Look out for code smells, use tech debt budgets, and implement the right tools to help you identify refactoring opportunities that are most important to your business.