Skip to main content
Laravel, thinking fast.
Chapter 15 · Failure Is a Feature

Refuse to Start Work You Cannot Finish

Julian Beaujardin

With capabilities named, the gate is obvious: check before beginning, not half-way through.

Provisioning a site is a long chain — generate content, create a repository, provision a server, deploy, verify. If the model provider is down, that chain fails at step one, having already consumed a slot, created a record, and given the user a progress bar that is about to die.

The right behaviour is to decline at the front door: "Site creation is temporarily unavailable. Please try again in a moment."

This is Chapter 6's never start work you cannot finish, raised from a single call to a whole flow. A clear refusal is a better product than an ambitious failure — it costs the user ten seconds instead of five minutes, and leaves nothing behind to clean up.

Stop Asking a Service That Is Down

Chapter 1 established which failures are worth retrying: connection errors, 5xx, and 429. That is the right policy for one call. It is the wrong policy for the thousandth call in a row while a vendor is out.

Retries against a service that is genuinely down are worse than useless. They multiply load on something already struggling, they turn a fast failure into a slow one — three attempts with backoff before the same answer — and they hold your workers for the duration.

A breaker fixes this by remembering. After a threshold of failures it stops making the call at all and fails immediately; after a cooling period it lets one through to see whether the world has changed.

The important consequence is not the saved requests. It is that failure becomes fast, which means the queue keeps moving, workers are not held, and the user gets an answer in milliseconds instead of a minute of dead air.

Say What Is Wrong Without Naming the Vendor

// always in Webplo's own terms, never the underlying vendor

Never "OpenAI is down" or "Stripe returned a 503."

Partly because it is unhelpful — a merchant has no relationship with your vendors and cannot act on their names. Mostly because it is an architecture disclosure, permanently public, that tells anyone reading exactly what to probe and what your product is assembled from. And it ages badly: swap the vendor and years of error messages, screenshots and support articles are wrong.

Say what the user cannot do, and when to come back: "New sites can't be created right now. Everything already published is unaffected — try again in a few minutes." Precise about scope, honest about impact, silent about plumbing.

What This Chapter Argued

  • Name your dependencies as an enum, so health is a fact you hold rather than a guess from a 502.
  • Name capabilities separately, in product terms, and map them to dependencies with one match.
  • A capability needs every dependency healthy, and the map answers "what can customers still do?" without a meeting.
  • Gate the whole flow at the front door. A clear refusal beats an ambitious failure that leaves state behind.
  • Add a breaker on top of your retry policy. Its value is fast failure, not saved requests.
  • Never name the vendor to the user. It is unactionable, it is an architecture disclosure, and it ages badly.