Code Quality Has No Lower Bound: Why Technical Debt Can Accumulate Infinitely

Software lacks physical limits—code can always get worse without collapsing like buildings do.
Software differs fundamentally from buildings: while physical structures collapse under excessive load, code can degrade infinitely through added abstraction layers and performance losses. This article explores why technical debt has no natural breaking point, examines the dangers of over-abstraction and gradual performance degradation, and provides strategies for establishing artificial quality guardrails through complexity thresholds, performance benchmarking, and regular refactoring windows.
Buildings Collapse, Code Doesn't
Developer Zach Kehs presents a thought-provoking insight in his blog: software and buildings are fundamentally different. If you keep adding floors and rooms to a building, it will eventually collapse due to physical limitations. But software code isn't bound by such constraints—code can always get worse, you can always add another layer of indirection, always degrade performance a bit more.
This metaphor reveals an unsettling truth in software engineering: technical debt has no physical breaking point.
The analogy between software engineering and civil engineering has been a long-standing discussion in the industry. The term "software engineering" was first proposed at the 1968 NATO Software Engineering Conference, attempting to bring the rigor of traditional engineering disciplines into software development. However, this analogy has fundamental limitations: building requirements rarely change after construction begins, while software requirements continuously evolve; building materials have clear physical property parameters, but the "strength" of software components is hard to quantify. Glenn Vanderburg has pointed out in multiple talks that software engineering is closer to "design" than "construction"—every line of code is a design decision, while compilation and deployment are the (nearly zero-cost) construction process. This cognitive framework helps explain why software doesn't collapse like buildings: its "cost" isn't in physical materials, but in the cognitive capacity humans need to understand and maintain it.

Why Code Quality Can Degrade Infinitely
Traditional engineering disciplines are constrained by physical laws. Building structures have load-bearing limits, bridges have fatigue strength, mechanical parts wear out and fail. These physical constraints force engineers to consider long-term stability during the design phase.
But software is a purely logical construct. It won't collapse because it's "too heavy," won't stop running because it's "too complex". A system with 100 layers of abstraction and 10x worse performance can still function normally—maintenance costs skyrocket and development speed plummets, but it won't physically collapse like a building.
This characteristic is both software's strength (high flexibility, sustainable scalability) and its curse (allows infinite accumulation of technical debt).
The concept of Technical Debt was first introduced by Ward Cunningham in 1992. He used financial debt as an analogy: taking technical shortcuts for rapid delivery is like borrowing money—you gain speed advantages in the short term, but later need to pay "interest"—the extra cost of maintenance and fixes. Martin Fowler later subdivided technical debt into four quadrants: prudent/reckless × deliberate/inadvertent, helping teams more precisely identify and manage different types of debt. This concept is important because it expresses the long-term costs of technical decisions in business language, making non-technical stakeholders understand the necessity of code quality investment.
The Trap of Indirection: How Over-Abstraction Drags Down Code
"You can always add another layer of indirection" points to the common problem of over-abstraction in software architecture. The classic statement about indirection comes from David Wheeler's quote: "All problems in computer science can be solved by another level of indirection—except for the problem of too many levels of indirection." This precisely captures the paradox of over-abstraction.
In practice, the Dependency Inversion Principle (DIP) and Interface Segregation Principle (ISP) in SOLID principles encourage decoupling through abstraction, but the YAGNI principle (You Aren't Gonna Need It) warns against designing prematurely for needs that haven't emerged. Joel Spolsky calls unnecessary abstraction "Architecture Astronautics," referring to those obsessed with building elaborate frameworks while disconnected from actual business needs. Healthy abstraction should follow the "Rule of Three"—only when the same pattern appears three times is it worth extracting.
Each added layer of abstraction theoretically provides decoupling and reuse, but in practice often leads to:
- Increased cognitive load: Understanding a feature requires crossing more files and modules
- Higher debugging difficulty: Problems can hide in the connections between any layer
- Accumulated performance overhead: Each layer of calls has overhead; multiple nested layers significantly slow down the system
More dangerously, this degradation is gradual. Each time a new abstraction is added, it individually "makes sense," but the cumulative effect often only becomes apparent months later—by which time the code is tangled, and refactoring costs far exceed expectations.
The Slippery Slope of Performance Degradation
"You can always degrade performance a bit more" is equally concerning. Against the backdrop of excessive modern hardware performance, developers easily overlook performance losses at individual optimization points:
- "This query takes an extra 0.1 seconds, users won't notice"
- "This library is slow but feature-complete, let's use it for now"
- "Just add a caching layer, no need to optimize underlying logic"
But when 100 such "small losses" accumulate, the system transforms from responsive to sluggish. Worse still, performance debt is harder to repay than feature debt—rewriting a slow endpoint might require refactoring the entire call chain.
Performance Regression Testing is a key approach to addressing this issue. It automatically detects whether system performance degrades after each code change. Unlike functional testing, performance test results have statistical variability and require multiple runs taking the median or P95/P99 percentiles for judgment. Common performance benchmarking tools include JMH (Java), BenchmarkDotNet (.NET), hyperfine (command-line tools), etc. Incorporating performance testing into CI/CD pipelines is becoming an industry best practice: Google's continuous performance analysis tools and Netflix's Performance Monkey are typical cases. The key is setting clear performance budgets, such as API response times not exceeding 200ms or page first contentful paint not exceeding 1.5 seconds—blocking deployment once exceeded.
Technical Debt Management: How to Establish Artificial Guardrails
Zach Kehs' observation actually reveals the core challenge of technical debt management: lack of natural boundaries. Without physical constraints, teams need to artificially set quality red lines:
- Code complexity thresholds: Use metrics like cyclomatic complexity and nesting depth to force refactoring. Cyclomatic Complexity, proposed by Thomas J. McCabe in 1976, quantifies code complexity by calculating the number of independent paths in a program's control flow graph. A function without branches has cyclomatic complexity of 1; each added if/else, switch case, or loop increases complexity by 1. The industry generally considers functions with cyclomatic complexity over 10 need refactoring, and over 20 means nearly untestable. Beyond cyclomatic complexity, Cognitive Complexity (proposed by SonarSource) more closely aligns with human comprehension difficulty and is a worthwhile supplementary metric. These metrics can be automatically detected through tools like SonarQube, ESLint, PMD and integrated into CI/CD pipelines.
- Performance benchmarking: Treat performance regression as blocking bugs, incorporate into CI/CD processes
- Architecture review mechanisms: New abstraction layers must be thoroughly justified to avoid unnecessary indirection
- Regular refactoring windows: Reserve time in iteration plans to repay technical debt rather than postponing indefinitely
Without these proactive constraints, code quality will continuously slide along the "path of least resistance"—until maintenance costs become so high that a complete rewrite is necessary.
Conclusion
Software's flexibility is a double-edged sword. It enables rapid iteration and continuous delivery, but also allows technical debt to expand infinitely. Buildings give warnings before collapse; code rots in silence.
Recognizing the fact that "code can always get worse" is the first step in establishing a code quality culture. Only by proactively setting guardrails and regularly cleaning up technical debt can we avoid falling into the predicament where "rewriting is the only way out." As Ward Cunningham's original metaphor implied: debt itself isn't scary; what's scary is never repaying it—interest will eventually consume everything.
Related articles

Building an AI Robot Dog for Kids: Multi-Model Routing, Content Filtering, and Latency Optimization
A $130 AI robot dog for kids integrates 8 LLMs with 61-language voice interaction. The team shares key engineering lessons on content safety filtering, multi-LLM intent routing, and sub-1-second latency optimization.

Can Omarchy Dominate the Sub-$1000 Laptop Market? An In-Depth Analysis
Omarchy, based on Arch Linux, shows unique advantages in the sub-$1000 laptop market. This analysis compares Windows and MacBook performance bottlenecks on low-spec hardware and examines why Omarchy enables cheap laptops to run smoothly, plus the ecosystem challenges and market prospects it faces.

AI Agent Beginner's Guide: Building a Creative Strategy Intelligent Assistant from Scratch
A complete guide to building a creative strategy AI Agent from scratch. No coding required — use tools like Dify and Coze to quickly build an intelligent assistant.