Tech Debt Isn't a Backlog Problem. It's a "Done" Problem.

I specialize in developing object-oriented java applications that aligns with business objectives, using Domain-Driven Design principles to ensure technical decisions drive tangible value. By focussing on a deep understanding of the business domain, I craft solutions that solve real problems while maximizing ROI. My approach evaluates the cost/profit ratio of every decision—only implementing technologies when the benefits outweigh the costs. I’ve been called in to revive stalled projects and address challenges where others have struggled. My focus is on creating software that not only meets but exceeds business expectations. Whether working with legacy systems or modern frameworks, I select the right technologies to maximize value—not just follow trends. I believe software should be a strategic asset, and this mindset guides every decision I make in development.
Technical debt is usually described as a bill that arrives later for a purchase made earlier: speed bought on credit, with the cost deferred to some future date. Ask practitioners why debt accumulates and the most common answer, by a wide margin, is some version of deadline pressure — not enough time, ship now, fix later. That framing assumes a conscious shortcut: someone saw the proper path and the fast path, and picked the fast one, aware of the trade.
Most technical debt doesn't work that way, and it isn't one single failure. Often, what gets built is an answer to what does this need to do — the verb — without ever asking what is this — the noun it's supposed to represent. Nothing gets bypassed here, because the better path was never in view to begin with. There is no shortcut, because there was no fork in the road.
But sometimes the noun does surface — partway through implementation, something makes the real shape of the thing visible. And it still doesn't get acted on, because there's a day left in the sprint and the verb version already works. That's a different failure than the first: not a question that was never asked, but one that got answered and then set aside. The first is a goal problem — attention was never pointed at the noun. The second is a process problem — attention found the noun, and process closed the door on it anyway.
Both produce the same debt, and both are more common than the conscious-shortcut story of deadline pressure suggests. What "done" ends up meaning, thousands of times across a codebase, is whichever of these two failures happened to occur: what does "done" mean for a piece of code?
Two definitions of "done"
Process thinking defines done against the ticket. The ticket describes a required outcome — add this event type, fix this bug, expose this field in the API — and the work is finished when the code satisfies that description. This is a clean, almost tautological measure: done is done when the acceptance criteria are met, the tests pass, and the sprint goal is hit.
Product thinking defines done against a second, independent question that no ticket ever asks: is this still the right shape for what this thing actually is? Call it product thinking not in the roadmap-and-discovery sense a product manager would recognize, but in the sense that a developer asking this question is doing for the code what a product owner does for the roadmap: refusing to let the immediate ask stand in for the real target. At the level where the application is being built, that question is domain thinking — is this a correct model of the thing being built. This question doesn't close when the ticket closes. It stays open regardless of what the ticket says, because it isn't about the requested outcome — it's about whether the system's model of the domain still holds up.
Here's the problem: process thinking's definition of done is entirely self-referential. It's measured against process artifacts — the ticket, the sprint, the acceptance criteria — never against anything outside them. "It works" becomes the terminal, closing argument, because "works" was defined by the ticket in the first place. There is no process artifact whose job is to ask the second question. So in a process-first environment, it simply never gets asked. Not because anyone decided the domain model didn't matter, but because nothing in the workflow ever put it on the table.
This is a sharper claim than "tech debt is caused by cutting corners under deadline pressure." Plenty of debt gets created by careful, unhurried developers doing exactly what was asked, cleanly, on time, with tests. The debt isn't a corner that got cut. It's a question that was never in scope to begin with.
Verb-shaped tickets, noun-shaped things
A ticket is verb-shaped. Add this event type. Fix this bug. Expose this field. Followed literally, each ticket gets satisfied by its own procedure: a sequence of steps producing the required output for that one case, and nothing else. Martin Fowler has a name for this style of organization — the Transaction Script pattern: logic organized as one procedure per use case, rather than as behavior belonging to a shared concept those use cases operate on. It isn't automatically wrong on its own; the trouble starts once several scripts need the same underlying data with no shared concept holding it together, and the natural next move is to push that shared logic into a fat service class every script calls into. Ticket-driven work doesn't inevitably produce this shape, but it produces exactly the conditions Transaction Script fits best — because a ticket only ever asks for the procedure, never for the concept the procedure operates on.
Domain thinking asks the other question: what is the underlying concept, independent of any single ticket, and does the behavior belong to it. That's not automatically the "more sophisticated" answer, either. Asking the question and concluding "this really is just a value, with no behavior of its own" is domain thinking done correctly. The failure isn't picking the simple version — it's arriving at the simple version by default, because the question of which one was correct never got asked in the first place. Building the plain version because nobody asked is not the same thing as building it because the honest answer was genuinely "plain," even though the two can look identical on the page.
Multiply the ticket-by-ticket version across a codebase and the result is spaghetti — not from any one bad decision, but from many individually reasonable, ticket-scoped scripts accumulating around a shape that was never named as the wrong shape in the first place. Nobody wrote spaghetti on purpose. Everyone wrote the smallest correct script for the ticket in front of them. This is the goal-problem failure from the opening, playing out at the scale of a codebase rather than a single decision: not that the shared concept was considered and rejected, but that it was never in view to reject.
"It works" as a closing argument
The staying-inside-the-ticket move is straightforward: satisfy the request in front of you, and file a separate ticket for the underlying redesign the work exposed. That second ticket rarely gets scheduled. Not from negligence, but for a completely defensible reason: it works. There's no bug, no outage, no complaint. Against the process definition of done, there is no case for spending a sprint on it.
This isn't a story about cutting corners under pressure. Satisfying a ticket exactly, cleanly, and on time is not reckless — it's correctness by the only definition that was visible. The debt accumulates anyway, silently, because "it works" is a sufficient closing argument inside process thinking and an insufficient one inside product thinking.
The process-first version of this gets defended in almost identical language across very different teams: "It does what the ticket says, and it got done in the time allotted." "At least the sprint goal got made." These aren't admissions of a corner cut. They're sincere descriptions of a job done correctly, by the only definition of the job that was visible. That's the trap — process thinking doesn't read as negligence from inside it. It reads as diligence.
There's a psychological effect underneath this worth naming directly: a ticket, treated as the boundary of the work, quietly becomes the boundary of what questions a developer feels authorized to ask. "It does what the ticket says" isn't really a statement about effort — it's a statement about jurisdiction. The question of whether this is the right shape for the domain was never asked; only whether it satisfies the requirement in front of it. Multiply that across a team and a codebase, and the result is a system built entirely out of locally-correct answers to a question nobody ever meant to be the only one asked.
So is tech debt an indicator of missing product thinking?
The strong version of that claim doesn't survive an obvious counterexample: a team that knowingly ships an imperfect design to hit a market window, fully aware of what it will owe later, has clearly asked the noun question — the debt was seen, named, and understood before it was accepted. That's product thinking present, not absent. So debt itself isn't reliable evidence that the question was never asked.
What it's evidence of is the other failure from the opening: the question was asked, answered, and then set aside anyway, because naming a piece of debt doesn't create any mechanism that reopens it later. The debt gets named once, at the moment of the decision, then meets the same process filter as everything else — it works, no urgency, off the sprint. This is the process-problem failure playing out at the scale of a whole team's roadmap rather than a single sprint. Named-and-abandoned debt and silent-and-undiscovered debt tend to converge on the same outcome, because neither has an artifact whose job is to keep the question alive once "it works" is satisfied.
So the indicator isn't the debt, and it isn't even whether the debt was consciously named. It's this: is there anything in the system that treats "it works" as insufficient, independent of the ticket that produced it? If yes — if someone's job, habit, or authority is to keep asking "is this still the right shape for what this actually is," regardless of what's currently on fire — debt gets caught early, named, and often fixed on the spot, because the question never fully closes. If no, debt accumulates invisibly, ticket by ticket, correct answer by correct answer, until the shape is wrong enough that the next requirement can't be satisfied without a rewrite.
Technical debt, in other words, isn't the disease. It's what accumulates in the space where a question should have stayed open and didn't. And whether that question stays open isn't a matter of talent or care — ticket-scoped work of this kind is neither careless nor unskilled — it's a matter of whether the system around the work ever asks it.
The uncomfortable implication
If that's right, then "reduce technical debt" is the wrong target for most organizations. You can't ticket your way out of a problem that is caused by the unit of work being a ticket. Refactoring sprints, debt-paydown quarters, and architecture review boards are all still process artifacts — useful, but structurally the same kind of thing that let the debt form in the first place, just aimed backward instead of forward.
The actual fix is smaller and harder to schedule than a new ceremony: making "what is this thing, structurally" a legitimate, expected question at the moment something is first built — not a virtue some developers happen to have, and not a separate ticket that competes with delivery for sprint capacity, but part of what "done" is understood to mean before the first line is written.
Concretely, that can be as small as a design review or pairing session that is explicitly allowed to reopen the shape of a ticket mid-implementation, not just its correctness — and a shared norm that "it works" is necessary but never sufficient to close a piece of work. Neither costs a sprint. Both require someone to have the standing to ask the question and not be overruled by the calendar.
That's a cultural condition, not a process — which is precisely why it's so often missing.





