← All writing
Philosophy · · 9 min

The loan with no lender

Debt works as an idea because somebody is coming to collect, and in a codebase nobody ever is.

Culture Agency

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, which are spread across 3 files, 2 of which are live depending on a setting nobody remembers turning on.

It has outlived 2 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 3 years.

Nobody has ever argued against doing it. That’s the part I keep coming back to. There is no opposition anywhere in this story. Every person who has looked at the ticket agreed it should be done, including the client, including the developer who inherited it from me for 8 months in 2023, including me, who wrote it and then didn’t do it, 3 years running 😞

3 years of unanimous agreement and 0 days of work.

I used to file that under prioritization. I don’t anymore. My sense now is that 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, because what’s left is 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, and Martin Fowler’s write-up is the version most of us have actually read. It was narrower than what we do with it now and considerably more interesting.

His 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.

His interest was immediate and personal, which is the part that matters for my purposes. While the gap is open, every change is harder, and you are 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 said later, 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, mine very much included.

The interest is real. I’m not making the argument that bad code is free.

But look at who pays it and how. It gets paid by whoever opens the file, 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. So it goes into the estimate instead, quietly, as 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 quietly accounts for it, and nothing anywhere names it out loud.

Then there’s the other half, which is that a lot of it costs nothing whatsoever. 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

The part that’s missing isn’t the principal and it isn’t the interest rate, though those are what people usually go after first.

Real debt arrives with machinery attached, and the machinery is external to you. There’s a date somebody else picked, a person whose actual job is to notice you missed it (read: nobody, anywhere, in any codebase), and a consequence that arrives whether or not anyone in your organization thinks it should. That apparatus is what makes the word do any work, and the number is almost incidental to it.

None of that came across when we borrowed the term. There is no date. Nobody’s job includes noticing. Nothing happens on the 15th. The guilt made it over, and it’s the one component 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, and analogies get to break somewhere. I’d just say the place this one breaks is the place we’re relying on it.

Because it explains the cleanup sprint, the quarterly allowance, the 20% of every cycle. Every mechanism anybody has invented for this is a voluntary one, and voluntary mechanisms get canceled the first week something real turns up. I watched the same shape play out with a performance budget that everyone signed off on and the site then went to more than twice it, and the reason was never that somebody overruled it. There was simply 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, without a sprint, without an allowance, 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, and the only difference between them is that a person outside the building set a date.

And notice nobody calls that work debt. It’s just work. The one category that reliably gets paid is the one we don’t use the word for, which I don’t think is a coincidence. It’s also, I suspect, 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, and the only parts of one a client approves without a conversation are the parts where somebody else’s roadmap is arriving 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, maps onto a category they already fund, and can approve. Before it existed the pitch was “the code is messy and we’d like a few days,” which sounds like craftsmanship, which sounds like preference, which loses to a feature every time. I used the debt framing this year, deliberately, on 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 thing borrows the category and not the creditor. So 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.

I might be being unfair, because I’m aware that I’m sitting here criticizing the one instrument that has ever bought me a day.

Give it a creditor

Make the fix ride along. The templates get cleaned up on the next scheduled change to the checkout, inside that estimate, as part of that work instead of as a thing 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.

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. Don’t manufacture one, because everybody can tell, and a fake deadline spends credibility you’ll want later.

And where neither of those is available, close the ticket. Say out loud that it isn’t happening. A ticket that has survived 2 project management tools isn’t a plan, it’s a feeling with an ID number, and 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 went along with a checkout change that was already scheduled, which is the ride-along version. 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, with the reason and with what it costs the next person who opens it. That’s about 20 minutes each time. I wrote the 20 minutes down, because a cost nobody has written down is a cost that 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 handover file gets read by the person who takes over and that column has never been read by anyone. My guess is that a good chunk of what any of us files under debt is in the same position, which is that it’s never going to be repaid, and that 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 3 years.

Read similar posts
9 min

The third proposal

A proposal landed in August to rebuild a client platform from scratch, and when I went digging in the shared drive I found two older ones saying almost exactly the same thing.

8 min

Somebody typed it into a text box

I spent three days on a maintenance retainer taking things out of a client's site, and the last thing on the list turned out to be holding up the one page the whole business hangs on.