Balancing the Tech Debt
Author
KalleDate Published
Did you ever work on a legacy code base and thought "How the hell did we get here?"? If you work in software development for more than a few years, I assume yes. But why does this happen? Why do we end up with code that is almost unmaintainable, even though on each new project we promise ourselves this time it will be different?
The answer is simple: Money. Or more abstract "business value". In the end, we are paid to deliver features, not to write code. So we cut corners, we take shortcuts, we ignore best practices, in short we accumulate technical debt. All in the name of delivering faster. So is technical debt a bad thing, if it helps us to build faster? Not necessarily. Like any debt, it is a trade-off. Like a loan it enables us to have something faster, but we still have to pay it, plus extra costs to cover interest and fees.
Also like taking a loan this should be a conscious decision, because of the long term impact. The problem is, in software development you often times don't know about the impact it will have, or even worse often times the debt is forgotten or ignored.
If the situation is clear, and everyone involved knows about the trade-offs, technical debt can be a powerful tool. It helps you to deliver faster, to test ideas, to validate assumptions. But if it's not managed properly, it can quickly become a burden. Typical symptoms are bugs, slow development, bad morale, and in general a fragile system.
How to mitigate the risk of technical debt?
The dream is of course to never have technical debt and always write the perfect code. But as it makes sense to avoid premature optimization, the "perfect" code will always be a compromise. So the question is not how to avoid technical debt, but how to manage it. There are two main strategies to deal with technical debt:
- Awareness: There are two perspectives on this topic, from the individual contributors who make decisions on code level, and the broader decisions by management.
- Individual contributors: As a developer, you should be aware of the trade-offs you make. If you take a shortcut, communicate it clearly and make sure that everyone involved knows about the consequences. This is especially important for non-technical stakeholders, as they often don't see the problem until it's too late.
- Leadership: As a manager, you should keep track of decisions and debt from the past, so you have a clear picture of the current situation. Also keep the experience level of your team in mind. A more junior team might create more debt than a senior team, so you have to adjust your estimates accordingly. Even more important, a junior team will create hidden technical debt, that they are not aware of due to a lack of experience.
- Prevention: As already established, complete prevention is not wanted, but you want to reduce the risk of technical bankruptcy. Here are some tips that work well for me:
- Keep the core clean: The closer you are to the core of your system, the cleaner the code should be. This is the part that is hardest to change later, so invest the time now.
- Duplication is your friend: If you see potentially reusable code, evaluate if both existing and new feature really have the same semantic meaning and will evolve in the same way. If not, duplication is better than the wrong abstraction.
- Strict project settings: Use linters, formatters, and other tools to enforce a consistent code style. This will make it easier to read and understand the code, and will prevent some common mistakes. Also language settings like the `strict` mode in TypeScript can help to have a good base level for your code quality.
- Keep cool: Even if you are in a rush, take a moment to think before jumping on a solution. Often a few minutes of thinking can save you hours of debugging and/or refactoring later.
- Boy Scout Rule: Empower the developers to clean up "small" issues without any extra process. This takes some alignment on what is considered a "small" fix/refactoring, but has a huge impact as it provides continuous improvement.
Clearing the debt
But what if it's too late to mitigate? What if you already have a huge mess, and development basically comes to a halt? That's the moment to get all stakeholders and discuss the situation. The important questions to answer are:
- How much more expensive is the feature development in the current state compared to a clean state?
- How expensive is a complete rewrite?
- How much can you reuse from the existing system?
- How confident are you that the new system will be better than the old one?
- How much pressure is on the team/company to deliver new features?
- Are the right resources available to improve the situation?
Most of these questions won't have a clear answer, but if the conversation is open and honest with input from the tech and business side you should be able to make decisions to go in the right direction.
The best strategy is of course very much dependent on the individual situation. For startups with a "young" system it can be a good idea to start over, as the system is not too complex yet. But at a certain scale the right solution is probably somewhere in between. Keep what works, rewrite what doesn't, and make a plan to avoid the same mistakes in the future.
Although it is always easier to build a feature a second time with all the learnings from previous implementations and iterations, keep in mind to analyze the reasons for the failed system before you start the rewrite. A common "solution" is to blame the language or base technology used, and then to rewrite it using the new fancy framework. This rarely solves the underlying issues, but just leads to a mess in a different style.
Mailing List
If you want to receive updates on new posts, leave your email below.