Http::retry, and the backoff that is one argument

Retrying a flaky upstream is a loop, a sleep, an attempt counter and a decision about which failures are retryable — four things that get written slightly differently every time.

$response = Http::retry(3, 100)
    ->timeout(3)
    ->connectTimeout(1)
    ->get('https://pricing.internal/quotes/FR-100');

// retry(times, sleepMilliseconds) — and the callback form,
// which is the one that matters:
Http::retry(3, 100, fn($e) => $e instanceof ConnectionException);

The default retries on any request exception including a 500, which is usually right and is wrong for a 422 — retrying a validation failure three times is three identical failures and a slower error. The callback form is what makes the policy explicit. The separate connect timeout matters as much as the read timeout: an unreachable host should fail in a second rather than at the full read timeout, and the two are frequently set as if they were one.