tolgacelik.net
All posts
·4 min read

Technical Debt: When Do You Clean Up in Production?

Technical debt is inevitable. Which debt do you live with, which do you pay with a sprint, when do you do a large refactor? A framework built from real production decisions.

tech-debtrefactorarchitectureproductionmaintenance

Every production system has technical debt. Even after one day of existence. That's inevitable. The real question is about recognizing, prioritizing, and paying that debt — otherwise one day it catches you at a very bad moment.

In 14 years I've never seen a "no tech debt" system. What I've seen is the difference between well-managed debt and debt just accumulated by accident. This post tries to draw that line.

What tech debt is and isn't

Technical debt = technical choices made consciously for short-term speed, with long-term cost.

Examples (debt):

  • Code written "fast now, refactor later"
  • Feature merged without tests
  • "Temporary workaround" that got permanent
  • Un-upgraded old library version

Not examples (not debt):

  • A bug (a bug is a bug, not debt)
  • A missing feature (that's a gap, not debt)
  • Code that's better written today than yesterday (that's hindsight wisdom, not debt)

Saying "we have a lot of tech debt" without this distinction is meaningless. Every team has problems of many kinds; debt is one specific category.

Categorizing debt

I think about debt on two axes:

1. How expensive is it to live with?

  • Does it hurt every day, or once a year?

2. How expensive is it to fix?

  • An afternoon, a sprint, months?

Plot them into a 2×2:

To live / To fixCheap to fixExpensive to fix
Cheap to live withFix when the chance comesLeave it alone
Expensive to live withFix nowPlan a big refactor

"Expensive to live / cheap to fix" is where the best ROI lives. This is the quadrant most teams miss. An hour of fixing that saves 5 minutes every day pays off in 12 working days. Do it now.

"Cheap to live / expensive to fix" — accept living with it. A 5-year refactor plan for code that never actually hurts is the fastest way to burn out a team.

When does a big refactor make sense?

A big refactor is justified in exactly two situations:

1. The current architecture can't meet the new requirement

Our case: on-prem wasn't capable of supporting cloud-native features (segmentation, canary, autoscale) without heavy custom work.

Reason: not "the old system is ugly" but "the old system can't do what we need to do now."

2. Security / risk has become serious

Unsupported framework 3 majors behind, critical security vuln, race condition that regularly reproduces in production — these justify a big refactor.

"More readable code" as the trigger is a trap. That refactor never ends.

Daily practice: small refactors

Big refactors are yearly events. Daily, small refactors are the habit:

  • When writing a new feature, clean the old code in the same file
  • When you see 2–3 duplicate spots, consolidate
  • Spend a minute to rename for clarity
  • Remove unnecessary abstraction (over-engineering is also debt, not just under-)

Boy Scout Rule: leave the code slightly better than you found it.

Are "debt sprints" a good idea?

Some teams do "tech debt sprints" — stop normal work for a sprint, pure cleanup. I have mixed feelings.

For: forces work that otherwise never happens. Good for catch-up upgrades.

Against: doesn't sustain. "One day per week for debt" kinds of ongoing practices work better than sprint-sized explosions. Every dev should be paying some debt every day.

My preference: instead of a separate sprint, carve off 15–20% of every sprint for "improvement" — debt + tooling + docs. Each sprint becomes slightly better, no big-bang resets.

What to record vs not

If you're tracking debt, don't open a JIRA epic with 100 tickets under it. 90% will never be read.

Instead:

  • One one-page "known debt" list per major service
  • For each: what, when added, why, priority, expected cost to pay
  • Reviewed quarterly — add new, delete paid, reassess stale

That simple discipline keeps the team's shared view of debt accurate. It's the team lead's responsibility to keep the page current.

Takeaway

Technical debt is a normal part of successful software. Systems without any debt are either too slow to move or never become important.

What matters is taking debt consciously and paying it consciously. Being able to step back and see how much debt you have, and at what cost it hurts, is the skill. Teams that do this don't get hurt by debt; teams that don't end up begging to "rewrite everything from scratch" three years in.

Big or small, see the debt, name it, prioritize it, pay it. That simple loop determines a system's long-term health.

ShareLinkedInX
Get the next one in your inbox

Long-form notes on distributed systems, production stability, AI-assisted shipping. No spam, easy to unsubscribe.

Email only. Stored on this server. Never sold or shared.

Comments

No comments yet. Be the first.

Leave a comment

Never shown publicly. Only used if I need to reply.