Shawn Seymour
We call it technical debt because that keeps the conversation safely technical.
It lets everyone point at the code, open a cleanup ticket, schedule a refactor, and move on. Engineering gets to sound responsible. Product gets to keep shipping. Leadership gets to acknowledge the problem without reopening the decisions that made it predictable.
That is what makes the phrase so useful.
It is also what makes it so weak.
Because a surprising amount of what gets labeled technical debt did not begin as a technical problem. It began upstream: in urgency, in weak ownership, in avoided tradeoffs, or in coordination costs high enough to make compromise look rational.
The code is where technical debt becomes visible. It is rarely where it starts.
Of course, some debt is genuinely technical. Bad abstractions exist. Weak engineering discipline exists. AI-slop exists. Lazy coding exists. Sometimes the code really is the source. Not every brittle system is an organizational morality tale.
But the debt that reshapes roadmaps, slows teams down for years, and survives repeated cleanup efforts usually lasts for reasons bigger than code quality.
A sloppy engineer can create debt.
A sloppy system of decision-making can industrialize it.
That is the pattern worth paying attention to.
The phrase is useful. The diagnosis is shallow.
“Technical debt” is one of those phrases everyone understands just well enough to use carelessly.
It sounds precise. It sounds professional. It sounds fixable.
Most importantly, it sounds contained.
It suggests the problem lives in the code, which implies the solution probably lives there too: refactoring, cleanup, migration, maybe a rewrite if things get dramatic.
That framing is comfortable for everyone.
Engineering gets to talk about maintainability. Product gets to protect the roadmap. Leadership gets to fund cleanup without revisiting the decisions that created the mess.
The phrase keeps the conversation polite.
Because once you admit that a lot of technical debt is really the downstream result of urgency, weak ownership, unresolved tradeoffs, and expensive coordination, the conversation changes.
Now you are not just talking about code quality.
You are talking about how the organization makes decisions, how it distributes authority, what kinds of ambiguity it tolerates, and which costs it prefers to hide until software makes them impossible to ignore.
That is a much less comfortable conversation.
It is also usually the more accurate one.
A lot of technical debt is upstream debt with a code footprint.
The most useful way to think about technical debt is not as a single category, but as the place where several different kinds of upstream debt eventually become visible in software.
Not exhaustive. Not perfect. But useful.
A surprising amount of what gets labeled technical debt is really one of four other debts wearing an engineering costume:
- urgency debt
- ownership debt
- tradeoff debt
- coordination debt
If you want to understand why systems become brittle, why migrations stall, and why cleanup rarely stays done, start there.
Urgency debt is speed purchased with future simplicity.
This is what happens when an organization buys speed in the present by borrowing simplicity from the future.
A launch is committed. A major customer needs something now. A quarter has to be saved. The date becomes immovable whether or not the system is ready.
So the shortcut gets taken.
A temporary wrapper. Hardcoded logic. A deferred migration. A partial fix with a promise that phase two will clean it up later.
Phase two, of course, is often fictional.
The shortcut hardens. Other work gets built around it. The local compromise becomes structural reality. Six months later, people are staring at a brittle system and talking about technical debt as if it formed on its own.
It did not.
The organization made a purchase. It bought speed with future simplicity.
Software just kept the receipt.
Urgency debt is rarely accidental. It is usually rationalized in real time and regretted in hindsight.
The code did not wake up one morning and decide to become a mess. The organization made a trade, and the system remembered it longer than the meeting did.
Ownership debt is pain without clear authority.
This is what happens when something is important enough to hurt, but not owned clearly enough to improve.
Everyone knows the workflow is painful. Everyone knows the service is fragile. Everyone knows the deployment path is full of ritual, superstition, and fear.
But no one owns it in the only way that matters: with authority, capacity, and accountability.
So the system lives in a gray zone.
One team deploys to it. Another depends on it. A platform group supports part of it. Someone has the history. Someone else carries the pager pain. No one has both the mandate and the room to simplify it.
This kind of debt compounds quietly.
Not because no one sees it.
Because seeing it is not the same as being able to change it.
That is why so many painful systems survive for years in plain sight. They are not hidden. They are unowned.
Once a system reaches that state, simplification becomes optional. Workarounds become normal. Ritual replaces design. Teams absorb the pain instead of removing it.
Then someone calls it technical debt.
Sometimes that is true.
But the deeper issue was ownership. The code just became the storage layer for an accountability failure.
Tradeoff debt is indecision made durable.
This is what happens when an organization refuses to make a real choice.
The product has to work for every customer. The platform has to support every use case. The architecture needs to stay flexible. Exceptions keep getting added. Very little gets removed.
So the design absorbs the indecision.
More configuration. More abstraction. More layers. More edge-case handling. More “just in case” complexity.
Eventually someone says the architecture became too complicated.
Not exactly.
The architecture absorbed unresolved tradeoffs.
That distinction matters. It means the complexity was not just technical overreach. It was the residue of choices the organization did not want to make clearly. Someone wanted optionality without constraint. Flexibility without boundaries. Scope without consequence.
A lot of complexity is not bad engineering. It is indecision made durable.
Once that complexity lands in schemas, APIs, workflows, and operating assumptions, it looks like an architecture problem — and at that point, it is one. But the debt was often incurred earlier, when the organization kept choosing ambiguity over commitment.
Software is where indecision stops sounding strategic and starts getting expensive.
Coordination debt is compromise made permanent.
This is what happens when the right fix is known, but too expensive to align.
The clean solution crosses team boundaries. The migration requires multiple services to move together. The system boundary is wrong, but fixing it means sequencing work across several groups with several backlogs and several opportunities for the effort to die quietly in planning.
So instead of doing the hard shared fix, everyone does the locally convenient one.
A wrapper. A duplicate model. A translation layer. A second source of truth. A shadow workflow nobody loves but everybody can tolerate.
This is how organizations accumulate systems that are obviously more complex than they need to be, even when smart people understand the better design.
The issue is not ignorance.
It is coordination cost.
When alignment is expensive enough, compromise wins by default.
Later, someone opens the code and sees duplication, drift, awkward boundaries, and brittle dependencies. It looks like technical debt, and in the immediate sense it is. But the debt was incurred against alignment long before it showed up in software.
Engineering inherited the complexity.
It did not invent all of it.
Code is where the consequences become concrete.
Engineers do not call all of this technical debt because they are confused.
They call it technical debt because code is where the consequences finally become tangible.
You do not get paged by an incentive structure. You get paged by the brittle service it produced.
You do not review a pull request full of organizational ambiguity. You review the workaround that exists because the ambiguity was never resolved.
You do not inherit the meeting where the tradeoff got dodged. You inherit the migration that stalled halfway because nobody had the authority, appetite, or coordination budget to finish it.
So people talk about the thing they can see, measure, and occasionally improve.
That is understandable.
But it creates a blind spot.
Once the issue is framed as technical debt, the organization starts reaching for technical remedies by default: refactoring, cleanup, modernization, rewrite, more engineering time.
Sometimes those are exactly right.
But they are often incomplete, because they address the manifestation without changing the conditions that keep producing it.
The code gets cleaner.
The pattern survives.
Then everyone acts surprised when the debt comes back.
It usually does.
Not because engineers failed to refactor hard enough.
Because the organization kept spending the same way.
Real organizations do not run out of examples.
You have seen this before.
The migration that has been “phase one” for eighteen months.
The service everyone complains about but nobody can truly own.
The enterprise exception that permanently bent the domain model.
The platform abstraction created to avoid saying no.
The duplicated workflow spread across multiple systems because convergence required alignment that never materialized.
The monolith blamed for being messy when the real issue was that nobody protected boundaries inside it.
The microservice architecture praised for being modern even though half its complexity is just unresolved disagreement serialized over the network.
A lot of legacy code is really old compromise.
A lot of bad architecture is really preserved indecision.
A lot of what organizations call technical debt is simply the downstream expression of decisions they no longer remember making.
That is why cleanup work so often feels unsatisfying. Teams are asked to repair the evidence while the causes remain intact.
The real question is what keeps creating it.
A lot of organizations diagnose technical debt too late and too shallowly.
They treat accumulated complexity as a code problem when it is often the visible cost of earlier decisions: speed purchased too cheaply, ownership never made real, tradeoffs postponed, coordination allowed to become more expensive than correctness.
That does not mean refactoring is wrong.
It means refactoring is usually downstream.
By the time a system feels brittle, the more important question is no longer just, “How do we clean this up?”
It is, “What keeps creating this pattern?”
Was speed purchased by borrowing against time?
Was pain allowed to accumulate because nobody truly owned the system?
Did complexity grow because the organization refused to make a real tradeoff?
Did compromise win because alignment was more expensive than correctness?
Those are not interchangeable causes. They require different responses.
Organizations get stuck when they fund cleanup without changing the conditions that made cleanup necessary in the first place.
They improve the code, but preserve the incentives.
They pay down the balance, but keep the spending habits.
That is why the same complexity returns on schedule.
Not because engineers failed to refactor.
Because the organization never stopped producing it upstream.