Technical Debt in Custom Software: How to Decide What to Fix

A practical technical debt guide for custom software teams: connect shortcuts to change cost, rank the interest they create, and pay down risk through the work that matters next.

Krishnam Murarka Updated 2026-07-14 Software Engineering

Technical debt is a decision about future change cost, not a label for every old or unpopular part of a codebase. In custom software, a shortcut may have been rational when it helped a team learn or meet a date. The problem begins when nobody can state what the shortcut costs now, who owns it, or what evidence would make repayment worthwhile. Martin Fowler’s metaphor distinguishes the principal of improving internal quality from the interest paid through slower or riskier changes. That framing is useful only when it leads to a concrete next decision rather than a larger backlog of vague cleanup.

Start with a capability the business is actively changing. If a customer onboarding flow takes two weeks to alter because permissions, validation, and notifications are tangled, the debt is not “bad architecture” in the abstract. It is the repeated delay, defect risk, and support effort created by that boundary. The software modernization guide helps when the constraint requires staged replacement, while the authentication production guide shows why debt can become a security and operations concern as well as a delivery concern.

Name the capability and the shortcut precisely

A useful debt record has a narrow boundary: affected capability, shortcut or constraint, intended benefit at the time, current consequence, owner, and trigger for review. Include the users and records at risk. “Legacy API” is too broad to govern. “Order approval depends on a shared controller that also formats emails; adding a new approval rule requires touching three unrelated paths and has caused two regressions” is specific enough for product and engineering to challenge. The record should also state what must not change while the team repairs the internal structure.

Separate debt from a defect, a feature request, and a preference. A defect violates the current promise; a feature request changes the promise; debt makes future work harder or riskier even if today’s behavior is correct. A preference may be worth discussing, but it should not compete for urgency until it is tied to a recurring consequence. This vocabulary prevents a team from using the debt label to win a style argument while missing the unowned migration, unsupported runtime, or manual recovery step that is actually harming the product.

Record fieldExample for custom softwareWhy it matters
CapabilityCustomer onboarding approval.Connects the issue to a real journey.
ShortcutRules and email formatting share one controller.Makes the design choice inspectable.
InterestEach new rule needs three regression-prone edits.Shows recurring cost, not only discomfort.
OwnerOnboarding service team with product partner.Creates a place for the decision to live.
TriggerNext two approval changes or a security finding.Defines when repayment becomes urgent.

Trace the interest the team pays

Six-stage technical debt decision flow from naming the shortcut to measuring repair.
A six-stage technical debt path: name the shortcut, trace the interest, rank the risk, choose a seam, pay down gradually, and verify the change.

Interest appears in the work that takes longer, needs more coordination, or carries more risk because of the shortcut. Ask the team to record a light-weight estimate after a change: actual effort, effort they believe a clearer design would have required, delay caused by waiting for an owner or environment, defects found after release, and manual operations created to compensate. These are imperfect measures, but they make a conversation less dependent on the loudest memory. Fowler also cautions that estimates are uncertain; use trends and repeated examples, not false precision.

Look for interest in multiple forms. A tangled module may add engineering days. A missing migration path may add release coordination. A shared database may add incident blast radius. A manual reconciliation step may consume on-call hours every week. Google SRE’s definition of toil is helpful when a workaround is repetitive, automatable, tactical, and grows with service size. Not every unpleasant task is debt, but recurring operational effort that produces no lasting improvement is evidence that the system or process needs a better boundary.

For the onboarding example, measure the lead time from rule request to verified release, the number of files or teams touched, failed acceptance cases, and support tickets caused by inconsistent approval messages. If the team repairs the boundary, compare the same signals across the next two changes. The goal is not to claim that a refactor “paid off” because it felt cleaner; it is to show that the next capability change became easier, safer, or more observable.

Interest signalCollection methodUse in prioritization
Change lead timeRecord requested, merged, and released dates.Compare recurring delay before and after repair.
Coordination loadCount teams, approvals, and handoffs.Find debt that blocks ownership or throughput.
Failure costLink defects, rollbacks, and incidents to the boundary.Elevate debt that threatens reliability or trust.
Manual workTrack repeated operator minutes and interventions.Identify toil that can be engineered away.
Repair costEstimate a narrow seam or migration slice.Compare principal with likely future interest.

Rank debt by change frequency, consequence, and reversibility

Prioritize the debt that is both active and consequential. A terrible but stable module may be cheaper to leave alone than a moderately messy module that every new feature touches. High-change areas pay interest repeatedly, and their failure consequences often spread through customer data, access control, billing, or support. Score the capability with a simple conversation: how often does it change, what harm follows from a mistake or delay, how many people must coordinate, how visible is the evidence, and how reversible is a repair? Use the score to start a decision, not to pretend that a spreadsheet can settle architecture automatically.

Security and maintenance debt deserve a shorter tolerance when the consequence is not merely slower delivery. NIST’s Secure Software Development Framework emphasizes practices that help prevent vulnerabilities, reduce impact, and address root causes. A dependency that is hard to patch, a logging path that cannot support investigation, or an authorization check duplicated across endpoints may be debt with an external deadline. Record the obligation, owner, and temporary control separately from the longer-term redesign. “We will fix it eventually” is not an acceptable control for a known exposure.

  • Prefer debt in capabilities that the roadmap will change repeatedly.
  • Escalate debt that can cause security, data, or availability harm.
  • Treat unowned cross-team dependencies as a delivery risk.
  • Separate a temporary compensating control from the repair itself.
  • Choose slices that can be verified and rolled back without a big cutover.

Pay down principal through a seam in real work

The safest repayment is often attached to a feature the team already needs. Extract a stable decision function, introduce a versioned interface, add a repository boundary, or move one source of truth while the capability is open. This approach keeps the repair connected to a user outcome and lets the team compare the old and new paths with real inputs. Avoid a broad rewrite whose proof will arrive only at the end. The code review systems guide is a useful reminder that ownership and review evidence matter as much as the new structure.

Write the seam as a contract. Name what the old caller may depend on, what the new component guarantees, how errors are represented, and who owns compatibility. If data moves, define dual-read or dual-write behavior only when it is necessary, and include reconciliation rather than leaving two stores to drift. If a module becomes a service, do not call the migration complete until latency, authorization, failure, and rollback behavior are measured. A smaller boundary with strong evidence is more valuable than an architectural diagram that cannot survive a partial rollout.

Prevent repayment work from creating new debt

Debt work can create its own shortcuts: a migration script with no restart rule, a compatibility layer that nobody owns, a test suite that checks only the new path, or a flag that remains forever. Add exit criteria to the repair before coding begins. Include the old path removal condition, data reconciliation, documentation update, monitoring, and a date or event for reviewing the result. Keep temporary code visibly temporary through naming, ownership, and an issue that states what will be deleted. Otherwise the repair may reduce one interest payment while adding another hidden loan.

Use automated checks where they guard a real boundary. A module dependency rule, schema compatibility test, vulnerability scan, or ownership check can prevent regression. But tools do not choose the right boundary or decide whether a capability is stable enough to leave alone. The SEI notes that architectural debt can be missed by code-oriented metrics; combine static signals with change history, incident evidence, operator effort, and stakeholder interviews. A healthy team can explain why a rule exists and when it should be revisited.

Review whether the interest rate is falling

Set a review date or a measurable trigger. After the repair, inspect the next few changes in the capability and ask whether the team touched fewer surfaces, waited less, released with more confidence, or recovered faster. If the expected signal did not improve, the selected seam may have been cosmetic or the real constraint may be elsewhere. Document that result without blame. Technical debt management is an ongoing decision system, not a one-time cleanup campaign, and a failed repair is useful evidence when it changes the next choice.

Keep the ledger small enough to be used. Retire entries when the capability, risk, or owner changes; merge duplicates; and close items that no longer create meaningful interest. Use a standing review with product, engineering, and operations when a debt item blocks a roadmap commitment or consumes recurring support time. The software modernization guide can guide a larger program, but the same discipline applies to a one-week refactor: name the outcome, stage the change, and preserve proof.

Run a short technical debt decision review

Use a thirty-minute review for each candidate. The author brings one concrete example, the affected capability, and the current evidence. A product partner states the roadmap or user consequence. An operator or support partner explains the recurring work or failure mode. The group decides whether to leave the debt alone, add a compensating control, repair it during the next change, or fund a bounded work item. Record the decision and the reason. This process is deliberately small: it makes trade-offs visible without turning every refactor into a committee.

Before approving a repair, ask what would prove that it is complete. A passing test suite may show implementation correctness but not ownership, operational recovery, migration cleanup, or reduced change cost. Conversely, a large rewrite may be unnecessary if a narrow interface reduces the most expensive interest. The REST API contracts guide gives a related example of why a contract must include compatibility and operation, not just an endpoint shape.

  • State the customer or operator capability that is paying the cost.
  • Show one recent change, failure, or manual workaround as evidence.
  • Name the shortcut, owner, consequence, and review trigger.
  • Choose leave, control, slice, or program-level remediation.
  • Define completion evidence, rollback authority, and cleanup work.
  • Revisit the interest signal after the next relevant changes.

The recommendations here are grounded in inspected primary guidance: Technical Debt, Google SRE: Eliminating Toil, Managing Technical Debt with Data-Driven Analysis, Secure Software Development Framework. These sources describe debt, toil, risk, and secure delivery; this guide turns those ideas into a repair decision for custom software.

Takeaways for managing technical debt in custom software

  • Describe debt as a shortcut and the recurring interest it creates.
  • Prioritize active, consequential boundaries rather than old code by age.
  • Use product changes as seams for gradual repayment where possible.
  • Treat security, data, and reliability exposure as time-sensitive debt.
  • Make the repair itself observable, reversible, and removable.
  • Review real change evidence to see whether the interest rate is falling.

Questions teams ask before paying down technical debt

Is all messy code technical debt?

No. Debt is a useful term when an internal choice creates a recurring cost or risk that the team can describe and manage. A defect, missing feature, or style disagreement may need different treatment. Name the capability, consequence, owner, and trigger before calling cleanup debt.

How can a small team measure technical debt without a complex tool?

Track a few repeated signals: change lead time, files or teams touched, incidents or regressions, manual operator minutes, and the difference between actual effort and a rough clean-system estimate. Review trends in one capability rather than trying to produce a universal debt score.

When should debt become a dedicated project?

Make it a dedicated project when the boundary blocks several roadmap commitments, has a security or data consequence, needs a migration across teams, or cannot be safely repaired inside normal feature work. Even then, split the program into evidence-bearing slices with rollback and ownership.

Conclusion: make technical debt a managed trade-off

Technical debt becomes manageable when a team can see the shortcut, the interest it charges, and the smallest repair that will change future work. Record the capability, rank the consequence, attach repayment to real change, preserve safety controls, and review the evidence. That practice gives custom software room to evolve without turning every delivery decision into a fight between speed and quality.

Continue with related articles