Skip to content
ExponaBLOG
ENGINEERING CRAFT · DAY 049 / 100

Keep a tech debt register: practical tips and trade-offs

Invisible debt never gets prioritized.

Expona Admin6 min read

Keep a tech debt register

I learned this lesson the hard way after shipping feature after feature while our codebase slowly became a maze of workarounds and half-finished refactors. The turning point came when we couldn't explain to product why a simple feature took three sprints, and we realized our technical debt had become invisible. Without a formal tech debt register, debt never competes fairly for prioritization against new features—it just becomes the invisible tax you pay every sprint.

Why invisible debt never gets prioritized

Picture this: your team discusses the roadmap, and someone mentions that the authentication service takes 800ms to respond on cold starts. Another person brings up that your deployment pipeline fails silently 10% of the time. A third mentions that the payment processing code is impossible to test without mocking half the database. These concerns disappear into Slack, get forgotten, and six months later you're still paying the cost.

The problem is cognitive. New features are tangible—they have acceptance criteria, they ship, users see them. Technical debt is invisible. It doesn't appear on dashboards or user-facing metrics. It slows down feature delivery, but that slowness gets attributed to "complexity" rather than to a specific debt item. Product teams can't justify investing time in something they can't see or measure.

A tech debt register makes debt visible and comparable. It forces us to name the problem, quantify it, and track it over time. Without that structure, debt accumulates until it becomes a crisis.

List each item with impact and effort

Start by capturing every piece of tech debt you identify. For each item, record three essentials: the description, the impact, and the effort to resolve it.

Impact describes what happens when you don't address this debt. Is it a performance problem? Does it slow down feature development? Does it increase bug rates? Does it create operational toil? Quantify where possible. For example:

  • Deployment takes 45 minutes; refactoring the CI/CD pipeline would reduce it to 12 minutes, saving 2 hours per deployment team member per week.
  • The legacy payment reconciliation job fails monthly, requiring 6 hours of manual recovery work.
  • Database queries in the order service timeout under load, causing 2-3% of transactions to fail during peak hours.

Effort is the story points or days needed to resolve it. Be realistic. It's better to overestimate and finish early than to underestimate and bleed time.

Here's what a simple register might look like in a spreadsheet or tool like Notion:

Item Impact Effort (days) Priority Status
Monolithic auth service lacks circuit breaker Cold starts: 800ms → cascading failures every 3 weeks, 30 min recovery 5 High Backlog
Legacy reporting queries N+1 on 50k records Reports timeout; product can't run end-of-month analytics 3 High Backlog
No structured logging in payment service Debugging payment issues takes 4+ hours per incident; 3 incidents in last quarter 2 Medium In Progress
React component test coverage below 40% Refactors introduce UI bugs; QA catches 8-12 per release 8 Medium Backlog

This forces prioritization by making trade-offs explicit. Should you spend 5 days on the circuit breaker or 8 days on test coverage? Now you can have that conversation with data instead of opinions.

The most powerful pattern I've found is connecting tech debt directly to operational pain. When an incident happens, trace it back to a debt item. When a feature takes longer than expected, ask: which technical decisions are slowing us down?

For example, after a payment processing outage, your incident review might identify that the legacy monolithic service has no proper circuit breaker. Add that to your register with a link to the incident postmortem. Three months later, when you see another timeout, you can pull up the register and see this is the second time the same debt caused pain.

Similarly, when estimating a feature and the engineer says "it's complicated because the auth service is tightly coupled to the API gateway," capture that. Link it to your auth debt item. Over time, you'll see which debt items create the most friction.

Here's a concrete example. Suppose your e-commerce platform has a "checkout" feature that used to take 2 sprints and now takes 3. Add an entry to your register:

typescript
// Example: Feature velocity tracking with debt correlation
interface FeatureSlowdown {
  feature: string;
  sprintsNow: number;
  sprintsBefore: number;
  correlated_debt_items: string[];
}

const checkoutSlowdown: FeatureSlowdown = {
  feature: "Add gift card support to checkout",
  sprintsNow: 3,
  sprintsBefore: 2,
  correlated_debt_items: [
    "Order service lacks proper caching layer",
    "Payment service tightly coupled to checkout UI",
    "No async job queue for reconciliation"
  ]
};

Now when product asks why checkout development is slower, you have a concrete answer backed by technical items you can plan to resolve.

Review monthly with product

This is where the register becomes alive. Set up a 30-minute monthly meeting with at least one product stakeholder. Review:

  • New incidents: Did any new debt items emerge? Link them to your register.
  • Closed items: What did the team resolve? Celebrate and communicate the impact.
  • Reprioritization: Have business priorities shifted? Update impact scores accordingly.
  • Effort estimates: Were the effort numbers accurate? Recalibrate for future estimates.

The meeting should feel collaborative, not accusatory. You're not blaming product for "letting debt happen." You're saying: here's the cost we're paying, here's the path to reduce it, here's the trade-off against new features.

A good conversation might sound like: "We had two payment reconciliation failures this month. Both trace back to the legacy job scheduler debt item. If we resolve it, we save roughly 10 engineering hours per month in incident response and manual fixes. It'll take 4 days to fix. Should we allocate one engineer for two sprints, or should we wait until next quarter?"

Product can now make an informed decision. They might say: "Let's do it next sprint," or "Actually, we need velocity for the new feature, can we live with the risk?" Either way, it's transparent.

Celebrate items closed

Teams rarely celebrate paying down debt. We celebrate shipping features. But debt payoff is shipping too—it's shipping stability, speed, and capacity for future features.

When your team closes a debt item, communicate it. In your sprint review, spend 90 seconds on it. Show the before and after: "The auth service circuit breaker is live. Cold starts dropped from 800ms to 120ms. We've eliminated this class of cascading failure." Post it in Slack. Link it in your register as "Closed - June 2024."

This does two things. First, it reminds the team why they invested time in something invisible. They see the payoff. Second, it builds momentum. When engineers see that debt closure is valued and celebrated, they're more likely to flag debt proactively in the future.

Over a year, closing 10-15 meaningful debt items is a substantial achievement. It should feel that way.

Common mistakes

  • Treating the register as write-only: A register is only useful if it's reviewed and acted on. If you add items and never revisit them, it becomes a backlog of despair. Review it every month.
  • Inflating effort estimates to avoid doing the work: Sometimes teams estimate debt at 20 days to make it seem too expensive to prioritize. Be honest. If it's genuinely hard, say why. If it's actually a 3-day job, own that.
  • Losing context over time: Document *why* each item is debt. "Legacy code" is useless. "This service was built to handle 1,000 concurrent users; we now hit 50,000 and need proper sharding" is actionable.
  • Mixing debt with feature work in the same sprint without discipline: It's fine to allocate 30% of sprint capacity to debt. It's not fine to let debt silently consume time while features slip. Allocate, track, report.
  • Forgetting to link incidents to debt items: The register is most powerful as a feedback loop. Every incident that traces to debt should update the register and increase the item's priority.

Key takeaways

  • Technical debt is invisible until you name it. A register makes it visible and comparable to feature work.
  • For each debt item, record the impact (what pain does it cause?) and effort (how long to fix?). This forces prioritization conversations.
  • Link debt items to incidents and slow features. Use operational pain as the primary signal for what to prioritize.
  • Review the register monthly with product. Debt closure is a business decision, not just an engineering one.
  • Celebrate closed items. Debt payoff is real work that enables faster future delivery. Make it visible and valued.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles