Every software development team creates technical debt that it strives to minimize. Systemic tech debt can seriously slow down development, demotivate engineers, force a ground-up rewrite, or even kill the company.
Repaying technical debt and developing features constantly compete for backlog priority. Some teams are better at repaying this debt than others and succeed in recognizing tech debt’s negative impacts, quantifying possible impacts, prioritizing fixes, and committing to a resolution plan. Addressing the technical issues seems to be the easy part and usually entails refactoring or changing some code. The hard part is prioritizing and quantifying technical debt. Development teams fail to resolve debt because they don’t prioritize well, not because they cannot fix technical issues.
- Designing AWS Environments
- Machine Learning with R Cookbook
- Natural Language Processing with Java
- Deep Learning Projects with PyTorch [Video]
- Practical AWS Networking
- Learning Azure Functions
- Jenkins Continuous Integration Cookbook
- Jenkins Essentials – Second Edition
- DevOps for Web Development
- Implementing DevOps with Microsoft Azure
- DevOps Bootcamp
- Jenkins Essentials
The Key to Prioritization: Three Types of Technical Debt
Types of technical debts very in their severity and ramifications. While some types may ultimately force teams to declare software bankruptcy, others warrant simple interest payments. Trivial debts can be ignored since they may be repaid at any point.
The expectation that all technical debt would be eliminated is not only unrealistic, but unwise. Realistically speaking, only the most untenable debt needs to be discarded. Technical debt is broadly divided into three categories: local, global and systemic. Categorizing each type would help you separate the tenable from the untenable.
- Local debt is self-contained in a single method, class, or function. Imagine SOLID violations applied to a method, class, function, or single file. Their impacts are only felt when modifying the code in question.
- Global debt is self-contained in a single application or service. Unlike local debt, global debt involves SOLID violations across subsystems in a larger system that spread to seemingly unrelated areas. Examples include a mismatch between abstraction layers (presentation vs. data), problems at other integration points, and hard coupling. These issues don’t impact consumers of the application or service but are felt when modifying the service itself.
- Systemic debt spills across multiple applications, infrastructures, or even teams. Examples include multiple services sharing a data store, high coupling across different bounded contexts, and a stark mismatch between technical implementations and business logic. Systemic debt can be caused by practices like using a NoSQL database where an RDBMS is the correct choice, creating tight coupling between independent services, communicating across bounded contexts via a shared database, splitting business concerns into too many microservices, and choosing the wrong deployment architecture. Systemic debts are untenable in the long term.
Each category of debt inflicts different impacts, so they need to be prioritized and handled differently.
Three Ways Agile Can Help You Prioritize Tech Debt
Agile organizations are better positioned to deal with technical debt than organizations using a waterfall SDLC style. The principles of agile software development are outlined in the Agile Manifesto. It allows minimal dependency on requirements and specifications and emphasizes the value of teamwork, trust and open communication over that of tools and procedures.
Agile methodologies are more conducive to eliminating technical debt because they are focused on fostering a new work culture that speeds up overall workflows, rather than on hitting specific release milestones.
This blog post discusses three of the ways adopting an agile development methodology can also help you better prioritize and address technical debt.
- Agile is about everybody doing everything – This helps tackling local debt
- Agile tracks value. Not time or execution – The right approach for eliminating global debt
- Agile is adaptable and continuous – So is the strategy for dealing with systemic debt
Let’s take a closer look at each.
1. Agile is About Everybody Doing Everything
Agile development methodologies encourage multidisciplinary learning and continuous adaptation. They offer job mobility, avoiding fixed job roles and career paths. Finally, agile is about incentivizing individual excellence over team excellence. All of the above becomes extremely useful when dealing with local technical debt.
To start with, it’s good to assign local fixes to new team members as a learning exercise. Teams can also apply the “boy scout rule” that the code should be left better than it was before, a practice that works well since code improvements are proportional to how often the code is modified.
2. Agile Tracks Value Rather Than Time and Execution
Agile methodologies typically apply Value Stream Management to ensure all resources are put into right use. This approach is essential to dealing with global, residual technical debt, for which unfortunately, there are no quick fixes. A technical solution to global tech debt requires focus on all related code. Senior engineers are best suited to repay the debt by carefully separating concerns, creating boundaries, and enforcing bigger architectural concerns.
Appling Value Stream Mapping would help you understand how judicious governance and workflow improvements can mitigate issues before they appear. For example, adding careful code review to specific releases is a good starting place. Not all releases are created equal. Releases that change code across boundaries or rework core business concepts must be vetted carefully to minimize technical debt. Releases that modify a single less-used class do not. These concerns should shift left in the workflow, meaning that engineers should discuss potential technical debt as early in the process as possible in order to mitigate problems later on.
Global tech debt must be attended to when it inhibits the development of frequently modified code. If the solution is not immediately clear, then spike on a potential 80/20 solution and take action swiftly before the situation devolves into a systemic issue. Clean up will take time out from sprints, so again, use Value Stream Mapping here to plan accordingly.
3. Agile Is Adaptable and Continuous
Using shorter work cycles enables agile development teams to continually refine the work. Any backlog can be reprioritized as customer or products need change. Items can be added, and work can be adapted with a new iteration
Systemic tech debt represents problems at the architectural level. The key to dealing with systemic debt is remembering that your goal isn’t to eradicate technical debt altogether. Instead, you should aim to keep it easily manageable and incorporate it into your agile delivery. Also, if it’s not inhibiting common development activities, then let it be. This keeps the team working in the right direction.
Building evolvable architecture is an automated way to combat systemic issues. Building Evolutionary Architectures proposes testing key architectural characteristics such as component coupling or durability with fitness functions. The deployment pipeline executes the fitness functions as part of its criteria verification, thus rejecting severely problematic code.
The Difference Between Doing and Being Agile
Technical debt becomes a problem when it’s not attended to. Adopting agile culture and not just agile tools can help you address tech debt better than any other SDLC methodology. For example, tech debt in dependent projects can inhibit delivery across other projects. Teams must prioritize fixes and coordinate releases across all project backlogs. Teams must adopt an agile release management strategy that promotes visibility into fixes and their relationship with other projects and releases.
Your tech debt strategy should be an 80/20 approach for improving development on the most frequently modified code paths. This way, addressing inconsequential local debt can be moved to the bottom of the pile, while addressing global tech debt is likely the biggest 80/20 win since it hedges against systemic debt and improves development of frequently modified code. Do not aim to wipe out technical debt in a single massive release. Fixes for technical debt should be released in continuous small batches.
Gearing Up for a Debt-free Future Via Technical Investment
A key tenet of agile is Value Stream Management that helps release managers identify and prioritize investment buckets.
Technical investments in automated testing, static analysis, and fitness functions bake quality into the process. Over time, this moves tech debt out of individual backlog tickets and into the current culture. And this kind of agile culture change can mitigate problems to the point where systemic debt rarely appears.
This future likely feels far away, so you’ll need to start with prioritizing the backlog by considering the different tech-debt categories and the affected code and business flows. You’ll also need to prioritize efforts across all projects since not all projects are created equally. Achieving visibility into all projects and releases with solutions such as Panaya Release Dynamix is a key factor in deciding what to tackle.
Eschew fixing local issues and prioritize integrating static code analysis into the existing test suite to mitigate the creation of more local technical debt. This can kick-start an important conversation on writing better code. Next, prioritize spikes aimed at 80/20 solutions for the worst global tech-debt offenders. Don’t bite off more than you can chew in the beginning by tackling systemic debts. Craft a technical strategy for mitigating their impacts and focus on 80/20 spikes after addressing pressing pain points.