Work & Career
You Can't Pay Off All Technical Debt, So Start with the One Most Likely to Blow Up
Translated from the original Chinese essay · Read the Chinese original →
For a while, our team's codebase felt like a dilapidated building. Touch one thing and another would break. Business kept pushing for new features, and every release felt like a prayer to keep things stable.
Everyone said, "We have too much technical debt. We need to pay it down." But whenever we tried to start, someone would stop us: "This doesn't matter much. Let's leave it for now."
The reality is, it's not that we didn't want to pay it off—we simply couldn't find the time to clear all of it. One feature just shipped, and another request arrived. We'd just finished refactoring one piece of logic, and before we could catch our breath, the next project was already scheduled.
So we fell into a familiar loop: Know there's a problem → Want to pay down debt → Get interrupted → Debt piles up more → Higher chance of things going wrong.
In this situation, "clearing all technical debt completely" is almost an illusion. It sounds like professionalism, but it's really just perfectionism that refuses to face reality.
But if we shift our perspective, we'll see that it's not that technical debt can't be paid—it's that we shouldn't start by thinking we need to pay it all. Instead, we should first find the one most likely to blow up.
What We Really Lack Isn't Perfect Time, but the Ability to Prioritize
Many teams, when facing legacy systems or messy code, have an instinctive reaction: "It's too messy. We need to refactor."
But refactoring isn't the goal. The question is: Why refactor? What is this code affecting right now? How much time do we expect to spend, and what return will we get?
Without answers, the debt repayment plan easily becomes a formality, or an excuse to push back on business.
Truly mature teams don't focus on whether debt exists, but on which debt, if it fails, would be truly unbearable.
For example, configuration files that need manual changes every release, middleware that all interfaces depend on but no one can fully explain, or critical paths where a single error brings down the entire platform.
They may not be the worst-written code, but they have the biggest business impact. In other words, you don't necessarily pay off the ugliest debt first—you pay off the most dangerous one.
Selective Maintenance Isn't Giving Up; It's Strategic Maturity
Many people hear "don't pay off all technical debt at once" and think it means slacking off.
But it's actually the opposite. Selective maintenance means that, after acknowledging limited resources, we make a rational plan for system safety and evolution.
Our team once tried a big-bang overhaul, marking every module as needing optimization. We ended up exhausted just listing todos, let alone actually doing the work.
Later, we changed our approach. Instead of starting from a technical perspective, we looked at our development process and reviewed the past month: Which modules were repeatedly modified? Which places required changing ten files for one edit? Which areas broke as soon as a new teammate touched them?
Soon, some high-frequency pain points surfaced.
We didn't hold endless refactoring meetings. Instead, we circled the three modules most prone to problems, with a clear goal: reduce risk and improve collaboration efficiency, not make things more perfect.
Within two weeks, our delivery speed improved noticeably, and code reviews had fewer arguments. Most importantly, people began to understand: being responsible doesn't mean paying off all debt—it means holding the line on the critical few. That's what truly maintains system health.
The Real Danger of Technical Debt Isn't Old Code; It's That It Slows Down Decisions and Execution
We once had a permission system where the logic was hardcoded from the start. It was fine at launch, but as business grew more complex, we needed temporary permissions, dynamic policies, and role-based access.
Every time we touched the permission logic, product and dev spent a whole day confirming conditions in chat, and deployment required two rounds of testing. On release day, nobody dared to touch it.
That's the direct cost of technical debt—not that the code looks ugly, but that it has become an obstacle to delivering new features.
Later, we calmed down and spent two weeks refactoring that part, changing it to a configuration-driven, interface-isolated structure. On launch day, hardly anyone noticed. But when the next iteration took only half a day to complete a feature, everyone said the debt was worth paying.
So, deciding whether to pay off a debt isn't about how old the technology is. The question is: Is it dragging down our current and future actions?
Some Technical Debt Is Safer Left Alone Than Touched
Not all technical debt should be cleared immediately. We've encountered a piece of logic that ran for five years. It looked ancient, but it was stable, and no one had ever seen it cause a major problem.
In that case, rather than risk rewriting it and introducing bugs, it's better to seal it off: wrap it with a clear interface, document how to call it, and stop modifying it casually.
We also added an internal label: frozen debt. It means we acknowledge the problem, but we won't touch it for now, unless there's a major architectural change.
This approach not only saves energy but also avoids introducing new bugs. Maintenance isn't black and white. Sometimes controlling risk is more valuable than digging up all the old accounts.
How You Phrase It Matters: Saying "We're Preventing Incidents" Gets More Understanding Than "We're Paying Technical Debt"
Often, when technical teams communicate about technical debt, they naturally say, "We've been paying down debt."
But to non-technical colleagues, that can sound like internal tinkering, unrelated to business, or even like stalling.
What if we rephrase it?
For example: "We optimized the permission module so future policy changes can go live faster." Or: "We're streamlining the interfaces to reduce integration time and support the big project next quarter."
Using different language, we're not saying the code is more elegant—we're saying it better supports the business. Trust and support naturally follow.
This isn't about showmanship. It's about helping others understand what we do and its value. That understanding doesn't mean engineers are becoming product managers; it means engineers are making their choices more influential.
We all know technical debt can never be fully paid off. That's not avoidance—it's a clear-eyed recognition of a system's real lifecycle.
The truly useful strategy isn't to pay it all off, but to know when, where, and how to act for the best return.
A mature technical team isn't one without debt. It's one that knows which debts to handle immediately, which to control and defer, and which aren't worth investing in at all.
We don't refactor to make code beautiful. We refactor so the team can move forward steadily.
We don't argue about idealized architecture. We argue to avoid last-minute patches on the next release.
Maybe we'll always be on the road to paying down debt. But as long as we pick the right priorities, we won't be crushed by debt—we'll walk more steadily.
—
One final question:
Lately, which part of our system makes us break things with every change, makes us nervous at every release, and makes handoffs a headache?
Maybe the answer lies in that one debt we should pay first.
May we all become people who don't prove professionalism by overhauling everything, but who can judge, control, and steady the situation at critical moments.