Strategies for Managing Architectural Technical Debt that DON’T Work
The most significant relief from performance issues and costly technical liabilities comes from resolving architectural technical debt. This blog post delves into the best strategies to achieve that.
“Doing things the quick and dirty way sets us up with a technical debt, which is similar to a financial debt. Like a financial debt, the technical debt incurs interest payments, which come in the form of the extra effort that we have to do in future development because of the quick and dirty design choice. We can choose to continue paying the interest, or we can pay down the principal by refactoring the quick and dirty design into the better design. Although it costs to pay down the principal, we gain by reduced interest payments in the future.” - Martin Fowler
In our previous blog post we analyzed the causes and symptoms of Architectural Technical Debt and why it ultimately has a highest interest rate than code-level technical debt. After all, when we talk about code-level technical debt we’re referring to poor naming, badly structured code, duplication etc. - things that tend to be localized and can be fixed in isolation. Architectural Technical Debt (ATD) instead tends to be more complex, with a larger scope, and more time-consuming fixes.
ATD is an insidious risk factor because its presence is not as immediately apparent: while source code bugs may be identified and addressed with a variety of tools, these same tools may not flag issues with sub-optimal architectural design.
This is particularly significant in the context of greenfield projects, where you don't experience ATD until months later: you have nothing but options as no code is yet written, no infrastructure is yet picked - and while the path may seems clear, this very freedom can lead to choices that do not stand the test of time or scale.
As projects transition from their early stages to more mature solutions, the presence and impact of ATD becomes more significant. The experience of working on a “new product that has only a few users”, on a “popular product with many users”, and on “critical product” are vastly different in terms of how many constraints are imposed by past (unintentional or intentional) choices on those systems’ evolution.
Without a comprehensive understanding of ATD's impact and a strategic approach to its management, organizations risk incurring significant costs in maintenance, missing out on opportunities, and faltering in their modernization efforts.
In this article, we'll delve deeper into effective strategies to manage ATD, ensuring the longevity and adaptability of your software systems in the face of inevitable change.
Strategies for Managing ATD that DON’T work
Does the following scenario sound familiar?
You take on debt to deliver a project within the deadline. As years pass, the business's demand for new features takes precedence over essential refactoring. Gradually, the project morphs into something akin to a tangled ball of yarn; complexity spirals out of control, leading to rampant architectural erosion. New feature development slows to a crawl and a large portion of the team’s time is spent maintaining the platform, fixing bugs and putting out fires. To solve the situation, your first instinct is to “kill it with fire” and start from scratch.
While the urge to overhaul a system teetering on the brink of obsolescence is common, this approach—and others like it—is typically labor-intensive and proves ineffective in the long haul. Here's why:
👎 (1) Major refactoring
Legacy software is, in many ways, a ticking time bomb.
However, major refactoring has significant drawbacks: it can take years, a substantial budget, and considerable engineering resources to complete, all the while part of the team is tasked with maintaining the outdated platform and rolling out new features on top of it.
Unless you’re prepared to declare “technical debt bankruptcy”, and halt operations to conduct a tabula rasa re-engineering, your users will expect uninterrupted service and regular updates. All the while your business stakeholders will expect you to keep the platform stable enough to deliver new products and leverage profitable market opportunities.
This leaves you with two unenviable choices: in one scenario, you risk losing business; in the other, the refactoring team is perpetually chasing the team tasked with keeping the original platform both operational and evolving.
Even if you manage to catch up and comprehensively refactor the original platform, the cycle of accumulating ATD would begin anew, unless preventative best practices were adopted, trapping you in a relentless cycle.
👎 (2) Build on top and hope for the best
If the core functionalities of a legacy platform remain operational, for velocity’s sake, you may decide to maintain them and build new products and features on top as requested by the business.
This approach, however, is fraught with risk. Building on an unstable foundation often necessitates workarounds, meticulous management of custom exceptions, and the continuous deployment of patches to circumvent the constraints imposed by ATD.
While this strategy might fulfil short-term deadlines, it essentially amounts to "kicking the can down the road," with maintenance costs escalating as the technological solution becomes increasingly outdated.
👎 (3) Build “Technical Credit”
This strategy seeks to preemptively forestall the accumulation of ATD by identifying and addressing potential architectural bottlenecks which could slow down future development.
However, due to pressing time constraints and the uncertain efficacy of this approach, it is seldom employed in practice.
A frequent outcome is the realization that the solution has been over-engineered, yet no corrective action is taken. This inaction is partly due to the sunk cost fallacy—the hope that the currently unused solution might find relevance in the future.
Another deterrent is the cost associated with refactoring. Removing an integrated solution from the architecture demands significant development effort to ensure it doesn't negatively impact customers or hinder the delivery of new features. Once a solution is entrenched in the architecture, excising it becomes a challenge.
A more pragmatic approach is to adopt a "just-in-time" mentality—address architectural needs as they arise rather than preemptively. There's no need to aim for the moon when the goal is to reach the sky.
Strategies for Managing ATD that DO work
To effectively manage Architectural Technical Debt, adopting proactive strategies is crucial. Specifically, this involves leveraging architecture visualization and observability tools, along with regularly conducting architectural reviews.
By prioritizing architectural quality and timely addressing of accruing debt, software teams can prevent the debt from spiralling out of control. This proactive approach significantly mitigates associated risks and ensures the long-term success and maintainability of software systems.
👍 (1) Implement architectural observability tools
The biggest challenge businesses face with Architectural Technical Debt is its invisibility: without the proper tools, visualizing the entire system architecture in real-time becomes a daunting task, often relegated to manual, general-purpose diagramming tools. This lack of visibility hinders the identification of architectural drift and the summarization of accumulated debt causes.
Effectively managing ATD involves bridging the gap between the current state of the architecture and its ideal state. The initial step towards this goal is to inventory the debt, creating a comprehensive visual map of the system's architecture.
Ideally, this is achieved using a tool that automatically discovers and monitors components, dependencies, and APIs within your system by directly interfacing with your infrastructure. Such a tool would provide a clear overview of your backend software platform's condition, even in the absence of detailed Open API documentation from your developers.
👍 (2) Make ATD a priority
Regardless of whether you are working on a new, 'greenfield' project or maintaining a mission-critical legacy application, incorporating short, upfront system design reviews into your development process is essential.
Regular architectural design reviews yield two primary benefits:
- They uncover any unintentional architectural violations before implementation and facilitate the documentation of intentional architectural decisions, thereby justifying any potential added ATD principal and interest.
- They enable you to proactively identify, evaluate, and prioritize areas of the system that require re-architecture and standardization. In essence, this approach embodies the 'Boy Scout Rule'—leave the codebase better than you found it—allowing for incremental repayment of technical debt alongside other development tasks, such as adding new features or fixing bugs.
Although addressing ATD in small increments can be challenging—especially when it involves significant changes like adopting a new programming language, replacing third-party components, or untangling complex subsystems—the effort is crucial. By consistently evaluating and prioritizing ATD alongside other tasks, it remains a focal point, underscoring the importance of architectural integrity.
Good system stewardship is rarely rewarded, recognized, or taught, however it means the difference between a team that thrives over decades and one that repeatedly faces code bankruptcy, rewrites, and frustration.
There's no one-size-fits all when evaluating and prioritizing ATD but there are a few crucial things to bear in mind:
- Not every imperfection constitutes “architectural tech debt”
- Address straightforward issues discovered during system design reviews immediately.
For medium to large issues, consider whether they reside in an active or dormant part of the system and the extent of maintenance they necessitate. Generally, prioritize issues that are exacerbating and hindering the evolution of the platform or the addition of new features.
Conclusion
Addressing code-level technical debt during delivery cycles may provide leaders with a temporary sense of progress, yet it overlooks the more substantial and risky debt accumulating at the architectural level. The most significant relief from performance issues and costly technical liabilities comes from resolving architectural technical debt.
However, the essence of managing ATD lies not in eradicating it entirely but in understanding, monitoring, and strategically addressing it to foster sustainable, long-term software health.
Effective management of ATD necessitates tools that support real-time architecture visualizations, observability, and drift detection, and facilitate collaboration among team members for designing and discussing system architecture.
Don’t use general purpose diagramming tools - use a purpose built, collaborative tool for teams that want better solutions for system design and architecture documentation. Check out Multiplayer and sign up for free!