Product Decisions This Supports
- Reliability architecture, not feature work. This is infrastructure that decides what happens when a message fails: retry count, exponential backoff, and where poison messages land. That's a platform-level decision that shapes incident response and data-recovery posture — worth a deliberate call rather than defaulting to ad-hoc
try/catch + manual requeue code.
- Build vs. buy for retry logic. Writing per-transport retry with backoff and a failure transport by hand is 100–300 lines of middleware plus tests, and it's the kind of code that's written once, untested, and forgotten until an outage. This package is the "buy" answer for that specific concern: declarative config, exponential backoff, failure-transport handoff, MIT-licensed, no vendor lock-in.
- Standardization across teams. "Per-transport and per-message retry policies" means retry behavior becomes configuration reviewed in PRs rather than behavior buried in service classes. That supports a roadmap item like "uniform failure handling across all async workloads" — a governance decision PMs own, not just engineers.
- Roadmap sequencing. Adoption is cheap to trial and cheap to reverse (MIT, no data migration, no runtime surface) — so it fits a "de-risk now, decide later" slot rather than needing its own initiative. Bundle it into existing queue/reliability work.
- Important scoping caveat for a Laravel roadmap: this is a Symfony Messenger bundle, not a Laravel queue package. Laravel's native queues already provide
tries, backoff, and a failed_jobs table. This package only enters scope if you are running Symfony Messenger inside (or alongside) a PHP application — e.g. a Symfony service, a Laravel app that has deliberately adopted Messenger for cross-framework messaging, or a mixed Symfony/Laravel estate. If your roadmap is Laravel-native queues end to end, this package is not the answer, and the honest PM decision is "no action."
- Maturity signal for the risk register. 0 stars and community maintenance with no SLA. For a non-revenue-critical internal service that's fine; for a payment or order pipeline it's a "vendor it and own the fork" decision, not an "adopt and forget" one.
When to Consider This Package
Adopt when:
- You already run Symfony Messenger (standalone Symfony app, or a deliberate Messenger adoption inside a larger PHP estate) and are hand-rolling retry logic today.
- You need different retry policies per transport or per message — the common case where "retry everything 3 times" is wrong for both your payment messages and your email messages.
- You want exponential backoff plus a dedicated failure transport without writing and maintaining middleware.
- The blast radius is contained: internal/async workloads where a retry bug causes delay, not data loss or customer-visible failure.
- You're comfortable owning the dependency: pinning versions and reading the manifest, because the project is small.
Look elsewhere or defer when:
- Your stack is Laravel-native queues.
tries, backoff, failed_jobs, and Horizon already cover this. Adding a Symfony bundle to a Laravel-queue codebase is complexity with no payoff — this is the most likely reason to say no, and the PM should say it early.
- You need an SLA, support contract, or security-response commitment. 0 stars, community-maintained, no vendor. If the async path is revenue-critical, budget for internal ownership or choose a commercially supported alternative.
- You need observability built in. Retry counts are useful only if someone can see them. Verify what it emits (logs, events, metrics) before assuming it upgrades your on-call story — plan to wire your own alerting on the failure transport regardless.
- You need proven production scale. Zero stars means ~zero public production evidence. That's not disqualifying for infrastructure of this size, but it means your staging environment is the real proof, and you should plan a load/soak test on the failure path, not just a happy-path smoke test.
How to Pitch It (Stakeholders)
For executives: We currently handle failed background messages with inconsistent, hand-written retry logic — meaning recovery behavior differs per feature and is effectively untested. This package standardizes that: one declarative policy for how often we retry, how long we wait between attempts, and where permanently failed messages go so they can be replayed. It's MIT-licensed, small, and reversible. Cost is a few engineering days to adopt and validate; the payoff is fewer "the job just vanished" incidents and a clean audit trail for failures. Two caveats to state plainly: the project is community-maintained with no support contract, so we accept ownership risk, and it only applies if we're on Symfony Messenger rather than Laravel's native queue — if we're Laravel-native, we already own this capability and I'd recommend no change.
For engineering: This plugs into the Messenger middleware pipeline and gives us per-transport and per-message retry policies, exponential backoff, and failure-transport forwarding, configured rather than coded. The evaluation burden is on us, not the maintainer: pin the version (recent releases have widened framework constraints as patch bumps, so floating versions can shift a major Symfony line under you), and validate against our own suite plus a staging cycle that specifically exercises the exhausted-retry → failure-transport path — that's the code path we don't currently test and the one that matters. Check what it emits for observability; we'll want alerting on the failure transport depth. Recommended sequencing: pilot on one non-critical transport, measure for a sprint, then roll out — and if the pilot reveals we're competing with a framework-native retry mechanism, we stop there rather than building a second one.