Skip to main content

Salesforce Sync vs. Async Apex: How to Choose the Right Path

๐Ÿ’ฌ In plain words: Synchronous (Sync) means the user waits for the process to finish, and you are constrained by tight platform limits. Asynchronous (Async) means the work happens later in the background, giving you much higher limits. Move work to async when it is slow (like API callouts or heavy calculations) or involves massive amounts of data. However, remember that async means "eventually." Do not use it if the user needs to see the result immediately on their screen.
๐Ÿ“Œ Real-Life Example: A user clicks "Save" on an order. You need to update the Salesforce record NOW (sync). But do you also need to notify your SAP system and recalculate the customer's yearly totals instantly? No. The user shouldn't have to stare at a loading spinner waiting for SAP to respond. Publish an event and queue the recalculation in the background. Think of sync as the fast lane for the user, and async as the freight lane for the heavy lifting.
๐Ÿง  Who waits? If the user must see the result instantly, use Sync. If it can happen a minute later with bigger limits, use Async.

The Core Concept

Synchronous Apex executes immediately and shares the user's active transaction. Because of this, it has strict boundaries:

  • Tight Limits: You get a 10-second CPU limit and 100 SOQL queries.
  • User Experience: The user physically waits for the transaction to finish. If the code fails, their save is rolled back.

Asynchronous Apex buys you three major advantages: bigger limits, isolation from the user's transaction, and the ability to make external callouts after performing DML (database updates). This category includes Queueable, Batch, Scheduled, and @future methods, as well as Platform Events.

However, Async comes with trade-offs. You pay for those higher limits with eventual consistency, harder error tracking, and increased monitoring overhead. To make the right architectural choice, ask yourself four questions:

  • Does the user absolutely need the result right now?
  • Does the operation exceed synchronous governor limits?
  • Does the process mix API callouts with DML operations?
  • Can the business tolerate the work finishing a few moments later?
๐Ÿงญ 360 Card — Sync vs. Async
  • Rule: If the user must see the result instantly, run it sync. If it can wait a minute and needs bigger limits, run it async.
  • Gain: Async doubles (or more) almost every limit. It also removes slow, heavy processing from the user’s critical path.
  • Reach For:
    - Stay sync while the user needs immediate feedback.
    - Move to Queueable when work can be deferred or requires a callout after DML.
    - Move to Batch when massive row counts are the primary issue.
    - Move to Scheduled when a clock (not a user event) should trigger the job.
  • Price: The user is gone if an async job fails. You must design explicit failure, logging, and retry mechanisms.
  • Limits: Sync gets 10s CPU, 100 SOQL, and 6MB heap. Async gets 60s CPU, 200 SOQL, and 12MB heap. (Remember: Callouts after DML are strictly blocked in sync transactions).
  • At Volume: Async is not infinite. You are limited to 50 enqueues per transaction, 5 concurrent batches, and a queue that can back up during heavy loads.

Core Q&A

Q: A save operation must update the record, notify an external system, and recalculate a heavy aggregate. How do you design the transaction boundaries?
๐ŸŽฏ Say this first: "Update the record synchronously. Notify the external system using a Platform Event or a Queueable callout. Push the heavy aggregate to asynchronous processing using Queueable or Batch."

A: Keep the initial record update synchronous because that fulfills the user's immediate intent. The external notification cannot happen in the same transaction after the database update (you will hit the dreaded "Callout after DML" error). Instead, publish a Platform Event or enqueue a Queueable class to handle the callout with built-in retry logic. The integration becomes "eventually consistent" by design.

For the heavy aggregate, you have a judgment call to make. If it is mathematically simple, run it synchronously. If there is any risk of hitting CPU or SOQL limits at scale, push it to async. Pro tip: Always verbally acknowledge the trade-off. Mentioning the brief "staleness window" (where users might see old aggregate data before the async job finishes) is what separates senior architects from standard coders.

Scenario-Based Follow-Ups

Q1: How do you handle and surface async failures, given the user is no longer on the screen?

A1: You must design an explicit failure path.

  • Wrap the logic inside your Queueable or Batch job in a try/catch block that writes errors to a custom Log object.
  • Better yet, utilize Transaction Finalizers. Finalizers execute even if an uncatchable limit exception completely kills the async job.
  • Flag failed records for reprocessing (dead-lettering) and display failure counts on an admin dashboard with automated alerts.
⚠ Anti-Pattern Trap: Avoid "catch-and-swallow" logic in asynchronous code. Running background jobs without a visible error surface is essentially scheduling data loss on a timer. The standard AsyncApexJob table shows platform status, but gives zero business context.
Q2: Product Managers want everything to happen "instantly." How do you convince them to use async?

A2: Reframe the definition of "instant." The user-visible part—the click, the save, and the success confirmation—remains instant precisely because you moved the heavy lifting off their critical path.

If you keep a 3-second API callout synchronous, every user save is held hostage by the remote system's latency and outages. Explain the failure math: a synchronous integration couples your system's uptime directly to theirs. Async processing decouples them. Offer a UI compromise: build an optimistic UI that shows "Status: Sending..." and use Platform Events to flip the status to "Complete" the moment the async job lands.

Q3: When is Batch Apex the wrong tool, even if the data volume is large?

A3: When per-record latency actually matters. Batch is like a freight train—it can haul a massive amount of data, but it takes time to start up and has to wait for an available track slot.

  • If records must be processed within seconds of arriving, chained Queueable jobs or event-driven architectures are a much better fit.
  • There is also a lower limit to consider. If you are processing fewer than 50,000 rows, a Queueable is usually much simpler to write and deploys immediately, avoiding the heavy Start/Execute/Finish overhead of Batch Apex.