Technical Debt Is an Architecture Decision, Not a Code Quality Problem

A startup launches in three months. Every tenant's data sits in shared tables, filtered by tenant_id. Two years later, the largest customer demands EU data residency. Three engineers spend four months on the migration.

The launch decision was correct. The migration cost was real. Neither fact contradicts the other. Both are the same architecture decision, priced at two different moments.

This is the frame most teams miss. Technical debt is not bad code. It is a structural tradeoff — simplicity now, flexibility later — made deliberately, tracked explicitly, and repaid when a trigger fires.


Bad code and technical debt are different things

Bad code is bad code. It should not have been written that way. Fix it and move on.

Technical debt is a structural decision that was appropriate at one point and becomes inappropriate at another, as requirements change. The word "debt" is precise. You borrow speed or simplicity now. You pay interest over time in slower development, harder maintenance, and higher risk of failure.

The shared-table decision at launch was correct. A single database is simpler to operate. Multi-tenant isolation was not needed yet. The team borrowed against a future customer that did not yet exist. When that customer arrived, the team paid the interest.


The debt quadrant tells you what action to take

Not every debt item is the same. The quadrant classifies debt by intent and awareness.

Deliberate/Reckless — "We don't have time to do this right." The team knows better, ignores it, accrues liability. This is the only category that is genuinely bad practice.

Deliberate/Prudent — "We need to ship now and we'll fix this later." The team makes a conscious tradeoff, accepts the debt, intends to repay it. The shared-table example lives here. Appropriate when the future fix is priceable, the speed benefit is real, and the team will remember.

Inadvertent/Reckless — "What's layering?" The team lacks the knowledge to make better decisions. They produce debt without knowing it. The most dangerous quadrant. There is no intention to repay because the debt is invisible.

Inadvertent/Prudent — "Now we know we should have done it differently." The team learns something through the work that they could not have known beforehand. The design that fit a simpler problem is wrong for the evolved one. This is the natural cost of discovery.

The quadrant is not a moral judgment. It tells you the action. Reckless deliberate debt needs discipline. Reckless inadvertent debt needs training. Prudent debt of either kind needs tracking.


Abstract measures are not actionable

"Code quality is poor" is not a debt statement. You cannot ship it to a stakeholder. You cannot prioritise against it. Four concrete measures replace it.

Lines changed per feature. If adding a payment method requires touching forty files, the abstraction is missing. Track the number over time. Rising means debt is accumulating.

Test coverage gaps. Uncovered code is unverifiable code. Every change to uncovered code carries unknown risk. Coverage gaps identify where debt makes change risky.

Deployment frequency. Low frequency is a symptom, not a cause. Teams deploy infrequently when deployment is risky. Deployment is risky when code changes have unpredictable side effects — which is exactly what high-debt codebases produce.

Mean Time to Recovery. High MTTR correlates with poor observability and complex, fragile systems. Both are symptoms of accumulated debt.

None of these measures are subjective. All of them move on a chart. All of them convert into a business case.


Debt compounds — this is AT3 and FM3

The tradeoff is AT3 — Simplicity vs Flexibility. Simple code is easier to write, easier to read, faster to ship. Flexible code takes longer to build and demands more sophistication to understand. Debt is the choice to take simplicity now. The interest is the cost of acquiring flexibility later, when the system has grown and is harder to change.

The failure mode that makes debt dangerous is FM3 — Unbounded Resource Consumption, applied to maintenance cost. A service with high coupling between modules costs more to add features to — not linearly more, multiplicatively more. Each new feature must navigate the existing coupling. Over time, an increasing proportion of team capacity goes to navigating debt rather than delivering value.

The interest rate can be quantified. A codebase where a medium-sized feature takes three weeks instead of one — because of coupling, missing tests, and architectural inconsistency — is running at 200% interest. Every £1 of original debt costs £2 in delayed delivery.

There is a second failure mode worth naming. FM8 — Schema/Contract Violation. The most expensive technical debt creates implicit contracts. Shared database schemas are implicit contracts between every application that reads them. When the schema must change to repay the debt, every reader is a change site. Repayment cost scales with the number of consumers of the implicit contract. This is why the shared-tenant-table decision is cheap to make and expensive to unwind.


Track debt as explicit architectural decisions

Untracked debt compounds silently. Tracked debt has a trigger and a price. A debt registry entry looks like this:

DEBT-001:
    Title: Shared-table multi-tenancy
    Introduced: 2023-01-15 (v1.0 launch)
    Decision: All tenant data in shared tables, filtered by tenant_id
    Reason: Speed to market; single tenant at launch
    Interest: Data isolation is per-query, not per-schema
    Trigger for repayment: First customer requiring data residency
                          OR >100 tenants
    Estimated repayment cost: 3-4 engineers for 3 months
    Current status: ACTIVE — 40 tenants, no residency requirement yet

Every field earns its place. The trigger removes the ambiguity of "later". The estimated cost lets you compare debt items against each other. The status tells you which items are approaching repayment.

An entry marked OVERDUE is a debt where the trigger has already fired and the team has not acted. Overdue debt does not stay in place — it produces incidents until it is paid.


Communicate debt in business terms, not technical ones

"We have high coupling in the billing module" is not a business problem. Stakeholders cannot act on it.

"Adding the next payment method will take eight weeks instead of two, and there is a 30% chance it breaks an existing payment method" is a business problem. It has a cost, a probability, and a consequence.

The translation is mechanical. Take the technical symptom. State the effect on delivery time. State the risk of doing nothing. Price the repayment. Skip the words "refactoring", "technical debt", "code quality", "legacy" — none of them travel across the room.

A working template: "We currently spend approximately 40% of engineering capacity on debt navigation. Addressing the three highest-interest components would recover 20 engineering-weeks per quarter." This is a business case, not a technical argument.


The signal that this applies to your system

Adding a new feature requires modifying an unexpectedly large number of files. A specific area of the codebase produces a disproportionate share of incidents. Deployments to one module are always risky when deployments to others are not.

Any one of these is a symptom. Together they are a diagnosis. Untracked technical debt is the cause. The concrete measures above turn the diagnosis into a number, and the number turns into a stakeholder conversation.


The harder question

The debt registry above assumes the team will remember to look at it. Most teams do not. Debt items get logged during the sprint they are introduced and never revisited. Two years later, the trigger fires and nobody remembers the entry existed.

What mechanism forces a debt registry to be read? Not written — read. A checklist item in the sprint ritual? A quarterly review? An architectural fitness function that blocks a merge when a debt item's trigger has fired? Every answer has its own failure mode. The one you choose determines whether your debt is genuinely tracked or merely documented.


The full framework treatment — compression blocks, three-level exercises, and the complete AT/FM mapping — is in Book 5: Code Architecture, Chapter 14 (Technical Debt as Architecture). Free chapter available at computingseries.com/books/book5.