technical debt quality and trade offs
Technical Debt, Quality, and Trade-offs: Striking the Right Balance
November 29, 2024
Justin Ammerlaan and Guy Coleman
Read Time: ~12 minutes
As engineers, we are constantly juggling the demands of delivering high-quality products quickly while also maintaining the integrity of our codebase. One of the most critical aspects of this balancing act involves managing technical debt, understanding what quality truly means, and making informed trade-offs. This post explores these concepts and offers insights into how teams can navigate these challenges effectively.
Our Co-Founder and CTO at Empower, Justin Ammerlaan, has laid the foundation through years of experience building successful financial service products for how we can and should leverage 2Technical Debt2 and view and challenge concepts of 2Quality2 at Empower.
Understanding Technical Debt
"Technical debt" is a metaphor for the future cost incurred when opting for a quicker solution now, rather than a longer, potentially better approach. The alternative might be simpler, more flexible, adaptable, robust, or easier to maintain in the long run. Essentially, it’s the trade-off of immediate action for a known solution versus investing time in developing a potentially superior one. Like financial debt, it’s a form of borrowing now with the obligation to repay later.
“In finance, using debt as a tool is a way to leverage, or increase returns. In our case, in Engineering, it is a tool to create speed. It is an essential part of any financial strategy and also an essential part of the MVP process.” - Justin Ammerlaan
“Strategic use of technical debt can be a tool for creating speed and leveraging opportunities. However, like financial debt, it needs to be managed carefully. Accumulating too much without a plan for repayment can hinder progress and introduce significant risks to the project.” - Justin Ammerlaan
Just like with financial debt, technical debt needs to be repaid and should be repaid on a schedule; otherwise, the ‘interest’ starts to compound, and the maintenance overhead becomes a massive burden. At Empower, we allocate 30% of engineering resources to non-product work, including technical debt.
Defining Quality
Quality is a foundational aspect of software development that ensures the product meets the needs and expectations of its users. However, misconceptions about what constitutes quality can lead teams astray. By clearly defining what "quality is" and what "quality is not," we can align our efforts toward delivering value effectively. Justin provides some specific guidelines on how to think about what quality is and what it isn’t.
Quality Is:
Correctness (Functionality)
- Correctness ensures that the software behaves as intended, fulfilling all specified requirements without bugs or errors.
- Example: In a financial application, correctness ensures that all calculations (like interest computations or balance updates) are accurate, preventing financial discrepancies.
Accuracy (Adherence to Requirements)
- Accuracy involves meeting the specifications and requirements.
- Example: If stakeholders need a report-generating tool that exports data in CSV format, accuracy means delivering exactly that feature—not an elaborate reporting suite with formats they don't need.
Quality Is Not:
Optimization
- While performance is important, premature optimization can lead to unnecessary complexity and wasted resources.
- Example: Spending days optimizing a function that runs in milliseconds, when users wouldn't notice the difference, instead of working on delivering new features can divert resources from more impactful areas.
Generalization
- Engineering solutions to handle a wide range of scenarios beyond current requirements can complicate the codebase.
- Example: Creating a complex plugin architecture for an application that is only ever going to need built-in functionality can be overkill if current needs are limited and well-defined.
Extensibility
- Building systems with capacity for extensive future expansion can introduce unnecessary complexity.
- Example: Designing a module with extensive API endpoints for integrations that aren't planned can make the system harder to maintain.
Elegance
- While elegant code can be beautiful, overemphasis on elegance can hinder progress.
- Example: Using a complex design pattern for a simple problem can complicate the code without offering significant benefits.
Personal Coding Style Preferences
- Imposing individual coding styles over team standards can disrupt consistency.
- Example: A developer insists on using programming language features that others on the team are unfamiliar with, complicating maintenance.
“Quality is something that should never be sacrificed when it comes to engineering at Empower. We are a financial tech company that moves millions of dollars every day, and a mistake in specific areas of the codebase can create significant issues.” - Justin Ammerlaan
Understanding what quality is and what it is not helps guide our engineering teams into the “pit of success,” or pit of 'productivity' success. Our engineering teams can deliver reliable and valuable software by focusing on correctness and accuracy.
Trade-offs
Balancing quality, technical debt, and speed of delivery requires careful consideration and strategic decision-making. The key is to adjust these trade-offs relative to the requirements of the solution and the consequences of the decisions involved.
Balancing Technical Debt and Quality
Technical debt is a tool that can be leveraged to increase development speed. However, the amount of technical debt incurred should align with the specific needs and risks associated with the project.
Balancing Trade-offs for Speed and Learning: When developing a product with uncertain customer demand, it may be advantageous to accept higher technical debt to deliver quickly and learn from real-world usage.
Example: Releasing a basic version rapidly allows for quick validation without over-investing in a potentially unviable solution.
Balancing Trade-offs for Critical Systems: Conversely, when working on foundational systems where errors can have significant consequences, minimizing technical debt is crucial.
Example: Developing an authentication system for a credit card application requires a high degree of reliability and security.
Ultimately, the approach varies depending on factors like the stage of the product and the criticality of the system being developed. Understanding when to prioritize speed over quality—or vice versa—is essential for delivering value effectively.
Key Take-Aways
Strategically Manage Technical Debt
- Leverage technical debt deliberately to accelerate development, treating it as an investment that can yield significant returns if managed wisely.
Quality Equals Correctness and Accuracy
- Focus on ensuring the software works as intended and meets requirements.
Balance Trade-offs Based on Decision Types
- Understand whether decisions are hard-to-reverse (Type 1) or easily reversible (Type 2).
Adjust Technical Variables Without Compromising Core Functionality
- Manage aspects like abstraction and documentation without compromising the solution's correctness and accuracy.
About Justin Ammerlaan
Co-founder and CTO of Empower Finance
Software expert with a wealth of technical and commercial skills. Extensive experience in finance software, including high transaction flow and modelling systems.