Software does not usually fail at launch. Launch is the moment everyone is paying attention. It fails somewhere around the third year, and it fails for reasons that were built in on the first day.
The mechanism
Every system encodes assumptions. Some are explicit — a price is in pounds, a booking has one lead passenger, a customer belongs to one account. Most are not. They are the shape of a database table, a validation rule, a foreign key that quietly asserts a relationship can only go one way.
On day one these assumptions are correct, because they were derived from how the business worked on day one. The business then changes. It adds a product line, or starts selling in a second currency, or acquires something. Each change meets the assumptions, and each time the cheapest available response is to work around rather than through.
What the workaround looks like
A nullable column added because the new product does not have that field. A boolean flag that means one thing for old records and another for new ones. A scheduled job that patches up data at 3am because two systems disagree. None of these is unreasonable on its own. Each is a Tuesday afternoon decision under deadline.
By year three there are forty of them, they interact, and the person who understands why the 3am job exists has left. Now every change carries risk that nobody can quantify, so changes slow down, so more workarounds accumulate, because a workaround is the only thing that feels safe.
That is the failure. Not a crash — a system that can no longer be changed at the speed the business needs.
What actually prevents it
Write the assumptions down
Most of them never get stated, which is why they never get revisited. A short document listing what the system assumes to be true — one lead passenger per booking, one currency per order, prices never negative — costs an afternoon and gets read every time somebody proposes a change that violates one.
Model the domain, not the current screen
The most expensive decisions are in the data model, because they are the hardest to reverse. A booking that can have multiple passengers, from the start, costs almost nothing extra on day one and saves a migration in year two. This is judgement rather than a rule: modelling for every conceivable future is its own failure. But a handful of dimensions — quantity, currency, time, ownership — are worth getting right early because they are the ones that always change.
Have tests that describe behaviour
Not for coverage. For permission. A test suite that captures what the system is supposed to do is what makes a developer in year three willing to change something, because they will find out immediately if they broke it. Without that, the rational response to unfamiliar code is to leave it alone and work around it — which is exactly the behaviour that produces the problem.
Make the handover real
A README that gets the system running locally in under an hour. An architecture note explaining why, not what. A list of the things that will bite you. If a competent developer cannot become productive in a week, you do not own your software in any practical sense, regardless of what the contract says.
The honest version
None of this makes software permanent. Systems have lifespans and that is fine. What it does is change the failure from sudden and expensive to gradual and visible — you can see it coming, plan the work, and decide when to spend the money.
That is the whole difference between a system that gets replaced on your schedule and one that gets replaced on its own.