Product Decisions This Supports
- Build vs. buy for HTTP plumbing. The package answers a question PMs otherwise fund as internal work: "who owns retry logic, timeouts, base URIs, and TLS policy across every outbound integration?" It is ~one class with a static factory, so the buy case is strong — writing and maintaining an equivalent in-house wrapper is recurring maintenance with no product differentiation.
- Roadmap: integration velocity. Each new third-party endpoint currently requires hand-wiring a Guzzle client. Standardizing on one factory turns "add a new partner integration" from a bespoke setup task into a config-profile entry, which shortens delivery on roadmap items that depend on external APIs (payments, notifications, analytics, logistics, identity).
- Reliability as a product decision, not an engineering detail. Defaults include retries on HTTP 429 and configurable 4xx codes plus a minimum TLS 1.2. That translates into fewer transient-failure support tickets and a stronger security posture — both shippable as user-facing quality improvements, not just refactors.
- Risk and compliance decisions. A single centralized place to enforce TLS floors and timeout policy supports security review and audit responses; it also makes "do any of our clients disable certificate verification?" a queryable question rather than an archaeology project.
- Platform consistency / tech-debt paydown. Replaces duplicated, diverging client construction with one governed seam — directly improves time-to-onboard engineers and reduces the blast radius of future Guzzle upgrades.
- Explicitly not a decision this supports. It is not an HTTP abstraction layer and not a replacement for Laravel's
Http facade. Adopting it should not be framed as an architecture modernization initiative; the injected type remains a stock GuzzleHttp\Client, which is a feature (no lock-in) and also a limit (no new capability for users).
When to Consider This Package
Adopt when most of the following hold:
- Multiple outbound HTTP clients already exist, constructed ad hoc across controllers, jobs, commands, and service classes, with inconsistent timeouts, retry behavior, and base URIs.
- You are already on Guzzle 7.11+ and PHP 7.4–8.5, and no other dependency pins Guzzle lower.
- You want policy centralization without an abstraction layer — teams keep using the standard Guzzle client and existing tests/mocks continue to work.
- You need retry-on-429 and TLS 1.2 minimums as defaults and can accept them as a baseline policy.
- You can afford a thin internal seam (an
HttpClientFactory your code depends on, which calls the vendor factory). This is the adoption precondition that makes the rest low-risk.
- You are willing to pilot on non-critical integrations first rather than converting revenue-path clients in the first release.
Look elsewhere or defer when:
- Any integration requires TLS below 1.2 or a legacy partner endpoint that fails the stricter default — you would be immediately fighting the defaults.
- Non-idempotent traffic dominates (payment posts, webhooks). Automatic retries on 429/4xx can be unsafe; you need per-service retry budgets and possibly retries disabled for specific profiles, which is extra design work.
- You currently depend on Guzzle handler-stack internals. v8 removed direct handler-stack creation from the public API; this becomes a breaking migration, not a version bump, and should be sequenced separately from the first rollout.
- Your real need is a first-class HTTP abstraction (unified mocking, telemetry, circuit breaking as a product requirement) — this package is deliberately too thin, and a framework-level client or gateway library is the better decision.
- Supply-chain policy forbids single-maintainer dependencies on critical paths without commercial coverage. Active releases, changelog discipline, and a Tidelift support channel mitigate but do not eliminate this; treat as low-probability, medium-impact.
How to Pitch It (Stakeholders)
For executives — one paragraph. We can stop maintaining bespoke outbound HTTP plumbing by adopting a small, MIT-licensed, actively released factory that gives every external API call the same safe defaults: sane timeouts, automatic retry on rate limits, and a TLS 1.2 floor. Cost is roughly one to two days of engineering to install behind an internal wrapper, plus a few days to migrate call sites depending on inventory. Benefit is faster delivery of partner integrations, fewer transient-failure incidents, and a single auditable place where security policy is enforced. Risk is contained by that wrapper: if the package is abandoned or a major version misbehaves, we swap two lines rather than rewrite the codebase.
For engineering — the ask. Add graham-campbell/guzzle-factory:^8.0, introduce an internal HttpClientFactory seam that application code depends on (never the vendor static call directly), drive base URI, timeout, and retry budget from per-service config profiles, and add a snapshot test asserting the effective client configuration per profile. Sequence: audit existing clients → seam and config → pilot on 2–3 non-critical integrations → tiered rollout with revenue-path integrations last → CI check banning raw client construction and verify => false outside the seam. Keep the v8 handler-stack API migration out of the first production rollout.
Three things to decide before we start. (1) How many Guzzle clients exist today, and which disable certificate verification or carry hardcoded timeouts — this baseline is also the business case. (2) Whether any partner requires TLS below 1.2 or retries on POST, since those need explicit exceptions. (3) Who owns the internal seam after launch, and whether payment-path consumers require commercial support coverage.