There's No Limit to How Bad Code Can Get
Simon Willison argues that rewriting systems to address technical debt is often riskier than patching; in the AI era, automated testing and incremental refactoring offer a more reliable path.
- Rewrites often fail due to ongoing business needs and waning team motivation
- Running old and new systems in parallel increases maintenance costs and project risk
- Incremental migration and automated testing are superior approaches to technical debt
- AI-assisted code generation may exacerbate technical debt, making testing and refactoring even more critical
Background: Why discuss technical debt and rewrites now? Technical debt is a universal pain point for engineering teams. When a system becomes hard to maintain, the instinctive response is to start over. But Simon Willison points out that this approach rarely works in practice. Especially today, with AI-assisted programming accelerating code generation far beyond testing and refactoring capacity, technical debt can accumulate at an exponential rate. Revisiting the rewrite versus patch decision is highly relevant for every tech leader.
Deconstruction: Why do rewrites consistently fail? You might think a rewrite is the shortcut to solving technical debt, but it is often a trap. The legacy system remains the core of the business during the rewrite, so new requirements keep pouring in. Developers maintaining the old system know it will soon be obsolete, so they do the bare minimum, allowing debt to grow. Meanwhile, the rewrite team starts with high energy but soon discovers that the real logic of the old system is far more complex than documented, with many implicit rules never recorded. Under pressure to deliver, the new system launches with only a fraction of functionality, running alongside the old one. The result: instead of reducing debt, you now maintain two systems, doubling costs.
Trend Insight: A new paradigm for technical debt in the AI era. This reveals a deeper trend: in an era where AI can rapidly generate code, the hidden costs of technical debt are severely underestimated. Previously, debt accumulated slowly, giving teams time to repay it. Now, AI boosts code output, but testing, documentation, and architectural understanding lag behind. Relying solely on rewrites or manual fixes is no longer viable. Instead, approaches like Will Larson's migration strategy are gaining traction: build a comprehensive automated testing net, then execute targeted refactoring. This maintains business continuity while gradually reducing debt.
Practical Value: How should tech leaders respond? If you face an unmaintainable system, resist the urge to launch a rewrite immediately. First, assess whether core logic is adequately tested. If not, prioritize automated test coverage, especially for critical business paths. Second, adopt the Strangler Fig pattern: migrate new features or high-value modules to a new architecture incrementally rather than replacing everything at once. Third, establish visible metrics for technical debt so the team and leadership can track its growth and repayment progress. In an era of AI coding assistants, beware the false confidence of fast code generation and integrate testing and refactoring into daily workflows.
Counterintuitive Insight: Rewrites do not cure technical debt; they amplify it. Most people believe a rewrite permanently solves technical debt, but Willison's experience shows it often just hides it in new forms. The new system may look clean initially, but to maintain compatibility or meet deadlines, it introduces new compromises. More critically, knowledge loss during rewrites is irreversible. The ugly but working logic in legacy systems often carries years of implicit business evolution. Rather than chasing a perfect one-time overhaul, embrace continuous evolution. Use testing and incremental refactoring to keep technical debt within manageable bounds.
Analysis by BitByAI · Read original