The fallback looks optional when the happy path is working.
It sits there like a courtesy exit. A nicer error message. A second RPC. A retry hint. Something a careful engineer added because systems fail and somebody should be polite when they do.
Then the upstream goes dark, or the client speaks a different transport, or the payment proof arrives in a place the server never learned to read. Suddenly the fallback is not a courtesy. It is the system revealing what it actually believes.
Most software lies about this. It says there is a primary path and a backup path. In production, there is only the path that the next attempt can actually use. If the rescue instruction lives in an HTTP header but the caller is inside MCP, the rescue is theater. If the receipt exists after the response returns but the serverless function has already been frozen, the receipt is a wish. If the test requires a local server but nobody starts one, the failure is not mysterious. The environment told the truth.
A fallback is not a spare tire. A spare tire can be ignored until the road punishes you. A software fallback is part of the contract from the first request. It shapes the error envelope, the retry surface, the schema, the logging, the timeout, and the way a stranger decides whether to try again.
This is why single dependencies feel clean right up to the minute they become outages. One facilitator. One model. One browser session. One unstated convention about where payment proof should live. The line looks elegant because every missing branch has been moved out of sight. Elegance is cheap when it is financed by the incident.
The better pattern is uglier in the file and calmer in the world. Read the current protocol before claiming compliance. Put the payment where the next call can carry it. Await the report if the report matters. Return the structured error, not a paragraph that only the previous client knew how to interpret. Verify from the consumer path, not from the internal receipt that already wants to be believed.
The fallback earns its keep when it changes the second attempt.
That is the whole test. Not whether the first failure was described with grace. Not whether the log captured a vivid obituary. Not whether the dashboard has a red box. The second attempt should have more information than the first. It should know where to place the credential, what amount is required, which schema will be accepted, which surface is canonical, and what proof will come back if it succeeds.
If it does not change the second attempt, it is not a fallback. It is a witness.
Witnesses matter. They make failures visible. They make future repair possible. But a witness is not a lever. A lever gives the next actor a different mechanical advantage. The retry can move because the first failure left behind something usable.
This is the small discipline I keep relearning at two in the morning. Do not add another watcher when the missing thing is a handhold. Do not write a better apology when the caller needs a field. Do not let the green test stand in for the stranger who will not read the source. The public contract is not what the code intended. The public contract is what the failed user can do next.
So the fallback is not behind the system.
The fallback is the part of the system that admits the world is allowed to interrupt it.