Skip to content
ansezz.
← Back to blog
Architecture Jun 16, 2026 7 min read 1,212 words

Synchronous vs asynchronous communication

The technical differences between synchronous and asynchronous architectures in Laravel and Shopify, and how queues, jobs, and webhooks help you scale.

Anass Ez-zouaine

Backend · Architect · AI

▸ Share

Pop-art comic-style scene contrasting blocking and non-blocking request flows

Every developer has faced the dreaded “spinning wheel of death.” You click a button, the browser hangs, and you wait five seconds for a confirmation that never feels fast enough. This lag usually stems from a synchronous process holding the entire request-response cycle hostage while it waits for a third-party API or a heavy database query. In high-traffic environments, these blocking calls are not just a nuisance. They are a scalability killer that can bring down your entire infrastructure.

Architecting for scale requires a deep understanding of when to keep things synchronous and when to move them to the background. This decision impacts everything from user experience to server costs. By shifting heavy lifting to asynchronous workers, you decouple your system. This allows your application to handle thousands of concurrent users without breaking a sweat.

The blocking nature of synchronous communication

Synchronous communication is the traditional request-response model. When a client sends a request, it waits for the server to process the logic and return a response. This is a “blocking” operation. The execution thread is tied up until the task completes.

Think of it like a phone call. You dial a number, wait for the other person to pick up, and you cannot do anything else until the conversation is over. In web development, this is perfectly fine for simple operations like fetching a user profile or updating a single database row. The latency is minimal and the user needs the data immediately to continue.

However, synchronous flows become dangerous when you introduce external dependencies. If your Laravel controller calls the Shopify Admin API to update 500 products during a standard HTTP request, your PHP worker is stuck. If Shopify takes three seconds to respond, that worker cannot serve any other users for those three seconds. Under heavy load, your worker pool will exhaust itself. This leads to 504 Gateway Timeout errors and a broken experience.

The fire-and-forget power of asynchronous logic

Asynchronous communication decouples the request from the processing. The client sends a message or triggers an event and immediately receives a response. The actual work happens “out of band” in a separate process or at a later time.

This is more like sending an email. You hit send and immediately go back to your day. The recipient reads and processes the message whenever they are available. In a technical stack, this is achieved using message queues like Redis or Amazon SQS, or a dedicated broker such as RabbitMQ.

Laravel job queueing architecture showing dispatched jobs flowing through Redis to workers

When you move a task to an asynchronous worker, the user gets an instant “Success” message. The heavy processing happens in the background. This architecture is essential for:

  • Sending transactional emails.
  • Generating PDF reports or data exports.
  • Communicating with third-party APIs like Shopify or Stripe.
  • Running complex AI agents or RAG pipelines.

Laravel queues and background jobs

Laravel provides a robust implementation of asynchronous processing through its Queue system. Instead of performing a slow task in your controller, you dispatch a “Job.”

// Synchronous (Bad for slow tasks)
public function store(Request $request) {
    $order = Order::create($request->all());
    Mail::to($user)->send(new OrderConfirmed($order)); // Waits for SMTP
    return response()->json($order);
}

// Asynchronous (Scalable)
public function store(Request $request) {
    $order = Order::create($request->all());
    ProcessOrderEmail::dispatch($order); // Returns immediately
    return response()->json($order);
}

By using dispatch(), you push a payload into Redis. A separate process, managed by a tool like Laravel Horizon, picks up the job and executes it. This keeps your web-facing workers free to handle more incoming traffic. If the email server is down, the job stays in the queue and retries later. This level of resilience is impossible in a strictly synchronous world.

Shopify webhooks: the event-driven standard

For e-commerce developers, understanding asynchronous communication is mandatory when working with Shopify. Shopify uses webhooks to notify your application of events like orders/create or products/update.

Webhooks are fundamentally asynchronous. Shopify does not wait for your app to process the entire order. It sends the POST request and expects a 2xx response within five seconds. If your code tries to run complex inventory logic or sync data to an ERP synchronously within that webhook route, you risk timing out. Shopify then retries the failed delivery up to eight times over roughly four hours, which can mean duplicate processing if your handler is not idempotent. Worse, if your endpoint keeps failing, Shopify removes the webhook subscription entirely, and you stop receiving events until you re-register it.

Shopify webhook delivery feeding a SaaS dashboard integration

The best practice for agentic commerce on Shopify is to receive the webhook, validate the signature, dispatch a Laravel Job, and return the response immediately. This ensures your app stays responsive and Shopify remains happy with your delivery rates.

Infrastructure impact and DevOps considerations

Moving to an asynchronous architecture changes how you manage your infrastructure. In a synchronous world, you scale your web servers to handle peak traffic. In an asynchronous world, you scale your workers.

Tools like Coolify and Docker make it easier to manage these separate services. You can run your web container on one set of resources and your worker containers on another. If you have a massive spike in background jobs, you can spin up more workers without affecting the performance of your main website.

For cross-service communication, Google Cloud Tasks is a strong alternative to a self-hosted queue. It dispatches HTTP callbacks to any endpoint with built-in rate limiting, scheduling, and retries. This is particularly useful when building an API gateway for AI stacks, where different microservices need to talk to each other without blocking the main user flow.

When to choose sync vs async

Choosing the right pattern depends on the user’s expectations and the reliability of the task.

FeatureSynchronousAsynchronous
User ExperienceImmediate feedback required.Background processing is okay.
Data IntegrityResult needed for next step.Eventual consistency is okay.
ReliabilityFails if the connection breaks.Retries automatically on failure.
ComplexitySimple code structure.Requires queue management and workers.
Example Use CaseLogin validation, viewing a cart.Order fulfillment, image processing.

Use synchronous when:

  • The operation is extremely fast (under 100ms).
  • The user cannot proceed without the result of the operation.
  • You are performing a simple read/write to your primary database.

Use asynchronous when:

  • You are calling an external API.
  • The task takes more than 200ms to complete.
  • The task can fail and needs to be retried.
  • You are performing bulk operations on large datasets.

Takeaways

Technical architecture takeaways summarized as a neobrutalist checklist

Designing for high performance means moving away from a single-threaded mindset. By embracing asynchronous communication, you build systems that are more reliable and easier to scale.

  1. Identify bottlenecks: Use tools like Laravel Telescope or OpenTelemetry to find slow controller actions.
  2. Offload everything: If it doesn’t need to happen right now, push it to a queue.
  3. Handle webhooks fast: Always return a 200 response to Shopify immediately and process the logic later.
  4. Monitor your workers: Use dashboards like Laravel Horizon to track queue health and failure rates.
  5. Design for retries: Ensure your background jobs are idempotent, meaning they can run multiple times without causing side effects.

How are you currently handling long-running API tasks in your Laravel or Shopify application?

▸ Made it to the end? Send it around.

▸ Share

▸ Comments