Every business owner who has commissioned custom software has a story that starts calm and ends badly. Software project failure stories are usually treated as bad luck, a vendor who happened to be dishonest, a team that happened to be slow. In fifteen years building and rescuing systems, I have found the opposite is true: nearly every horror story traces back to one specific, preventable decision made early and ignored until it was too late to fix cheaply.

It is Halloween week, so here are three anonymized cases from the field, each with the one habit that would have prevented it.

The vendor who vanished with the source code

A trading company (not named here, details altered to protect confidentiality) hired a small development shop to build their inventory and order management system. The build took eight months, cost was reasonable, and the system worked well enough at launch. Eighteen months later, the company wanted to add a feature. They called the vendor. The number was disconnected. The email bounced. The vendor had closed, quietly, with no notice.

The code existed only on the vendor's own machines and a private repository the company never had access to. No source code escrow clause, no code handover at any point during the relationship, nothing. The company was left with a working system they could not modify, could not patch, and could not migrate without a full rebuild from scratch, which they eventually paid for, at nearly double the original cost, because now it needed to replicate a live production system's exact behavior with no documentation.

The lesson: get write access to a shared, access-logged repository from day one of the build, not at project end. This is not a trust issue with the vendor, it is a continuity issue that has nothing to do with the vendor's honesty. I go through this and other contract protections in negotiating software contracts.

The rewrite that never ended

A logistics company decided their aging system needed a full rewrite. Reasonable decision, the old system genuinely was creaking. The rewrite was scoped as "let's redo it properly this time", with no fixed deadline, no phased releases, and a growing wishlist of new features layered onto the rewrite as it progressed, because "since we're rebuilding anyway."

Two years in, nothing had shipped. The original system was still running, patched further past its intended life, while the new one grew in scope every quarter as stakeholders kept adding requirements to a project with no finish line to defend against. The team building it burned out and half of them left. The rewrite was eventually shipped, three years after starting, at a cost nobody had originally budgeted for, delivering roughly what a well-scoped six-month project could have delivered if it had shipped in phases.

The lesson: a rewrite without a fixed, narrow first release is not a project, it is an open-ended commitment with no natural end. Ship the smallest version that replaces the riskiest part of the old system first, get it live, then iterate. Anything scoped as "let's do it properly" with no deadline is a scope-creep magnet waiting to happen.

The go-live that deleted production data

A retail chain migrating to a new point-of-sale and inventory system scheduled their go-live for a weekend, reasonable, low traffic. The migration script that moved historical transaction data from the old system to the new one had a bug: instead of copying records, under certain conditions it deleted the source records before confirming the copy succeeded. Nobody had tested the migration against a full copy of production data, only a small sample set that never triggered the edge case.

The bug triggered mid-migration. Several months of transaction history vanished, unrecoverable, because there was no fresh backup taken immediately before the migration ran (the last backup was from four days prior, per the usual schedule, which nobody had thought to override for a high-risk event). The store spent the following month reconstructing partial records from paper receipts and bank statements.

The lesson: any migration or go-live that touches production data needs a fresh, verified backup taken immediately before the operation, tested on a full-size copy of real data (not a small clean sample), with a clear, rehearsed rollback plan. "We have backups" only counts if the backup is recent and the restore has actually been tested, not just assumed to work.

The pattern behind all three

Each story looks different on the surface (legal, scope, technical) but shares one root cause: a risk that was visible and cheap to address early became invisible and expensive to address once the project was already moving. Nobody in any of these stories was incompetent. They were busy, optimistic, and trusted that the obvious failure mode wouldn't be the one that happened to them.

If you are starting a software project now, run this five-minute exercise before kickoff: list the three worst things that could go wrong (vendor disappears, project never ends, data gets destroyed), and for each one, name the single cheap thing that prevents it. It is almost always cheaper than the story above. For a broader look at reducing this kind of risk before it starts, see why your business needs a technology strategy.

Takeaway

Software project failure stories are entertaining because they are dramatic, but the prevention is boring on purpose: get code access early, ship in small phases with real deadlines, and take a fresh, tested backup before anything touches production data. None of these cost much. All three horror stories above cost their companies months and, in two cases, real money that a boring habit would have avoided entirely.