Laravel's queue system changed how I think about time
·2 min read ·Laravel · Backend · Performance
The first time I sent a welcome email synchronously from a controller, everything seemed fine. Then on a slow SMTP day, our registration endpoint took 4 seconds to respond. That's when I understood what queues are actually for.
The Synchronous Trap
By default, we think about web requests synchronously: user submits form, app does stuff, user sees result. Everything in the 'does stuff' phase blocks the user from seeing anything.
But most of that stuff doesn't need to happen before the response. Sending a welcome email? The user doesn't need to wait for that. Notifying analytics? Background. Resizing an uploaded image? Definitely background.
The Queue Mental Model
Queues let you say: 'dispatch this job and move on.' The job runs on a worker process, not the web process.
// Before: blocks the response
Mail::to($user)->send(new WelcomeEmail($user));
// After: dispatches to queue, responds immediately
SendWelcomeEmail::dispatch($user);
The user gets a response in 50ms. The email goes out a second later. Nobody notices the difference, and your app is faster.
Failed Jobs: The Part You Can't Skip
Queues introduce a new failure mode: silent failures. The job fails, the user doesn't see an error, and if you're not watching, you don't know.
class SendWelcomeEmail implements ShouldQueue
{
public int $tries = 3;
public int $backoff = 60;
public function failed(Throwable $exception): void
{
Log::error('Welcome email failed', ['user' => $this->user->id]);
}
}
Set retry counts. Set backoff delays. Handle the failed case. Laravel Horizon gives you a dashboard to monitor all of this.
Rethinking What's Synchronous
After working with queues for a while, the question I ask for every operation is: 'does the user need this to happen before they see a response?' Most of the time the answer is no. That shift in thinking makes everything faster.