
When Technical Debt is the Right Answer
Your no-BS weekly brief on software engineering.
Join 100,000+ developers
We’ve all been there—facing a looming deadline with a growing backlog, knowing there’s no way to finish everything in time. It’s a game of chicken with the clock.
Spoiler: the clock usually wins.
So what do we do? We cut corners. We push forward with solutions we know aren’t ideal, all in the name of shipping something viable. In software development, this is what we call technical debt—knowingly (or unknowingly) making trade-offs that will need to be addressed later. But not all debt is created equal. Some shortcuts cripple a product over time, while others enable rapid iteration and faster pivots.
The key is knowing the difference between a poor decision and a strategic one.
The Spectrum of Technical Debt
Martin Fowler’s Technical Debt Quadrant provides a useful framework for categorizing debt. It breaks down along two dimensions: reckless vs. prudent and deliberate vs. incidental.
- Reckless debt happens when teams rush ahead with no plan or ignore best practices.
- Prudent debt is intentional—teams understand the trade-offs and document their rationale.
- Deliberate debt is a conscious choice, often made to meet deadlines or validate a product idea.
- Inadvertent debt occurs when teams lack the necessary knowledge at the time, only realizing their mistakes later.
Some of these are recoverable, some aren’t, but all of them have consequences.
So what does this look like in practice? Let’s break it down into The Good, The Bad, and The Ugly.
The Good: When Technical Debt is a Smart Move
Not all technical debt is bad—sometimes, it's the right call. Take handling job execution and real-time updates.
Say you're building an order-tracking dashboard. WebSockets could push real-time updates as job execution progresses, but setting that up takes time. Instead, the team starts with background jobs and requires users to refresh the page to see updates. This keeps things simple, allowing the feature to ship faster.
The trade-off? No live updates. But if users don’t need second-by-second status changes, **this is an acceptable short-term decision. The team can always introduce WebSockets later when real-time visibility becomes necessary.
This is the kind of debt you choose to take on, not the kind that sneaks up on you. When used intentionally, technical debt can be a tool for speed, not a ticking time bomb.
The Bad: When Debt Creates Unnecessary Friction
Some technical debt won’t force a rewrite, but it will slow you down in ways that could have been avoided.
Take background jobs without real-time monitoring. Some teams assume jobs will “just work” and skip proper retries or failure tracking. Everything seems fine—until users report missing orders or delayed actions, and nobody realizes jobs have been silently failing for days. Now, instead of a quick fix, the team is scrambling to diagnose and retroactively add logging, alerts, and retries.
Or consider an admin dashboard where pagination was skipped because "there weren’t that many records yet." It works fine—until the dataset grows, queries slow to a crawl, and you’re suddenly fixing performance issues under pressure.
This kind of debt creates unnecessary operational headaches—the kind that cost more to fix later than they would have to do right the first time. It's the coding equivalent of saying, "We'll fix it in post."
The Ugly: When Debt Becomes a Trap
Some shortcuts don’t just slow you down—they lock you in.
A common mistake is embedding business logic directly inside background jobs, rather than keeping it in a service layer. This gets even worse when the job interacts with a third-party integration.
I once worked with a client whose entire video session workflow—provisioning, metadata setup, and API calls—was buried inside Sidekiq jobs. When they needed to switch video platforms, every background task had to be rewritten. The fix? Untangling business logic from job execution and introducing a service layer.
But by then, the damage was done—what should have been a simple provider swap became a painful rewrite.
Or, let’s talk about healthcare. I once reviewed a preregister
method—500+ lines
long, full of nested if statements and multiple return paths. It was lifted straight from a legacy system because "it worked." Years later, preregister
still exists, only now it’s even worse. Nobody wants to touch it. This is the worst kind of technical debt—the kind that grows over time, turning every change into a potential disaster.
When to Take on Debt—and When to Walk Away
Not every feature needs pristine, scalable code. The real question is: Will this feature stick around?
Mission-critical features demand careful design because they form the backbone of your product—cutting corners here can create insurmountable issues down the road. But for disposable features, like a one-off campaign or an internal tool, a messier approach may be acceptable—as long as it remains isolated from the rest of your codebase.
Sandi Metz calls these "omega messes"—code that’s ugly, but contained. It’s not ideal, and it’s not something you want more of, but sometimes it’s a tradeoff worth making.
The real challenge is knowing whether technical debt is an investment or a liability. Some shortcuts buy time and flexibility, allowing you to validate ideas. Others create bottlenecks that drag development into a cycle of endless rework. The trick is recognizing which debts give you leverage—and which ones will leave you stuck.
Learning from the Real World
Real-world examples show the consequences of different types of technical debt.
- Startups often take on deliberate, prudent debt—building scrappy prototypes for Demo Day, knowing they’ll refactor later.
- Mid-sized companies that rushed to build tightly coupled applications often find themselves paralyzed years later. Their once "temporary" workarounds become permanent bottlenecks, slowing every new feature down.
- The worst cases? Teams that unknowingly take on reckless, incidental debt—realizing too late that they misunderstood the framework or technology, leading to painful rewrites.
Understanding these patterns helps teams make better trade-offs.
Making Smarter Trade-Offs
The key to managing technical debt isn’t avoiding it entirely—that’s impossible. Instead, it’s about making informed choices. Before deciding to take on technical debt, consider:
- How rigid is the deadline? If there’s no flexibility, shortcuts might be necessary—but only if there’s a plan to repay the debt later.
- Is this feature mission-critical? If it’s central to your product, ensure the code remains extensible and maintainable.
- Will this feature evolve over time? If future iterations are expected, avoid an approach that could make modifications difficult or impossible.
- What is the cost of future refactoring? Some shortcuts are easy to undo, while others will require a full rewrite.
- Do you have a repayment plan? Document why debt was incurred and schedule time for refactoring before it compounds.
Technical debt, when used strategically, can accelerate development and create opportunities. But without foresight, it can bury teams under a mountain of maintenance work.
You can’t always beat the clock. But if you’re smart about your trade-offs, you can at least keep it from beating you.
What’s Next: Managing the Technical Debt You Couldn’t Avoid
Now that you know when technical debt can be the right call, the next step is learning how to manage the debt you couldn’t avoid.
Next week, we’ll dive into strategies for repaying technical debt without slowing down development, including:
✅ How to prioritize which debt to tackle first
✅ Ways to advocate for refactoring time with leadership
✅ Long-term strategies to prevent debt from spiraling out of control
Be sure to join the Nerd Gang Newsletter so you don’t miss it!