Loading...

Async Apex Selection: Queueable, Batch, Future, or Scheduled

Salesforce ships four async Apex patterns. Each has a specific shape, a specific governor limit profile, and a specific failure mode. Choosing the wrong one produces code that works at low volume and fails at scale.

Async Apex Selection: Queueable, Batch, Future, or Scheduled

Salesforce ships four ways to run Apex outside the current transaction: Future methods, Queueable, Batch Apex, and Scheduled Apex. Each has a specific shape, a specific governor limit profile, and a specific failure mode. Engineers new to Salesforce often pick whichever pattern they encountered first. The result is code that "works" in a developer org with 10 records and falls over in production with thousands.

The selection question is not academic. The wrong choice locks the implementation into a pattern that has to be refactored later, often under time pressure when the failure mode emerges.

Future methods: the legacy default

@future is the oldest async pattern in Apex. The method is annotated, accepts only primitive types, and runs asynchronously some time after the calling transaction commits.

public class AccountService {
  @future
  public static void syncToExternal(Set<Id> accountIds) {
    // ... runs async ...
  }
}

When to use Future today: almost never for new code. It was the only option before Queueable shipped in 2015, so a lot of legacy code uses it. New implementations should prefer Queueable.

Why avoid Future:

  • Cannot accept sObject parameters. Only primitives, which forces ID lists and a re-query.
  • Cannot be chained or sequenced.
  • No way to query its execution status programmatically.
  • Limited to 50 calls per transaction.
  • No retry on failure.

The one case where Future still applies: synchronous callouts that must not block the current transaction, where the simpler pattern is acceptable. Even then, Queueable with the Database.AllowsCallouts interface is more flexible.

Queueable: the modern default

Queueable is the recommended async pattern for most new Apex work. The class implements Queueable and is enqueued with System.enqueueJob(...).

public class AccountSyncQueueable implements Queueable, Database.AllowsCallouts {
  private Set<Id> accountIds;

  public AccountSyncQueueable(Set<Id> ids) {
    this.accountIds = ids;
  }

  public void execute(QueueableContext qc) {
    // ... runs async ...
    // Can chain: System.enqueueJob(new NextStepQueueable(...));
  }
}

What Queueable does well:

  • Accepts complex types (sObjects, custom classes) via constructor.
  • Can chain itself or other Queueables for multi-step async workflows.
  • Job ID returned by enqueueJob is queryable via AsyncApexJob.
  • Supports callouts (with Database.AllowsCallouts).
  • Higher async governor limits than synchronous context.

When to use Queueable:

  • Any async work involving sObjects or complex types.
  • Sequential async steps where step N depends on step N-1.
  • Callouts that should not block the user's transaction.
  • Most "fire and continue" patterns.

What Queueable does not do well:

  • Volumes above 50,000 records in one job. Use Batch Apex instead.
  • Scheduled execution at fixed times. Use Scheduled Apex.

Batch Apex: high-volume work

Batch Apex processes large data sets by chunking. The class implements Database.Batchable<sObject> with three methods: start, execute, and finish. The framework calls start once to define the data set, then calls execute repeatedly with chunks of 200 records (configurable up to 2,000).

public class CaseAgingBatch implements Database.Batchable<sObject> {
  public Database.QueryLocator start(Database.BatchableContext bc) {
    return Database.getQueryLocator(
      'SELECT Id, Status FROM Case ' +
      'WHERE CreatedDate < LAST_N_DAYS:90'
    );
  }

  public void execute(Database.BatchableContext bc, List<Case> scope) {
    // Process up to 200 cases per execution.
  }

  public void finish(Database.BatchableContext bc) {
    // Optional cleanup, notifications, chained job kickoff.
  }
}

// Run:
Database.executeBatch(new CaseAgingBatch(), 200);

When to use Batch Apex:

  • Operations on more than 50,000 records.
  • Data migration cleanup that must complete reliably.
  • Nightly maintenance jobs (often combined with Scheduled).
  • Anything that would breach synchronous heap or CPU limits.

What Batch Apex does well:

  • Handles up to 50 million records per job.
  • Each chunk runs in its own transaction with its own governor limits.
  • Resumable: a failed chunk does not roll back the others.
  • Higher async governor limits.

What Batch Apex does poorly:

  • Latency. Jobs are queued and may not start immediately, especially when the org is busy.
  • Per-record processing. The 200-record chunk size is a sweet spot, but operations that genuinely process records individually inside the chunk hit per-execution limits.
  • Concurrent runs. Only 5 active or queued batch jobs allowed simultaneously (with some exceptions). High-frequency batch scheduling can hit this limit.

Scheduled Apex: time-driven execution

Scheduled Apex fires at defined times. The class implements Schedulable and is scheduled via the UI or System.schedule(...).

public class NightlyCleanupSchedule implements Schedulable {
  public void execute(SchedulableContext sc) {
    // Trigger Batch Apex from inside the Schedulable.
    Database.executeBatch(new CaseAgingBatch(), 200);
  }
}

// Schedule:
String cron = '0 0 2 * * ?'; // 2am every day
System.schedule('Nightly Case Aging', cron,
                new NightlyCleanupSchedule());

When to use Scheduled Apex:

  • Recurring jobs at fixed times (nightly, weekly, monthly).
  • Time-based triggers that the platform's standard Time-Based Workflows cannot handle.

Scheduled Apex is rarely the workhorse itself. It is typically used to fire off a Batch or Queueable that does the actual work. Keep Schedulable.execute short.

What to watch:

  • Cron strings are tricky to get right. Test the schedule with System.schedule in a sandbox before relying on it in production.
  • Scheduled jobs run as the user who scheduled them. Permission gotchas if that user lacks access to objects the job touches.
  • Maximum 100 scheduled jobs total per org. Heavy use of System.schedule in dynamic patterns can hit this limit.

A decision tree

For each new async requirement, run through:

  1. Is the work fixed-schedule (nightly, weekly)? → Scheduled Apex (kicking off Batch or Queueable).
  2. Is the data volume above 50,000 records? → Batch Apex.
  3. Does the work involve sObjects, callouts, or chained steps? → Queueable.
  4. Is this legacy code that already uses Future? → Leave it for now; refactor opportunistically.
  5. None of the above? → Queueable is the safe default.

Governor limit differences

Each async context has different limits. The ones that matter most:

Context SOQL queries DML statements CPU time
Synchronous 100 150 10,000ms
@future 200 150 60,000ms
Queueable 200 150 60,000ms
Batch Apex (per execute) 200 150 60,000ms

The async contexts roughly double the SOQL limit and 6x the CPU time. This is why moving expensive synchronous work to async often resolves governor limit issues even when nothing else changes.

Common async mistakes

Five patterns Sapota has seen in audits:

  • Future overused. Legacy orgs are full of @future methods that should have been Queueable. The cost is operational; the code keeps working until something requires chaining or sObject parameters.
  • Queueable used for huge volumes. A Queueable that tries to process 100,000 records in one execute. Hits async limits. Use Batch.
  • Batch Apex for small volumes. A Batch job that processes 1,000 records. The framework overhead is wasted. Use Queueable.
  • Scheduled Apex doing the work directly. A schedulable's execute method runs business logic synchronously. Hits limits at scale. Schedulable should kick off Batch or Queueable.
  • Async without testability. Async methods without Test.startTest/Test.stopTest boundaries in their tests. The async work never runs during the test, coverage is misleading.

What good async architecture looks like

A Salesforce org with healthy async patterns:

  • New code defaults to Queueable for async work.
  • @future code marked for opportunistic refactor when touched.
  • Batch Apex used for operations clearly above 50K records.
  • Scheduled Apex used for time-based kickoff, not for the work itself.
  • Async methods tested with Test.startTest/Test.stopTest to force execution.
  • AsyncApexJob queried for observability and operational dashboards.

Sapota's Salesforce team holds the Platform Developer I credential and selects async patterns deliberately during architecture design, not after the first scale incident. The selection has long-lived consequences for code maintainability and operational behavior.


Designing async Apex patterns or refactoring legacy @future code? Sapota's Salesforce team handles async architecture, batch processing design, and scheduled job operations on production engagements. Get in touch ->

See our full platform services for the stack we cover.

About this post

Posts on the Sapota engineering blog are written by the engineer who shipped the pattern, not by a marketing copywriter. The byline above maps to a verifiable LinkedIn profile on the team page; click through and you will see the same author with a link back to recent posts. Posts that reference internal projects describe the relevant architecture and the gotchas in enough detail to be useful while keeping client-confidential specifics out of the public version.

If a pattern in this post matches what your team is currently building, the engineers who wrote it are usually available to scope an engagement. Sapota's engagement model starts with a two-week no-commitment trial scoped to a concrete deliverable, with named senior engineers from day one. Direct pricing starts at $1,800 per engineer per month, no agency markup, monthly rolling. Reach out via the contact page with a short description of the project and the rough timeline.

For more on the platforms Sapota ships on, see the services hub, which links to dedicated pages for Salesforce, Power Platform, Retool, Shopify, AI and machine learning, custom software, web app development, and mobile app development. For more posts in this topic area, browse the relevant category linked at the top of this page or use the blog index for a full chronological view.

Sapota is a senior-only engineering bench based in Đà Nẵng, Vietnam, working directly with global businesses and as the engineering tier behind 30 plus Vietnamese IT service firms. Direct pricing starts at $1,800 per engineer per month, with a two-week no-commitment trial that opens every engagement. No agency markup, no minimum commitment, no bait-and-switch between proposal and kickoff. The same engineers who run the trial continue the project if the fit is right. The about pagecovers the company-level context, the engagement model, and the operating principles that shape every project. The FAQ page answers the practical questions that come up in the first conversation with a prospective client.

One more note on how to use the patterns described above. Most posts intentionally stop at the pattern itself: the decision framework, the failure mode, or the configuration walkthrough. The post does not try to map onto a specific client engagement because the same pattern applies across very different project contexts. If you read a post and think "yes, this is what we just hit," that is the intended reaction; the next step is usually a quick scope conversation about whether Sapota can help, what team composition would make sense, and how the two-week trial would be structured around your specific deliverable. That conversation lives at the contact page and replies typically come within one business day.

Contact Us Now

Share Your Story

We build trust by delivering what we promise – the first time and every time!

We'd love to hear your vision. Our IT experts will reach out to you during business hours to discuss making it happen.

WHY CHOOSE US

"Collaborate, Elevate, Celebrate where Associates - Create Project Excellence"

SapotaCorp beyond the IT industry standard, we are

  • Certificated
  • Assured quality
  • Extra maintenance

Tell us about your project