The Technical Debt You Can't See Is the Debt That Gets You
How to retain agency over technical debt before the workarounds make it invisible.
Most technical debt conversations start in the wrong place. Engineering flags something that needs fixing, product asks why it matters, nobody has a good answer that lands, and the debt stays on the backlog until it becomes a crisis. The problem isn't the debt. It's that by the time it becomes a crisis, the workarounds have made it look like everything is fine. And things that look fine don't make roadmaps.
The trade-off you make consciously
At one of the startups I led engineering at, we built a data export feature (converting heatmap data into a series average and outputs an excel doc) which was a contractual commitment with a hard deadline. When the spec landed I worked with our senior backend engineer to run a feasibility check, him leading the system design while I fed the requirements. We mapped it out on a Miro board and it became clear quickly that the architecture we wanted, a fully async pipeline that would trigger exports on an event basis and process data in chunks, was going to take three or more sprints. The timeframe was only one to two. Our estimates lined up independently which gave us confidence in the number, and confidence that we weren't going to hit it.
So we designed a v1. A synchronous service that could deliver within the constraint, with a known ceiling on file size and a known path to the async architecture when we had the runway to build it. We brought it to a meeting with product and design, walked through what it would mean for the user experience in the short term, and tried to get the time buy-in for v2 on the roadmap at the same time. That last part didn't pan out.
What we shipped was documented, understood, and deliberately scoped. The debt was visible, the trade-off was explicit, and we had a design for where we were going. The agency felt intact. However, what we didn't have was a trigger, a specific condition that would force v2 back onto the roadmap before the urgency dissolved.
How debt goes underground
We shipped v1 and moved on, which was the right call given the deadline was hard committed and we'd built what we'd promised. The plan was always to follow it up with the async architecture we'd already designed.
In our roadmapping sessions we used a now, next and later framework. The async v2 sat in next for a while, then moved to later, then drifted to the bottom of later, consistently losing out to whatever was more urgent, more visible, more directly tied to something the business had committed to. Nobody decided to abandon it, that's just how debt goes underground, through a slow accumulation of other priorities rather than any single decision.
Within a month the constraint was already showing. The architecture had a hard ceiling on file size, and our main customer stakeholder, a power user and the person who signed the contract, hit it during a user feedback session, which was also reporting on Sentry. He needed larger exports than the service could support.
So we built workarounds, loading states to keep his attention during long downloads and early failure checks to surface the file size limit before the export started rather than after. Both were legitimate product improvements, things we might have built anyway because they were good UXD, and that's where it got complicated. Good UX on top of a fragile architecture is harder to argue against than bad UX on top of one, and every time the loading state worked cleanly or the early failure check caught an oversized file, the evidence for "it just about works" got a little stronger. The debt became more invisible precisely because the frontend looked better. Additionally, the stakeholder accepted a workaround, exporting in smaller chunks manually, which meant for the V2 roadmap any sense of urgency to build it was lost.
V2 never got built, and maybe it was never worth building. Even though the debt didn't get worse, we'd lost the agency to do anything about it.
The cost never showed up on the roadmap, but it was real. It doesn't appear as a line item, it just leaks out of everything else, in the time spent maintaining workarounds, debugging unexpected failures, and making a case for buy-in that never came.
What retaining agency looks like
The data export service is a story about losing agency gradually. Building the heatmap visualisation on the frontend was the counterpoint, a case where the debt stayed visible long enough to act on, because there was something to measure it against before anyone could deprioritise it.
The heatmap visualisation was a known problem area. We were building a feature to overlay series data on top of the existing heatmap, and before committing to the build we ran a POC to understand what we were actually dealing with. We built a basic version with throwaway code, just enough to get it rendering, then profiled it, tracked the number of re-renders, and played with it to develop a feel for the interaction. We also evaluated how difficult it would be to extend, specifically what it would take to add new series into the chart's render function. What we found confirmed what we suspected, the component was re-rendering far more than it needed to, the interaction was laggy, and building on top of it without cleaning it up first would compound the problem rather than contain it.
That gave us something concrete to bring to the scoping conversation with product. So we went from "the code needs cleaning up" to "we tested it and it's not hitting the threshold we need." The threshold came from UX research on human attention - beyond 1 second users lose the feeling of direct manipulation, beyond 3 seconds task continuity breaks down - so the reference points were simple enough to explain to anyone in the room and grounded in something beyond engineering opinion.
Which is where SLIs and SLOs become useful in practice. The SLI is the actual measurement, response time in milliseconds captured through profiling. The SLO is the target, under 1 second for interactions. One tells you what's happening, the other tells you whether it matters. Together they give us something objective to point at, and objective things are harder to deprioritise than engineering opinions.
So we addressed the tech debt before building on top of it. The code cleanup improved readability, reusability and maintainability, and the feature we built on top was stable rather than compounding what was already fragile. The contrast with the data export service isn't that we were more disciplined, it's that we had a number attached to the problem before anyone could argue the frontend looked fine.
What to do differently
The practical difference between the two stories isn't discipline or experience, it's whether there was a mechanism to keep the debt visible before the urgency dissolved.
The most important thing you can do when you create debt is name the trade-off and set a trigger at the same time. Not "we'll address this later" but "we'll address this when X happens," where X is something specific and observable, a performance threshold breached, a file size ceiling hit, a new feature that requires building on top of the fragile thing. The trigger is what keeps the agency intact. Without it the debt sits in later indefinitely, losing ground to whatever is more urgent, until the workarounds have made it look like everything is fine.
The second thing is to translate the cost into product consequences rather than engineering metrics. Render counts and re-render cycles don't move roadmaps, but "this will make the next feature take three times as long to build" or "this is why the last three small changes caused unexpected failures" does. The conversation changes when the cost is expressed in terms product already cares about, and that's what gets it prioritised.
The third thing is to set your thresholds before something breaks rather than after. Ground them in something beyond engineering opinion, UX research on attention and response times, industry benchmarks, observed user behaviour, contractual performance commitments. Any of these give you a reference point that's harder to argue with than "it feels slow." Pick your reference points early and treat breaching them as a signal that forces the conversation.
Closing
None of this eliminates the tension between shipping and addressing debt. Deadlines don't move because the architecture isn't ready, and sometimes the trade-off is forced. The v1 data export service was the right call given the constraint. What wasn't right was shipping it without a condition for coming back to it. The workarounds made it look like progress, but the urgency dissolved, and v2 never got built.
Name the debt when you create it, set the condition for addressing it, and treat the workarounds as exactly what they are. Otherwise you aren't managing the debt, it manages you.