Moving work to a queue improves response time, but does not automatically make execution reliable. Email, shipment creation and exports introduce failure boundaries: workers can start before a transaction commits, crash after a side effect, or repeat a request that already succeeded remotely.
Give each job one clear responsibility
Pass stable identifiers rather than a whole HTTP request or an oversized object graph. Include tenant context and an operation identifier when needed. Reload current state before executing. A cancelled export should not consume substantial resources simply because its job was already queued.
Dispatch after the transaction commits
A worker can otherwise read a record before its creating transaction becomes visible. Laravel supports after_commit configuration and per-job afterCommit dispatch. This addresses timing, not distributed atomicity between a database and an external broker. A durable outbox may still be appropriate for operations that cannot tolerate a lost handoff.
DB::transaction(function () use ($order) {
$order->update(['status' => 'confirmed']);
SendOrderConfirmation::dispatch($order->id)->afterCommit();
});Make repeated execution safe
Suppose a carrier creates a shipment, but the response connection fails. Retrying without a stable key may create a second shipment. Persist an operation identifier and use the provider’s idempotency mechanism where available. Otherwise reconcile by reference or route uncertain outcomes to review. A unique-job lock reduces certain duplicate dispatches; it does not replace business-level idempotency.
Coordinate timeouts and retries
Align worker execution limits, message redelivery timing and outbound request timeouts. A message that becomes available while its original worker is still running can execute concurrently. Use bounded backoff for transient failures. Invalid input needs correction, not endless retries.
Bound large exports
Read stable batches instead of loading every row into memory. Define whether the export represents a snapshot or data as it is read; records may change during processing. Track progress, publish the link only after completion and enforce tenant authorization at download time.
Include operations in completion criteria
Observe waiting time, execution duration, oldest pending work and failures. Correlate job identifiers with the originating operation. Ensure deployment restarts long-running workers appropriately. A job is ready when retries, crashes and releases preserve the user’s intended result, not merely when one happy-path run succeeds.
Scenario: a resumable large export
Split an export into batches belonging to one operation and persist a stable cursor or batch range. Do not expose the final output while some pieces remain incomplete. Write intermediate parts separately and publish the result after verifying completeness. Re-running a batch should replace or recognize its own part, rather than appending duplicate rows to a shared file.
Resume or restart?
Resuming is useful when batch boundaries remain stable and verifiable. If report criteria or ordering change, a new run may be clearer than mixing two versions of a result. Save the operation’s configuration at creation time and decide how concurrent record changes are handled. Test a crash between writing a part and saving progress; that boundary often exposes incorrect completion assumptions.
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




