Three different clients. None of them saw it coming.
The first one contacted me when the agency that built their website stopped answering calls. The second when the last developer who still understood their core app retired, and the third when a Supabase invoice doubled overnight and the board started asking why.
What they had in common wasn’t the choice they’d made years earlier. Each of those choices was reasonable at the time. Their shared pain was that the bill arrived as a surprise, and by the time they understood what they were paying for, they were paying for it under deadline pressure.
This isn’t a post about being stingy. It’s about not knowing the price you’re actually paying. Three rewrites I was hired to do, and what each one cost the client before they called me.
Cheap software is a loan you didn’t know you took out.
Every “cheap” software choice has a sticker price and a hidden cost stream that runs for years. The discount at signing is principal you’ll repay later, with interest. Sometimes the interest is small and you’re fine. Sometimes it compounds.
The interest takes specific shapes. Six worth naming.
The bill arrives as a crisis, not an invoice. There’s no monthly statement. No email warning that the loan is about to default. The first signal is usually a broken thing, a surprisingly expensive bill, or a vendor going silent. By then you’re not choosing whether to rewrite. You’re choosing how fast.

Cheap software is a loan you didn’t know you took out.
Three real engagements. Different industries, different stacks, same shape. A cheap choice that compounded into a rewrite, and a client who didn’t see the bill coming until it was already late.
A real estate firm hired a local agency to build their listings site and back office. The cheap move, two cheap moves really, was the price and the packaging. A fixed quote well under what a larger shop would have charged, and a single bundle that covered everything so the firm never had to think about it. The agency hosted it all themselves. Domain, email, SSL, all of it on one invoice. Years went by. Nobody at the firm thought about it because nothing needed thinking about.
The agency started slipping. First the email forwarding stopped working for a week. Then SSL renewals got missed and customers got browser warnings. Then a server moved without notice and the listings site went down for three days. Calls went unreturned. By the time the firm understood the agency was winding down, half their digital infrastructure was on machines they couldn’t access, under accounts they didn’t own, with passwords only the agency had.
The rewrite was as much archaeology as engineering. Recovering what could be recovered. Reconstructing the listings database from a mix of partial dumps and screenshots the staff had saved over the years. Most of the work, before any new code got written, was figuring out what existed, where, and under whose control.
The loan came due in two shapes. Vendor risk showed up first. Security debt sat underneath it. Lapsed SSL, credentials nobody had copies of, account ownership in the wrong hands. The “saving” the firm thought they were getting from a single-vendor bundle (one bill, one phone number, one team responsible for everything) was repaid the moment that phone stopped being answered.
The cheap part wasn’t the price. It was the absence of a contingency.
A single vendor is only a risk when they hold something you can’t easily replace. If the answer to “what happens if they go quiet tomorrow?” isn’t obvious, you don’t have a vendor. You have a single point of failure with an invoice attached.
A mid-sized industrial firm. An app that had started small as an internal portal, and over the years had grown into what the word “enterprise” defines perfectly. Built in old .NET Framework and running on their own Windows server. The original team shipped in the distant 2007. It worked. The mantra was “if it works, don’t touch it,” which in practice meant never spending on maintenance. No upgrades, no refactors, no documentation worth the name, no time budgeted for keeping the thing healthy. Every hour that would have gone into cleanup went into the next feature instead. For a long time, that was the right mantra.
Then the original developers started retiring. One by one, the people who actually knew how the thing was wired up walked out with a cake and a card, and what they knew walked out with them. Nobody had written much of it down, because for most of the app’s life nobody had needed to.
In the meantime, the app hadn’t stood still. Features had been bolted on whenever the business asked for them, always under time pressure, always with “we’ll clean it up later.” Dependencies were years behind. Refactors never happened. Whatever code quality the original team had insisted on quietly eroded, one shortcut at a time, until the codebase was a museum of decisions nobody present had made.
By the time I was called in, the cost of touching the app had outgrown the cost of replacing it. The next two contractors they’d approached had quoted three times their “modern stack” rate because they didn’t want to touch it. Recruiters stopped returning calls when the job spec mentioned the .NET version. The codebase wasn’t broken. The market around it was, and the only people who could navigate it had retired.
The rewrite brought the app onto a stack the firm could actually hire for. We stayed on .NET, just on a current version, and took the chance to untangle everything that had accumulated around it. The business logic was mostly fine, it was the layers around it that were the problem. We carried the rules forward and rebuilt the plumbing underneath, workflow by workflow, until nothing was left of the old one.
Three interest shapes, paid all at once. Hiring tax was the one the firm noticed first, because it showed up in quotes. Maintainability debt was the reason every change was a risk, and the reason nothing had been changed in years. Security debt sat quietly underneath, because an unpatched .NET Framework with years of un-reviewed dependencies is a CVE buffet nobody wanted to inventory. The “free” choice (don’t touch it) had been costing them for years, but in pieces small enough that nobody ended up adding them up.
“If it works, don’t touch it” is a strategy with a hidden expiry date. The expiry isn’t when it stops working. It’s when the cost of keeping it working passes the cost of replacing it.
A startup picked Supabase early. Sensible call. Cheap in two senses at once. Cheap in engineering time, because auth, database, storage, and real-time all came as one thing they didn’t have to build. And cheap on the bill, because the pricing looked very affordable at their stage of growth. They moved fast. They shipped. The product worked.
Two things broke at the same time. Supabase had reworked its pricing model a few months earlier. Nobody on the team had pulled up the new page and done the math against their actual usage until the bill arrived with the update applied. By then it was twice the budget (in my opinion, already generous). At the same time, the parts of the stack that had been “good enough by default” started to hurt. The rules deciding which user could see which data had grown into a tangle nobody on the team could fully audit anymore. Background jobs that used to run fine started hitting vendor-imposed limits in production, with no way to raise them without jumping to a much more expensive tier. And the backup setup, which technically worked, didn’t give them the guarantees their larger customers were starting to ask for in contracts.
The board started asking about cost predictability. Engineering wanted more control. Both were asking the same question in a different language. Can we leave?
The migration wasn’t a “just rewrite it” job. A lot of the work was thinking, not typing. The appeal of Supabase had always been that it felt like one cohesive product, a single thing you could point at and understand. Leaving meant taking that single thing apart into the smaller pieces it was actually made of, and picking a replacement for each one without the rest of the app falling over in the meantime. The team had also been leaning hard on AI to move fast, with not much guidance on structure, so the code we were untangling was messier than its age would suggest. It took months. The old and new systems ran side by side the whole way through, until the last piece of the app moved over.
The interest was paid in lock-in tax, mostly, with a side helping of cost drift as the bill kept climbing during the migration. The original “saving” (skip building auth, skip building real-time, skip building storage) was real and useful. It bought speed when speed was what the company needed. The bill came due as soon as the company needed something else. Predictable cost. Deeper control. A compliance fit that one vendor’s defaults didn’t cover.
Backend-as-a-service doesn’t lock you in by accident. It locks you in by design. That’s not a bug. It’s the business model. Plan for the exit before you sign the lease.
None of the three clients came to me saying “Code is unmaintainable” or “I’m vendor locked” They came with symptoms. The diagnosis came after. So this section is the symptoms, told the way you’d actually notice them, with the shape they map to.
Two is worth a review. Three is worth a plan.
Most readers who recognised themselves in the previous section are not going to rewrite next quarter. They’re going to live with the loan for a while longer. This section is for that interval.
You can’t always avoid the loan. You can almost always avoid the surprise.
The real price of software is rarely on the first invoice. Some of it shows up on later bills, in lines you don’t notice until you add them up. The rest never reaches an invoice at all. It’s the hours your team spends working around a system they can’t change, the knowledge walking out of the building when a key person leaves, the hiring conversations that don’t go anywhere, the answers you can’t give compliance with a straight face. By the time you see the full price, you’ve been paying it for years.
Every business in those three stories made a defensible choice with the information they had. The damage came from having no plan for the day the real price showed up. Planning costs little. Discovering it the hard way costs much more.
If you’re reading this and thinking “that’s us”, you already know what the next step is. Talk to someone before the bill arrives, not after.