Classifying Technical Debt by Type Before a Rebuild
Understand what type of debt you have before deciding whether to rebuild or refactor.

Not every product that feels broken needs a new one built from scratch. That's the core problem with how most teams approach technical debt: they treat "the code is bad" as a single diagnosis, when it's actually at least five different diagnoses wearing the same trench coat. A rebuild is expensive, slow, and prone to reintroducing the exact problems it was meant to fix, so the decision to pull that trigger deserves more precision than a gut feeling that things have gotten messy.
The scale of the problem is worth sitting with for a second. The CAST 2025 "Coding in the Red" report analyzed 10 billion lines of code across 47,000 applications and found that 45% of the world's code is fragile, 32% is bloated, and 31% is too rigid to change without breaking something. Those are three separate failure modes, not one undifferentiated blob of "bad code." Fragile code breaks under pressure. Bloated code slows everyone down. Rigid code resists the very changes a growing product needs to make. Each of those points to a different type of debt, and each type calls for a different fix. Classify the debt first, and the rebuild-or-refactor decision starts to look a lot less like a coin flip.
Where the concept of technical debt comes from, and what it originally meant
Ward Cunningham coined the term "technical debt" to describe something simple: the future cost of choosing a faster, weaker path today instead of a slower, sturdier one. The metaphor holds up because it's financial in a literal sense. Shortcuts accrue interest. Every one of them makes the next piece of work a little slower, a little riskier, a little more expensive, which then creates pressure for another shortcut to keep pace. That's the debt spiral, and it looks almost identical whether it's happening in a codebase or a checking account.
Steve McConnell sharpened this with a two-type model: intentional debt, where a team consciously trades quality for speed, and unintentional debt, which comes from poor practices or gaps in knowledge nobody caught in time. Martin Fowler pushed the idea further with his Technical Debt Quadrant, which maps four named combinations across dimensions of deliberateness and prudence. The insight buried in that grid is that intent and context together decide what kind of debt a team is actually holding.
Why does that distinction matter for a rebuild? Because strategic debt, the deliberate-prudent kind, is usually documented, time-bound, and reversible. Someone chose it on purpose, knowing the tradeoff, planning to revisit it. Accidental debt is different. It's hidden, it compounds without anyone noticing, and it tends to surface at the worst possible moment. The mindset that created the debt in the first place shapes whether it can be paid down with a few weeks of focused refactoring or whether it needs something closer to structural surgery.
The academic standard for classifying technical debt: the 13-type ontology
In 2014, Alves and colleagues published "Towards an Ontology of Terms on Technical Debt," a paper that Carnegie Mellon's Software Engineering Institute later referenced. It broke technical debt into 13 distinct types, each with its own tells:
Architecture debt covers short-term structural choices that need extensive reworking later, the kind no simple code fix can resolve. Build debt shows up as builds that get slower and more resource-hungry with each release. Code debt lives in the source itself: clumsy algorithms, tangled data structures, patterns nobody would choose on a clean slate. Defect debt is the pile of known bugs nobody had the bandwidth to fix. Design debt violates basic design principles in ways that quietly erode maintainability and user experience. Documentation debt is the gap between what the docs say and what the system actually does. Infrastructure debt is a foundation that can't hold the weight of current scale. People debt is knowledge trapped in one person's head, and the risk of a project stalling the day that person leaves. Process debt is the friction generated by workflows that are missing or broken. Requirement debt gets baked in when specs are unclear or shift halfway through a build. Service debt comes from third-party integrations that turned into liabilities. Test automation debt is the coverage that was never automated, and test debt is the manual QA gaps that stack up as a shortcut becomes a habit.
Thirteen categories might sound like overkill, and it would be, if the point were exhaustiveness for its own sake. It isn't. The value is that each type demands a genuinely different remediation strategy, and lumping them together leads straight to misdiagnosis. A team that treats architecture debt like code debt will spend weeks refactoring functions that were never the actual bottleneck.
There's a newer data point that backs up how real these categories are. Research published in the Journal of Systems and Software in August 2025 (Shivashankar et al.) trained transformer-based classifiers on GitHub Archive Issues from 2015 through 2024 to detect these debt types automatically, and hit notably high precision and recall. A model can only learn to sort things into buckets that actually exist. If the 13-type taxonomy is granular enough to train a classifier on, that's a decent signal it's tracking real, distinguishable patterns in real codebases, not just an academic exercise in taxonomy for its own sake.
Five types that actually drive rebuild decisions in practice
Thirteen categories matter for research. Five matter for the phone call about whether to rebuild.
Code debt is the most visible kind: duplicated logic, tight coupling, hardcoded values, missing tests. Developers run into it every day, and the good news is it's usually addressable incrementally, through refactoring and static analysis tools that flag the worst offenders.
Architecture debt is the one that changes the whole calculus. It shows up as violations of modularity, monolithic designs, and components so interdependent that touching one breaks three others. Research consistently identifies architectural issues as among the most consequential sources of technical debt, and unlike code debt, architecture debt can't be resolved with code-level fixes alone.
Infrastructure and build debt hides until it doesn't. Outdated runtimes, legacy servers, single points of failure, build pipelines that get more fragile with every release. These sit quietly until a failure exposes exactly how much risk had been accumulating.
Test and documentation debt doesn't stop the product from running today, but it blocks safe change tomorrow. Missing coverage, outdated API references, architecture diagrams nobody updated in two years. When a team needs to move fast, that constraint functions exactly like a technical blocker, even though nothing is technically broken yet.
Security debt carries the steepest potential cost of the five, because a breach doesn't just cost engineering time. It costs trust, and it can trigger regulatory exposure that dwarfs the original fix. It's also the type most often discovered at the worst possible moment: during a fundraise, or during a security audit nobody scheduled until an investor asked for one.
Research increasingly points to a sixth, non-technical category worth naming even outside the core five: organizational and process conditions like knowledge silos, poor handoffs, and requirements that keep shifting. None of that shows up in a code scan, but it explains something important: why the debt keeps coming back after a cleanup that looked complete on paper.
Line these five up, and they map directly onto the questions any rebuild assessment actually needs answered. What layer is failing? What can get fixed incrementally, on the side, without stopping the roadmap? And what actually requires tearing something down to replace it?
Why architecture debt is the type that determines whether a rebuild is necessary at all
Architecture debt behaves differently from the other four. Code debt lives in specific files and functions, so it can get isolated and fixed one piece at a time. Architecture debt is baked into the shape of the system itself, which means it can't be cornered the same way.
It tends to show up in three forms. Violation of modularity, where components are stitched together so tightly that changing one means touching several others. Monolithic architecture, where the entire product lives in a single codebase, making isolated deployments or targeted scaling close to impossible. And violation of separation of concerns, where business logic, data access, and the interface layer are tangled together so thoroughly that even a small change carries outsized risk.
Practitioners and researchers have noted that architectural debt frequently sits in place for years, sometimes much longer. That's worth pausing on. The longer architectural debt sits, the more of the system gets built around it, and the harder it becomes to isolate later. Ten years of features layered on top of a shortcut isn't a shortcut anymore. It's the foundation.
So how does a team know whether architecture debt, specifically, is the actual problem, rather than a diffuse sense that things feel slow? A few behavioral signals tend to travel together. Deploy frequency drops below once a week, because the system has grown too tightly coupled to ship safely. New features start taking significantly longer to build than they did during the MVP phase. Senior developers need weeks just to understand the system well enough to touch it without breaking something, which means the architecture itself has quietly become a knowledge silo.
When all three of those show up at once, refactoring individual code files won't move the needle. The intervention has to hit the structural layer directly. But if only one signal is present, or two at most, a targeted refactor of the specific modules causing friction is probably enough. That distinction, on its own, decides whether a team is looking at a six-week cleanup or a six-month rebuild.
How MVPs accumulate mixed debt types, and why the mix matters more than the total amount
MVPs are basically debt factories, and that's not a criticism. Speed-to-market pressure turns every launch deadline into a string of tradeoffs, and most of those tradeoffs never get revisited once the product starts working and attention moves to growth.
What accumulates isn't one kind of mess. It's several kinds stacked on top of each other. Code debt creeps in from shortcuts in the logic and tests that never got written. Architecture debt comes from a monolithic structure that made total sense for a two-week prototype and none at all six months later. Documentation debt builds up because the context for half the decisions lives only in one developer's memory. Infrastructure debt shows up from hosting choices that were fine at zero users and start creaking at a few hundred. Security debt comes from authentication and data handling that passed muster for a demo but was never built for production traffic.
The instinct, once a team notices all this, is to rebuild everything at once. But that raises the more useful question: rebuild the right things, or just rebuild the things that are easiest to see? The debt types most likely to stay invisible, architecture and security, also tend to surface at the worst possible moments and carry serious downstream costs. Self-admitted technical debt, the kind developers flag directly in code comments, only ever captures a slice of what's actually there, and it tends to miss less visible categories like these.
That's the real risk. Not the debt itself, but debt nobody is tracking, sitting in a codebase nobody is auditing, compounding quietly until it surfaces during a fundraising round, a security review, or the week a team needs to ship something critical and discovers the system won't bend that way. Classification before intervention means walking into a rebuild already knowing which layers need structural work and which ones can improve gradually, alongside whatever else is on the roadmap.
Running a type-first classification before scoping a rebuild
The goal isn't a spreadsheet with every single debt item logged. It's answering three questions: which types are present, which ones are actually blocking growth right now, and which require structural intervention versus incremental cleanup?
A practical sequence starts with behavior, not code. Deploy frequency, feature velocity, how long it takes a new hire to onboard, these numbers surface architecture and process debt that a code review alone would never catch. From there, each layer gets audited on its own: code, architecture, infrastructure, tests, documentation, security. Auditing them all together muddies the findings, because each layer has different indicators and a different fix.
Findings then get sorted by type, not just by severity. A critical code issue and a critical architecture issue might both feel urgent, but one is a refactor target and the other needs structural work. Treat them the same just because they're both marked "critical," and rebuild effort ends up misallocated before scoping even starts.
It's also worth mapping which types feed each other. Architecture debt is often the root cause behind downstream code debt and test debt. Fix the symptoms without touching the architectural root, and the debt just grows back, usually faster than the first time.
Security and infrastructure debt deserve a separate track regardless of what happens with the rest of the system. Both can sit silently for months and then fail catastrophically, so they get triaged immediately, independent of whatever the rebuild decision turns out to be elsewhere.
What comes out the other end is a debt map: which types are present, how they connect, and which ones are root causes versus downstream symptoms. That map is what actually determines scope. The rebuild targets the structural root causes. The refactor plan handles the rest, incrementally, without derailing the roadmap. Most technical debt, once classified this way, can get paid down by targeting the specific parts blocking whatever's coming next, a scaling event, a new integration, a security audit, while the rest of the product keeps moving forward. A full rewrite only earns its cost when the structural layer itself is the actual blocker.
What classification reveals about when to rebuild, when to refactor, and when to do neither yet
The rebuild decision should come out of the debt map, not precede it. Once classification is done, it tends to point toward one of three conclusions.
Sometimes structural debt really is the primary blocker. Architecture and infrastructure debt are widespread and clearly tied to the velocity and reliability problems the team is feeling. In that case, a rebuild of the structural layer is justified, and code or test debt can get cleaned up as part of building the new system.
Other times, debt is concentrated in one specific type. Maybe it's security, maybe it's test coverage, maybe it's a single architectural boundary causing most of the pain. A targeted fix on that one type is faster and considerably lower-risk than tearing the whole thing down.
And sometimes, debt is present, but the product hasn't proven yet that it deserves the investment of a rebuild. In that case, the honest answer is to wait. The right moment to tackle structural debt is once real users have arrived and the business case for the product's longevity is established, not before that case exists.
A 2024 survey found that for more than half of companies, technical debt eats up more than a quarter of the total IT budget. That's a real cost, and it's easy to point to as justification for a rebuild. But an ill-scoped rebuild carries its own cost, one that's often invisible until the brand-new product starts exhibiting the exact same symptoms as the old one. Rewriting code that was never actually the problem doesn't fix anything. It just moves the debt to a newer file with a cleaner git history.
For teams operating on thin runway, early-stage founders especially, the stakes of getting this wrong are higher than they might seem. A rebuild that misses the actual debt types burns through the exact runway that would have funded a precise, targeted fix instead. And classification surfaces an uncomfortable truth here too: process debt, people debt, and requirements volatility aren't technical problems at all. They're organizational. A brand-new codebase, built by the same team under the same conditions, will accumulate the same debt types all over again if nothing about those conditions changes.
How an ongoing technical partner changes the classification and remediation dynamic
Type-first classification works best when the people doing it actually know the system's history: what decisions got made, why they got made, and what the debt map looked like at each stage along the way. That kind of longitudinal knowledge is hard to fake.
A one-time audit by someone encountering the codebase for the first time can absolutely surface debt items. It's harder for that kind of audit to reliably tell root causes apart from downstream symptoms, since that distinction usually depends on context nobody wrote down anywhere. Was the monolith a deliberate speed tradeoff in month two, or an accident nobody caught? Did the test coverage gap start because of a deadline, or because nobody ever agreed on what needed testing in the first place? Those answers live in institutional memory, not in the code itself.
That's the case for an ongoing technical relationship over a one-off engagement. Debt keeps evolving as the product does, and a partner who's been present for that evolution can classify new debt against a map that already exists, instead of starting from zero every time something breaks. The alternative, bringing in a new team for every audit, means re-deriving that context from scratch, which costs time exactly when time is the scarcest resource. Given how much of a team's attention technical debt already consumes on an ongoing basis, that context isn't a nice-to-have. It's the difference between a rebuild that fixes the actual problem and one that just repeats it with better naming conventions.


