12 min read

The hidden cost of "cheap" development: case studies of rewrites I've been hired to do

Three real rewrite engagements, what the original 'cheap' choice actually cost, and how to spot the same loan in your own business before it defaults.

Share

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.

Cheap is a loan, not a discount

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.

  • Vendor risk. The people you bought from might not be there in two years.
  • Lock-in tax. Leaving costs more than staying. Until staying costs more than leaving.
  • Cost drift. What you pay creeps up over time, often in ways the original quote didn’t hint at.
  • Hiring tax. Nobody wants to touch your codebase.
  • Security debt. Un-patched dependencies, lapsed credentials, no plan for the next critical CVE. Data breaches and compliance fines don’t show up on the original quote.
  • Maintainability debt. No tests, no docs, no observability. Every change can lead to an outage.

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.

Cost over time

Cheap software is a loan you didn’t know you took out.

Three rewrites, three loans

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.

Case 1. The agency that disappeared

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.

Case 2. The .NET codebase nobody wanted to touch

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.

Case 3. Escaping Supabase

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.

Diagnostic signals you’re holding one

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.

  1. You can name only one person who understands the system. And they don’t work for you full time. Or they’re about to retire. Or you’re paying them by the hour to remain reachable. (Maintainability debt, hiring tax.)
  2. Your last security update was a long time ago, or you don’t know when it was. No patching schedule. No dependency auditing. No plan for the next critical CVE. (Security debt.)
  3. You’ve never tested what happens if your main vendor goes away. No exported backup of your own data. No second supplier on standby. No inventory of what’s hosted where, under whose account. (Vendor risk.)
  4. Hiring for the stack feels harder every year. Recruiters quietly drop the role. Quotes from contractors are surprisingly high. Engineers you interview keep asking what’s on the migration roadmap. (Hiring tax.)
  5. Leaving would be a project, not a decision. You’ve thought about switching. You can’t, because the auth, the data model, or the integrations are wired into one vendor’s specific shape. (Lock-in tax.)
  6. A line item on your software budget has quietly become one of the biggest, and nobody can quite say why. Past choices keep the bill higher than it needs to be, even though usage hasn’t changed much. (Cost drift.)
  7. Every small change is a risk. No tests. No staging environment that mirrors production. No observability. Deploys happen at night, by hand, and somebody watches the logs. (Maintainability debt.)
  8. Your only “vendor relationship” is one phone number. One agency, one contractor, one inbox. No second supplier, no written handover, no way to keep running if that line goes quiet. (Vendor risk.)
  9. Compliance can’t get a clean answer from you. Where the data is, who can access it, when the last audit was, who has root. The answers are vague, or “I’d have to ask.” (Security debt, vendor risk.)

Two is worth a review. Three is worth a plan.

What to do if you’re already holding one

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.

  1. Inventory what you actually own. Before anything else, get a written list of every system, every account, every domain, every credential. Who controls each one. Where the data lives. Where the backups live, if they exist. You can’t plan an exit from a system you can’t describe. Most of the work in Case 1 was this, before any code got written.
  2. Get your data out, on a schedule. Even if you stay. Even if you love the vendor. A regular export of your own data into a format you control is the single cheapest insurance policy on this list. It’s also the one that would have saved Case 1 the most pain.
  3. Cost the loan, not the invoice. Add up everything you actually pay for the system, per year, including the parts that aren’t on the bill. Licenses. Hosting. The contractor you keep on retainer because nobody else will touch it. The hours your team spends on workarounds. That number is your real cost. Compare it to the cost of replacing the system, amortised over five years. Do it once a year.
  4. Stop adding to the loan. Don’t build new features on top of a system you’ve already decided to replace. Don’t sign multi year renewals on a vendor you’re trying to leave. The interest rate on the existing loan is bad enough. New principal makes it worse.
  5. Plan the exit before the crisis. The most expensive rewrites are the ones done under deadline pressure, after something already broke. The cheapest are the ones started while the old system still works. The window between “we know we’ll have to leave” and “we have to leave right now” is where the difference is.

You can’t always avoid the loan. You can almost always avoid the surprise.

The takeaway

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.