The loan with no lender
Debt works as an idea because somebody is coming to collect, and in a codebase nobody ever is.
There’s a ticket in a client’s backlog that I wrote in the winter of 2022. It asks for a few days to clean up the checkout templates. They’re spread across three files, two of which are live depending on a setting nobody remembers turning on.
It has outlived two project management tools. It got renamed once when somebody reorganized the board and again when we adopted a convention for ticket titles. It currently sits in a column called Tech Debt with about 30 others. It has been in some version of that column for three years.
Nobody has ever argued against doing it. That’s the part I keep coming back to. There’s no opposition anywhere in this story.
Every person who has looked at the ticket agreed it should be done. That includes the client, the developer who inherited it from me for 8 months in 2023, and me. I wrote it and then didn’t do it three years running 😞
Three years of unanimous agreement and zero days of work.
I used to file that under prioritization. I don’t anymore. I’ve come to think a loan isn’t really a pile of money you owe. It’s a person who is going to call you on a date you didn’t pick and keep calling.
Take that person out and the concept stops doing anything at all. You’re just left with a number you feel bad about, which is more or less what we’ve built.
Terms and conditions
The phrase comes from Ward Cunningham in the early 90s. Most of us actually know it from Martin Fowler’s write-up. The original was narrower than what we do with it now and considerably more interesting.
Cunningham’s point was about understanding, not about mess. You start building before you fully understand the domain because building is how you come to understand it. So the first version encodes a model you’ve already outgrown by the time it’s live.
The debt is the gap between what the code says and what you now know. You repay it by going back and making the code say the new thing.
In his version the interest was immediate and personal. That’s the part that matters here. While the gap is open, every change is harder. And you’re the one making the changes, today, this week.
There’s no statement in the mail. You just work slower and you can feel it happening to you.
He later said repeatedly that none of this was a license to write bad code and call it borrowing. Which is now roughly the only thing anybody uses it for, me very much included.
Paid in cash
The interest is real. I’m not arguing that bad code is free.
But look at who pays it and how. Whoever opens the file pays it in 10- and 20-minute increments. It never lands anywhere that anybody reads. I spend 20 minutes figuring out which of the 3 templates is live.
That doesn’t go on a line item because “the file is bad” isn’t something you can put on an invoice. It goes quietly into the estimate instead. It’s the reason our number for a checkout change on this site is bigger than our number for the same change somewhere else.
So the client pays it and gets told it’s the price. And I’m the one telling them because in the moment it is the price.
This is the fat end of an estimate from the other side. The tail is fatter on this codebase than on that one. Every number I give accounts for it and nothing anywhere names it out loud.
Then there’s the other half. A lot of it costs nothing at all. A tangled module nobody has opened since 2022 accrues exactly zero, forever, unless somebody opens it.
No lender is adding to a balance in the dark. That alone should have told us the comparison was doing something odd.
No repo man
When people go after the metaphor, they usually start with the principal or the interest rate. I don’t think either one is the missing piece.
Real debt arrives with machinery attached, all of it outside you. There’s a date somebody else picked, a person whose actual job is to notice you missed it (no codebase anywhere has one), and a consequence that comes whether or not anyone in your organization thinks it should. That machinery is what makes the word do any work. The number is almost incidental.
None of that came across when we borrowed the term. There’s no date, no one whose job is to notice, and nothing that happens on the 15th.
The guilt made it over. It’s the one part of a loan that has never made anybody pay anything. Nerfed on arrival.
I know, I know. Comparing a codebase to a loan and then complaining that nobody repossesses it is a lot of weight to hang on one analogy. Analogies get to break somewhere. I’d just say this one breaks right where we’re relying on it.
That break is why we have the cleanup sprint, the quarterly allowance, the 20% of every cycle. Every mechanism anybody has invented for this is voluntary, and voluntary mechanisms get canceled the first week something real turns up.
I watched the same shape play out with a performance budget. Everyone signed off on it and then the site ended up at more than twice the number. Nobody ever overruled it. There was just never a moment where a person had to say no.
Except when there’s a date on it
The exception is worth staring at because I think it’s the whole argument in one move.
Certificates get renewed. PHP end-of-life gets handled. When a payment provider emails to say the old API stops answering in March, the work happens in February. It happens without a sprint or an allowance, and without anybody having to make a case for craft in a meeting.
None of those are more important than the checkout templates. A certificate is 10 minutes of work whose failure mode is embarrassing and survivable. The templates cost somebody real time every single month.
The certificate gets done every time and the templates never do. Somebody outside the building set a date on the certificate. That’s the only difference between them.
And notice that nobody calls that work debt. It’s just work. We never use the word for the one category that reliably gets paid, which I don’t think is a coincidence. It’s probably also why maintenance is so hard to sell as a category.
A retainer is an offer to pay interest on a loan nobody is calling in. Clients approve exactly one kind of retainer work without a conversation, the kind where somebody else’s roadmap arrives on a day you didn’t pick whether they pay or not.
Credit where it’s due
The counter is a good one. It’s that this metaphor is the only thing engineers have ever come up with that gets anybody time.
“We’re carrying technical debt and we need to pay some of it down” is a sentence a finance director understands in one pass. It maps onto a category they already fund, which means they can approve it. Before the metaphor, the pitch was “the code is messy and we’d like a few days.” That sounds like craftsmanship, which sounds like preference, which loses to a feature every time.
I used the debt framing on purpose this year for a project where I wanted two weeks. It worked. I’d use it again next month and I’m not going to sneer at it.
So my complaint is narrower than “it’s a bad metaphor.” It’s that the metaphor borrows the category and not the creditor. It’s excellent at getting a yes and has no opinion whatsoever about whether anything happens after the yes. It’s a very good way to open an account and a terrible way to close one.
Maybe I’m being unfair, since I’m sitting here criticizing the one instrument that has ever bought me a day.
Give it a creditor
My first suggestion would be to make the fix ride along. The cleanup goes inside the estimate for the next scheduled change to the checkout, as part of that work instead of a job of its own. Nobody approves it separately because nobody is asked about it separately. It’s the only one of these I’ve watched work more than once.
Otherwise, I’d borrow a real date where one honestly exists (the support window, the provider’s deprecation, the certificate). If the work can legitimately attach to something that already has an external date, attach it and let the date do the collecting. I wouldn’t manufacture one because everybody can tell. A fake deadline spends credibility you’ll want later.
And where neither of those is available, close the ticket and say out loud that it isn’t happening. A ticket that has survived two project management tools isn’t a plan. It’s a feeling with an ID number. Leaving it open is a way of not deciding while looking like you’ve decided.
Deliberately unfinished
I closed mine in October. Not fixed, closed.
Maybe a third of it rode along with a checkout change that was already scheduled. It got done without a single conversation about whether it should.
The rest went into the project’s handover file, in the section for things that are deliberately unfinished. The entry has the reason and what it costs the next person who opens it, which is about 20 minutes each time. I wrote the 20 minutes down because a cost nobody has written down gets paid forever by whoever happens to be holding the file.
The templates are still bad. They’re bad on the record now instead of bad in a column called Tech Debt. I do think the difference matters because the person who takes over reads the handover file. Nobody has ever read that column.
I’d guess a good chunk of what any of us files under debt is in the same position and will never be repaid. I think writing it down as a known cost is more honest than keeping it in a list whose whole shape implies a plan. I don’t love landing there. It’s just the only account that matches what’s actually happened over three years.