Refactoring is a daily practice for developers, but it’s often viewed as complex. According to stats from my last project, bad refactoring ranked 6th among the top causes for introducing bugs. However, avoiding refactoring isn’t the solution—it's an essential process for maintaining the quality and scalability of a software project. Over time, technical debt builds up, slowing down development, demotivating stakeholders, impacting product stability, and making it harder to scale. Refactoring is a must-have tool to not only prevent the growth of debt but also reduce it in more extreme cases.
I recently attended the Mobilis in Mobile conference and took a course on this topic. I’d like to share some key insights on refactoring and how to master it. This topic will be explored in three articles, starting with this one, which focuses on technical debt.
Technical debt comes from the trade-offs made during development—often driven by deadline or budget—that end up hurting the quality of the code. These shortcuts may save time in the short run but eventually slow future development and make the code harder to maintain.
Since refactoring is often the solution to technical debt, let’s take a closer look at some common causes of this debt.
When fixing a bug, you have two choices: tackle the root cause or patch it up. The quick fix can seem appealing, but it’s like sweeping dust under the rug. The problem isn’t gone—it’s just hidden, and it will come back, often at the worst possible time, complicating future code changes. Just like replacing the rug would spread the dust all over the room, touch the workaround code can spread issues through your entire project.
POCs are great for testing ideas quickly, but if you start building an application directly on top of a POC without firming up the foundations, you’re inviting technical debt. It’s like building a house out of straw and then adding bricks on top: it’ll collapse sooner or later.
In long-term projects, experienced developers become the gatekeepers of code quality and development standards. They have a deep understanding of the most complex or even obscure parts of the project. With their experience, they know how to quickly fix certain bugs because they’re familiar with the product’s history and recurring problems. When they leave, they take with them undocumented, valuable knowledge, leaving the remaining team to deal with this new debt.
It’s like when previous homeowners knew the exact trick to closing a stubborn door, but when they move out, the new occupants are left scratching their heads, eventually having to fix it and pay the bill.
Sometimes, a feature is developed without knowing for sure if it will meet user needs, or maybe it was useful at one time but became obsolete after an update. If no one bothers to remove it, it turns into technical debt by just sitting in the code. As the rest of the project evolves, this stagnant feature becomes a risk. Over time, it can even become a vulnerability, offering hackers a backdoor into your app.
It’s like leaving an unused room in your house without ever cleaning it: it eventually becomes a haven for pests.
Even if nothing changes, the passage of time itself creates technical debt. Technologies evolve, development practices improve, and the code that was once top-notch slowly becomes outdated. Over time, libraries deprecate functions and replace them with more efficient, modern alternatives. Even perfectly clean code can turn into a problem if it isn’t regularly updated.
Think of it like buying the best VCR in 1980, convinced it’ll serve you forever. Fast forward to 2024, and VHS tapes are obsolete. You don’t have a choice anymore: it’s time to switch to streaming platforms. That’s exactly what happens to code when it’s not kept up to date.
When you develop software, you try to anticipate the future to ensure the project is scalable. But when the strategy changes midway, the initial foundations can become obsolete, leaving you with technical debt.
Imagine building a sleek, futuristic home with glossy white walls and hidden wiring, only to realize before moving in that you now want an industrial style with exposed pipes and concrete walls. The house still works, but the clashing styles create a disconnect: this is technical debt in the software world.
Developers often have their own ideas about the best technical strategy. But if those visions aren’t documented or evolve over time, you’ll start seeing divergences. When the team isn’t aligned, even small differences in code implementation can snowball into larger maintenance issues.
Imagine two carpenters laying down flooring: one lays the boards in one direction, and the other lays them the opposite way. It won’t align in the middle, and now you’ve got to decide whose work to undo or compromise. That’s what happens when developers follow different approaches in a project.
The “broken window theory” applies to development too. If a window is broken and left unfixed, eventually all the windows will break. In the same way, when a project accumulates too much technical debt, developers can lose motivation to write clean code, which leads to even more debt.
Technical debt is unavoidable, but there are plenty of strategies to manage and minimize it effectively.
My favorite way to assess the health of a project is by analyzing its bugs. Don’t just fix them: take the time to understand their root cause. This will allow you to implement preventive solutions. The more you study a bug, the better equipped you are to prevent it from coming back.
Document your technical standards. This not only gives you a better view of your global strategy but also aligns the whole team on the best practices for the project. Update these standards as needed, covering both the overall strategy and project-specific details.
Standards aren’t enough on their own. It’s crucial to train developers on best practices and familiarize them with the existing standards. This is especially important when new team members join.
Remind developers why it’s important to follow best practices. This keeps the project’s quality high and builds a culture of excellence. The “why” is key to getting buy-in for best practices.
Dedicate some time each week to improving the quality of the project. This helps manage and reduce accumulated technical debt over time. Use this time to implement the other tips discussed here.
You’ve probably heard Uncle Bob’s famous quote: “Leave the code cleaner than you found it.” Inspired by the scouts, this simple but powerful rule, applied regularly, can significantly reduce technical debt.
Most of the time, it’s about making small refactoring adjustments.
That’s the heart of these three articles, when you spot an opportunity to refactor and improve the code structure, don’t hesitate to do it, even if it requires a time investment. It’s a key step in preventing technical debt from building up.
In the next articles, we’ll dive into how to execute the tricky task of refactoring and how to convince the product team to invest time in it.