Finding and Fixing Five Kinds of Architectural Technical Debt
Software architects have lacked the observability and tooling to understand, track, and manage architectural technical debt, from dependency entanglements to dead code.
Join the DZone community and get the full member experience.Join For Free
Developers, architects, and application teams are constantly chasing technical debt. For better or worse, it’s a nagging problem that too often gets kicked down the road until it’s too late and application development slows down, new features slip, test cycles increase, and costs ramp up. In the most public situations, the applications tip over completely — like we’ve seen most recently at Southwest Airlines, Twitter, FAA, and others which never get publicized — but you know who you are. Technical debt takes on various forms from source code smells to security risks to the more serious issue of architectural technical debt. Great tools exist to scan for source code quality and security, but tracking, baselining, and detecting architectural drift has been hard due to the lack of observability, tooling, and best practices.
What exactly is architectural technical debt and why should I care? If you are an architect or developer responsible for maintaining and extending an older Java or .NET monolith, you probably are already deeply familiar with the problem. A monolithic application is actually defined by its architectural (monolithic) pattern which carries with it dense dependencies, long dependency chains, and in essence a big ball of mud that is opaque for any architect trying to understand and track. This is the essence of architectural technical debt: the class entanglements, deep dependencies, dead-code, long dependency chains, dense topologies, and lack of common code libraries that plague monoliths, older applications, and more recently even microservices that have begun to resemble monoliths themselves.
Software architects, up to this point, have lacked the observability and tooling to understand, track, and manage technical debt from their perspective. Architectural debt is NOT source code quality or cyclomatic complexity, although these are critical technical debt elements to track and manage. The problems cut much deeper, as these structural issues directly affect product quality, feature delivery lead time, and testing times. Academic research has highlighted how analyzing dependencies provides a primary predictor towards the complexity of rework, refactoring, and application modernization.
Architectural observability shines a light on application black boxes and ball-of-mud apps, making the opaque transparent, so architects can shift left into the ongoing software development lifecycle. This allows them to manage, monitor, and fix structural anomalies on an iterative, continuous basis before they blow up into bigger issues. Observable architecture starts with the tooling to first establish a baseline, set thresholds, and check for architectural drift to proactively detect critical anomalies.
Five Critical Forms of Architectural Debt to Track
Getting in front of architectural debt is challenging, but it’s never too late to start. Many monoliths have been lifted and shifted to the cloud over the last decade and should be your first targets. There are five critical factors to analyze, track, and set a plan to fix.
Dead Code: The hardest kind of dead code to find is reachable legacy code residing inside applications and common libraries that is obsolete or no longer accessed by any current user flows. It’s often identified as “zombie code” as it lurks in the shadows and no developer really wants to touch it. Finding it requires a mix of dynamic and static analysis to determine whether the code is there but not ever accessed in production. Dead code is different from “unreachable code” in that the code is in fact technically reachable but actually no longer used. Dead code can develop and spread over time, bloating and complicating refactoring and modernization efforts.
Service Creep: Set a baseline service topology either through manual or automated means. Itemize the core business services and common services within the application, preferably in a shared location that the entire team can track. Routinely audit the app structure to see if new services have been added or deleted and whether that occurred for the proper business or technical reason.
Common Classes: One of the critical aspects of preparing for a refactoring or re-architecting project is identifying common classes that should comprise core platform services that act as a shared common library. This critical modernization best practice will reduce duplicate code and dependencies, collecting common services in one place. Routinely observe the application to check for new common classes that should be added to a common library to prevent further technical debt from building up.
Service Exclusivity: Once you’ve extracted one or more microservices from a monolith, baselining the exclusivity of those services and looking for architectural drift will flag future technical debt early. Measure and baseline service exclusivity to determine the percentage of independent classes and resources of a service, to alert when new dependencies are introduced that expand architectural technical debt.
High-Debt Classes: Some classes carry much more technical debt than others. Analyze and set “high-debt” class scores based on the dependents, dependencies, and size to determine the best candidates for refactoring which will have the highest impact on reducing your technical debt.
Proactive architectural oversight with automated tooling will enable architects to be in front of these types of changes by setting schedules for observation, analysis and setting configure baseline measurements and thresholds.
Architecture Drift Management
Continuous modernization requires architects to take on a much more proactive role not only at the initial design of an application or when it’s time to re-architect or refactor, but all the way through the lifecycle of their apps. Architecture drift management gives architects the observability and instrumentation they need to stay on top of their architecture, modernize over time, and avoid the next tech debt disaster.
Opinions expressed by DZone contributors are their own.
A React Frontend With Go/Gin/Gorm Backend in One Project
Database Integration Tests With Spring Boot and Testcontainers
Revolutionizing Algorithmic Trading: The Power of Reinforcement Learning
Seven Steps To Deploy Kedro Pipelines on Amazon EMR