Skip to main content
Laravel, shipping fast.

Chapter 16

Work That Survives the Network

Julian Beaujardin

Every chapter before this one built one API well. Real systems don't stop at one. Webplo runs api-server as the hub in front of a dozen leaf services: a git host, a hosting provisioner, a CDN, an AI content generator, a license service, a billing processor. A single merchant action, someone clicking "create my site", doesn't touch one of them. It touches most of them, in order, over a network that drops calls, kills worker processes mid-request, and never asks permission before doing either.

This chapter covers what changes when "the request" becomes "a workflow spanning a dozen services": chaining multi-step work so a crash halfway through is recoverable instead of catastrophic, naming the resources that workflow creates so a retry converges on one instead of leaking another, sweeping for the work that gets stuck without ever raising an exception, and putting the one piece of resilience every integration needs, retry and backoff, in exactly one place instead of reinventing it leaf by leaf.

A workflow that only finishes when nothing fails isn't a workflow. It's an incident waiting for its second attempt.

One request, a dozen services, none of them promise "now"

Chapter 4 already put a number on this: a single merchant action can touch three services before it's done, merchants calling api-server, api-server calling api-license, api-license calling Statamic. Provisioning a new site is the same shape, stretched further: create a repository, stand up hosting, configure a CDN tenant, generate content, deploy, and only then tell the merchant it's done.

merchant asks api-server to provision a site
create the repository
in parallel: CDN tenant · content generation · hosting setup
deploy
mark provisioning complete

Chapter 6 already showed you the code for this, one Bus::chain() with a Bus::batch() in the middle for the parallel steps. What that chapter didn't dwell on is what each step actually is. RepositoryCreateJob isn't business logic that happens to run on a queue. It's a thin wrapper around an HTTP call to a service that isn't api-server, doesn't share its database, and can fail in ways api-server's own test suite has never seen. Bus::chain() guarantees the steps run in order. It says nothing about what happens when step three, retried because a worker died mid-attempt, calls out to a leaf service for the second time.

That's the actual subject of this chapter. Not "how do you queue work", Chapter 6 answered that. This is "how do you queue work that reaches outside your own process, and survive the fact that the thing on the other end doesn't know or care that you're retrying."