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
SOQLqueries. - 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
DMLoperations? - Can the business tolerate the work finishing a few moments later?
- 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 toQueueablewhen work can be deferred or requires a callout after DML.
- Move toBatchwhen massive row counts are the primary issue.
- Move toScheduledwhen 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
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
A1: You must design an explicit failure path.
- Wrap the logic inside your
QueueableorBatchjob 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.
AsyncApexJob table shows platform status, but gives zero business context.
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.
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
Queueablejobs 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
Queueableis usually much simpler to write and deploys immediately, avoiding the heavy Start/Execute/Finish overhead of Batch Apex.