vadimkravcenko

Managing technical debt at an agile company

03 May 2021 ·Updated 04 April 2026

Tuesday’s stand-up started with the same sentence from two different teams — “The build suddenly takes fifteen minutes, what changed?” Nothing changed, of course, except the invisible interest rate on the codebase finally came due. That’s technical debt in a nutshell: yesterday’s shortcut creeping into today’s velocity.

Technical Debt Dilemma

The hard part is balance. On one side the business is yelling “Ship, we need revenue by Q3.” On the other, the devs are quietly reminding us future-selves will need tests, CI, and a clean architecture to keep things moving. I’m not entirely sure a perfect balance exists, but I do know one thing: the debt must be a conscious decision, not an accident of forgetfulness.

  1. We deliberately accept it, fully aware of the price tag (often hidden in slower release cycles six months later).
  2. Or it reveals itself as the domain evolves — you learn the hard way that yesterday’s “elegant” model no longer maps to reality and now needs a rewrite.

Our agency ships half a dozen products at any given moment, so change is our default state. Features pile up, infrastructure scales, product owners pivot, devs roll off projects (sometimes taking half the context with them). Normal. What’s easy to forget is that every one of those perfectly reasonable changes adds a gram of complexity. Give it a year and you’re lugging a suitcase you can barely close.

I could list examples all day — the login form that started as “email + password” and six months later needs Apple, Google, and a loyalty integration because marketing promised it at a conference. (Side note: I still have nightmares about that conference.) Each layer makes sense in isolation, but stack them and you end up with a brittle monster.

Sometimes the complexity shows up for awful reasons — “The CEO wants this by Friday, period.” You can fight it or accept it, but either way the interest compounds. Eventually somebody pays in refactoring hours or lost customers. Usually both.

Build what manager tells you
Don't be like this.

Alright, enough doom. How do we keep the balance from tipping into chaos?

Tips & Tricks how to manage the debt

The toolbox below is what’s worked for us. I could be wrong about the order of importance, but the mix has saved my skin more than once.

Take a step back and a hard look at your codebase

First job: stop the bleeding. Freeze new shortcuts for a sprint and measure where the rot lives. Visualization tools help — we run CodeScene because it highlights “hotspots” where complexity and churn overlap. If you’re bootstrapping, even a simple git log --stat heat-map is a start. The bigger hurdle is cultural: you may have to tell leadership their spending habits (rushed features, half-finished pivots) are the actual driver of debt, not “lazy engineers.” That conversation is rarely fun, but without it nothing changes.

Tech debt ceiling

Indicators of technical debt

If you’re staring at 200k lines wondering where to begin, start small:

  1. Warnings or outdated packages in the build — classic hygiene red flags.
  2. Functions with no docs and cyclomatic complexity in the comic range.
  3. Modules that trigger edits in three other places every time they change (high change coupling).

Quick anecdote: we once moved a service to a new datacenter, rebuilt the container, and it exploded. Turned out the requirements file used >= for half the libs, so a dozen transitive upgrades slipped in. Two developers lost three days tracing phantom version bumps. Lesson learned: lock your deps or be ready to pay the detective fee.

Infrastructure choices belong on this checklist too. A dev-heavy SaaS I know skipped the cloud entirely and estimates it saved them a significant amount over a decade. Bold, yes, but a reminder that tech debt isn’t just code — the wrong hosting bill can bankrupt you faster than nested callbacks.

CodeScene Clusters
Circle represents a module, the larger it is, the more complex it is, the deeper the color the more developers work on it. CodeScene

Negotiation with the business

Debt is cheap to take, expensive to repay — that’s finance 101, yet somehow it shocks product managers every time. Your job is to translate hours into euros: “One-day hack now means four-day rewrite when we add Google and Apple sign-ins.” I usually put the numbers in a slide, keep the tone calm, and — crucial bit — point out that the true blocker might be the org chart itself. Separate CIO and CTO fiefdoms, or product teams isolated from ops, breed debt faster than sloppy code ever could.

If leadership still wants the Friday release, fine, but record the decision in the backlog with a cost estimate. Future you will thank present you for the paper trail.

Waterfall is obsolete. Long live agile.

Invest in the quality of development processes

Process sounds dull until you’ve debugged a Friday-night outage caused by a missing unit test. Good pipelines — automated linting, peer review, release checklists — act as speed bumps against reckless merges. They also serve another motive the business cares about: risk control. In choppy markets the ability to roll back to a self-hosted stack, or to exit a cloud contract, can be the difference between a manageable invoice and a life-threatening one. Process gives you that optionality.

The Broken Windows theory applies. Let one sloppy PR through and you’ll have three next week. Hold the line early; it saves arguments later. (I got this wrong for the first 18 months of my leadership career.)

Old Code

I used to equate “old” with “bad.” Turns out only good code grows old — the bad stuff gets rewritten every quarter. Unless the requirements have changed, that dusty module quietly doing its job is probably fine. Touch it only when new features demand it, otherwise move on to juicier refactors.

Dilbert
Dilbert on point

Engineering mistakes

Not every mess is debt. Sometimes it’s just a mistake — wrong library, premature optimisation, you name it. Bundle those into retros and 1-on-1s, not the debt register. Conflating the two blurs accountability: debt is strategic; mistakes are educational.

Strict Definition of Done

“Done” means the PO is happy and the checklist is green: docs written, tests passing, two reviewers signed off, monitoring configured. Anything short of that is started, not done. Low-hanging fruit like comments or missing assertions? Fix them before merge, otherwise they’ll survive until the next audit.

Low-hanging fruit postponed today becomes high-interest debt tomorrow.

New hires bring their own styling habits — good for diversity, bad for consistency. Enforce a formatter and a style guide from day one. Nobody wins style debates at 2 AM during an outage.

More documentation

Most devs would rather debug Kubernetes than write docs, so bake it into the definition of done. We aim for a Bus Factor > 1 on every critical module. If the only person who understands the payment flow wins the lottery, the doc saves the day. (Well, more like stops the panic long enough for someone else to step in.)

Finding time to work on the problems

Product owners optimise for velocity, so they’ll pack every sprint. Agree on a standing rule: no release ships without at least one chore or refactor ticket closed. We tag ours “DEBT-CUT” in Linear so they stay visible. Invisible debt never gets paid.

Allocate capacity explicitly — a portion of the sprint, depending on pain level. Otherwise the urgent will eat the important, as it always does.

Track metrics overtime

I track four numbers quietly (making them KPIs invites gaming):

  1. Bug origins — new vs legacy code.
  2. Complexity & coupling trends per module.
  3. Ownership concentration — single-point knowledge risk.
  4. Bus Factor movement over time.

So is technical debt inherently bad?

Not really. Debt is a lever. Use it to test a market early, or to outrun a competitor. Abuse it and you’ll drown. I once spent three years polishing a product that never shipped — perfect architecture, zero debt, no customers, and eventually no runway. Contrast that with the scrappy release that goes out in three months, signs paying users, and uses the revenue to clean up the mess. Guess which company still exists.

Infographic on technical debt in agile software development, showing balance between rapid delivery and maintainability.
When someone asks you why technical debt is bad

Take shortcuts if they buy you learning or revenue, but keep the ledger visible and pay it down before the interest cripples you. That, at least, is the equilibrium I’m aiming for — still haven’t perfected it, but each project gets a little closer.

.

Worried your codebase might be full of AI slop?

I've been reviewing code for 15 years. Let me take a look at yours and tell you honestly what's built to last and what isn't.

Learn about the AI Audit →

No-Bullshit CTO Guide

268 pages of practical advice for CTOs and tech leads. Everything I know about building teams, scaling technology, and being a good technical founder — compiled into a printable PDF.

Get the guide →
Cancel