This is the fifth post in my series on technical debt categories. This time, I’ll cover design debt. Again, we’ll look at what design debt is, where it comees from and how and why it should be solved.
What Is Design Debt?
The original paper that identified the thirteen categories of technical debt defines design debt as:
[…] debt that can be discovered by analyzing the source code by identifying the use of practices which violated the principles of good object-oriented design (e.g. very large or tightly coupled classes).
And this is where design debt, to me, has an overlap with code debt. Indeed, the paper mentions examples like code smells and duplicated code as both code and design debt. I suspect code debt is more about code metrics like cyclomatic complexity, not adhering to standards and slow performance, whereas design debt is about code design. Things like:
- tight coupling: modifications to one class or module ripple through to other classes and modules
- brain methods: single functions performing multiple tasks where a better design would be to split the function into multiple, separate functions
- low cohesion: a class or modules that covers many responsibilities which could be regarded as separate modules or classes (i.e. it’s doing to many different things)
- bad object-oriented design: not adhering to the principles of object-oriented design (if applicable) like inheritance without using (many of) the inherited functions
Causes Of Design Debt
I wouldn’t worry too much about the exact difference between code debt and design debt. There’s some overlap and they’re closely related. As we saw with code debt, a big cause of design debt is also schedule pressure. Because time is limited, teams sometimes skip over a necessary design phase. This isn’t necessarily “big design up-front” (preferably not), but the team can sometimes go faster if they take 30-60 minutes to discuss how a solution will be designed. When done together, a lot of pitfalls can be uncovered before even starting to code.
Even without schedule pressure, developers like to dive into the code. I’m not sure whether it’s something we learn or just how we’re wired. But many developers immediately see some solution when confronted with a problem. Yet if they’d take time to think about the design, and discuss it with fellow developers, they will probably come up with a better design.
Design debt can also be caused by lack of knowledge and experience in good design. Even seasoned developers sometimes don’t recognize bad design when they’re working in it (even more so when they’re the author of the design). This differentiates good from not-so-good developers, and it’s not always correlated to seniority.
Finally, like all technical debt, design debt is normal as a system grows.
Solving Design Debt
Solving design debt will require a certain level of maturity in your team. Both to not succumb to the schedule pressure as well as to recognize bad design and know how to fix it.
But it also requires buy-in from management to accept estimates for work that are now higher than usual. Yet, often these more mature estimates are more realistic. Say you have a team that you know you can pressure to agree with shorter delivery times. Either they delivery on time but the quality is lacking, or they deliver late. Wouldn’t you rather have a team that gives you the “bad news” about it being more work than you expected?
The Business Case For Reducing Design Debt
Just like code debt, design debt can be useful in the short-term, but over time it comes back to bite you. You wouldn’t want an electrician to wire your house without fuses and wires that are too thin to handle the expected amperage, would you? You want the electricity, but you also want to be sure that inside the walls, it’s quality work.
The same is true for software. You want the features, but you should be able to trust that internally, there’s quality that will ensure your team can continue the current velocity and that bugs are limited.
Because, as systems grow in size and complexity, bad design will also increase the amount of bugs that reach production and decrease the pace at which the team can release new features.