Mastering Queueable Apex in Salesforce

Queueable Apex is a flexible, efficient way to run long-running and bulk operations in Salesforce. Learn when to use it, how to chain jobs, and best practices to avoid governor limits.

Why Queueable Apex?

Asynchronous processing is essential when you must process large data volumes or run operations that exceed synchronous governor limits. Queueable Apex fills the gap between simple @future methods and Batch Apex by allowing complex parameter types, chaining, and monitoring via the Apex Jobs queue. This makes it ideal for data migration, bulk updates, and multi-step integration workflows.

Core features

  • Accepts complex data types (sObjects, collections, custom Apex classes)
  • Monitored via Setup → Apex Jobs and returns a job Id
  • Supports chaining for sequential processing
  • Works well with Finalizer for post-job cleanup and reporting

Basic Queueable example

Below is a concise implementation that updates Account records in the background.

public class AccountUpdateQueueable implements Queueable {
    private List accountsToUpdate;
    private String updateReason;
    
    public AccountUpdateQueueable(List accounts, String reason) {
        this.accountsToUpdate = accounts;
        this.updateReason = reason;
    }
    
    public void execute(QueueableContext context) {
        try {
            List processedAccounts = new List();
            for (Account acc : accountsToUpdate) {
                acc.Description = 'Updated: ' + updateReason + ' on ' + DateTime.now();
                processedAccounts.add(acc);
            }
            if (!processedAccounts.isEmpty()) update processedAccounts;
        } catch (Exception e) {
            handleError(e, context.getJobId());
        }
    }
    
    private void handleError(Exception e, Id jobId) {
        // Log or notify
    }
}

Enqueueing the job

List accounts = [SELECT Id, Name FROM Account LIMIT 100];
Id jobId = System.enqueueJob(new AccountUpdateQueueable(accounts, 'Bulk Update'));
System.debug('Job enqueued: ' + jobId);

Chaining and stateful processing

Chain queueable jobs when you need ordered steps or when governor limits require breaking work into smaller chunks. Use lightweight state objects (serializable) to carry progress between chained jobs, and always include guardrails to prevent infinite chaining.

Best practices

  • Be governor-aware: monitor Limits.getDmlStatements() and Limits.getQueries()
  • Batch work into safe sizes and chain continuation jobs when needed
  • Implement retry strategies with capped retries and exponential backoff
  • Separate setup-object DML from non-setup DML to avoid mixed-DML errors
  • Log job start/completion using a custom monitoring object and Finalizer when available

Testing Queueable classes

Wrap your enqueue call inside Test.startTest()/Test.stopTest() to execute queueable jobs synchronously in tests. Assert both functional outcomes and AsyncApexJob status to ensure reliability.

Real-world use cases

  • Large data migrations with chained batches
  • Multi-step integration workflows requiring ordered API calls
  • Scheduled cleanup or archival jobs that run off-peak

Conclusion

Queueable Apex gives Salesforce developers a powerful tool to scale processing without blocking the UI or hitting synchronous limits. When designed with governor limits, error handling, and monitoring in mind, queueable jobs can dramatically improve reliability and throughput for heavy workloads.

Why this matters: Admins can offload heavy operations to scheduled or event-driven jobs, developers can design resilient, multi-step processes, and business users get timely updates without performance trade-offs. Implementing Queueable Apex correctly helps teams maintain platform health while delivering complex automation.