Warning

Fraudulent domains such as innostaxtech.com or innostaxtechllc.com are NOT affiliated with Innostax. Official communication only comes from @innostax.com. We never request money, banking details, deposits, or equipment purchases during hiring.

Comprehensive Guide to Queueable Apex in Salesforce

Unlock the power of Queueable Apex in Salesforce with our guide. Learn tips, best practices, and boost your development efficiency with scalable solutions.

Comprehensive Guide to Queueable Apex in Salesforce
Key takeaways
  • 1 Queueable Apex allows the Salesforce developers to work with massive amount of data and intricate business processes in a flexible way improving performance and using fewer resources due to the background execution of tasks.
  • 2 Using Apex with the enqueueable feature affords flexibility and scalability of the application, and it assists in the handling of major datasets as well as the management of sequential jobs.
  • 3 Thus, the guidelines for Queueable Apex are the following: designing for massive datasets, keeping in mind the governor limits, utilizing callouts – to create effective and stable applications.
  • 4 Queueable Apex gives each job a separate transaction with fresh governor limits, making it useful for breaking asynchronous work into manageable steps.
  • 5 Queueable Apex supports flexible data passing, job chaining, callouts, and monitoring, making it a practical choice for many Salesforce background processes.

In the world of Salesforce development, where efficiency and scalability are of utmost importance, the Queueable Apex stands out as a flexible and powerful tool. In this blog, we will delve into the Apex and queueable and explore its capabilities and various applications, and best practices. Throughout this blog, developers will garner the knowledge and skills necessary to harness the power of Queueable and create highly efficient and scalable apps. By understanding the inner workings of this powerful tool, developers can be at the forefront of Salesforce development, utilizing the power of Queueable Apex to its maximum potential and ensuring their apps are up to the task in the dynamic world of Salesforce.

What are Apex classes?

Apex classes may be regarded as the key element in the force.com platform since they allow developers to design powerful business logic and build high-quality applications capable of addressing particular organizational needs. Being an object-oriented programming language, Apex provides developers with appropriate concepts and tools needed to effectively perform their tasks. In general, Apex classes are created in order to define methods, variables, and properties that will be utilized to develop custom applications in the Salesforce environment, for instance, apex map class, apex string class, etc.

  • Apex Map Class : The Apex map class represents a collection of key-value pairs, where each key is associated with a specific value. Maps are versatile data structures that provide efficient methods for accessing, updating, and iterating over their elements.
  • Apex String Class : The String class in Apex is used to represent and manipulate sequences of characters, providing a wide range of methods for string manipulation and analysis. It supports concatenation, substring extraction, pattern matching, and conversion between different data types.

Understanding Queueable Apex

What Is Queueable Apex and When to Use It

Queueable Apex runs a unit of work asynchronously, in its own transaction, separate from the one that enqueued it. It’s the right choice when you need to offload work that doesn’t have to complete before the user’s request finishes — sending a callout to an external system after a record is saved, performing a moderately sized calculation across related records, or running a sequence of dependent steps.

It sits between Future methods and Batch Apex in complexity and scale:

  • Use Queueable Apex for a single unit of work, or a short chain of dependent steps, involving up to a few thousand records or a handful of callouts.
  • Use Batch Apex when the record volume is large enough that it needs to be split into multiple chunks with a start/execute/finish lifecycle.
  • Use a Future method only when maintaining older code that doesn’t need chaining, complex parameter types, or job monitoring — for new development, Queueable Apex is almost always the better option.

How Queueable Apex Works

A class becomes queueable by implementing the Queueable interface and defining a single execute method:

public class SendAccountUpdateQueueable implements Queueable {
    public void execute(QueueableContext context) {
        // Asynchronous logic goes here
    }
}

You submit it for execution with System.enqueueJob, which returns the job’s AsyncApexJob ID:

Id jobId = System.enqueueJob(new SendAccountUpdateQueueable());

The QueueableContext parameter passed into execute exposes the running job’s ID via context.getJobId(), which is useful for logging or for querying AsyncApexJob to check status after the fact. Unlike Batch Apex, there’s no start or finish method — the entire unit of work lives in execute, and the job either runs to completion or fails as a whole.

Passing Data to a Queueable Job

Because a Queueable class is a regular object, data is passed the normal way — through a constructor — rather than through a query defined inside the job itself. This is one of its clearest advantages over Future methods, which only accept primitive parameter types (String, Integer, List<String>, and similar):

public class SendAccountUpdateQueueable implements Queueable {
    private List<Id> accountIds;

    public SendAccountUpdateQueueable(List<Id> accountIds) {
        this.accountIds = accountIds;
    }

    public void execute(QueueableContext context) {
        List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
        for (Account acc : accounts) {
            acc.Description = 'Reviewed';
        }
        update accounts;
    }
}

Passing sObject records, custom classes, or collections directly means the job doesn’t need to re-derive its working set from scratch, which keeps the logic close to how the rest of the class already handles data.

Chaining Jobs and Managing Stack Depth

A Queueable job can enqueue another Queueable job from within its own execute method, which is how sequential, dependent steps are modeled — for example, updating a record and then, only once that succeeds, notifying an external system.

public void execute(QueueableContext context) {
    // Step 1 logic here
    System.enqueueJob(new NextStepQueueable());
}

Two constraints govern this: each executing job may enqueue only one child job from within its own execute method, and in production orgs the chain depth itself is unlimited — a job can keep chaining the next job indefinitely, provided each step respects standard governor limits. Developer Edition and trial orgs cap chain depth at 5 for safety.

For jobs invoked from trigger context, uncontrolled chaining can create runaway loops if the same condition keeps being true. The AsyncOptions class lets you cap this explicitly:

AsyncOptions options = new AsyncOptions();
options.MaximumQueueableStackDepth = 5;
System.enqueueJob(new NextStepQueueable(), options);

System.AsyncInfo.getCurrentQueueableStackDepth() can be checked inside execute to see how deep the current chain is, which is useful for logging or for deciding whether to stop chaining and hand off to a different mechanism (such as Batch Apex) instead.

Making Callouts from Queueable Apex

Queueable Apex supports HTTP callouts, but the class must explicitly declare this by also implementing Database.AllowsCallouts:

public class NotifyExternalSystem implements Queueable, Database.AllowsCallouts {
    public void execute(QueueableContext context) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:External_System/notify');
        req.setMethod('POST');
        new Http().send(req);
    }
}

Without this second interface, any callout attempt inside execute throws a runtime exception. The same per-transaction callout limits apply here as anywhere else in Apex — up to 100 callouts and a combined 120-second timeout per transaction — so a job making several callouts should be tested against those ceilings, particularly if it’s also part of a chain where callouts happen at each step.

Governor Limits and Transaction Boundaries

Each Queueable job execution is its own transaction and gets a fresh set of governor limits, the same way each Batch Apex execute call does:

LimitValueNotes
Jobs enqueued from a Queueable’s own execute1Enforced per chained step
Jobs enqueued from non-queueable synchronous context50Per transaction
Chain depth (production orgs)UnlimitedCapped at 5 in Developer/Trial orgs unless overridden
Heap size12 MBAsynchronous Apex limit
CPU time60,000 msAsynchronous Apex limit
Callouts per transaction100Requires Database.AllowsCallouts
Callout timeout (cumulative)120 secondsPer transaction
DML statements per transaction150Standard Apex limit, resets per job

Because limits reset per job rather than accumulating across a chain, a well-designed sequence of small Queueable jobs can process considerably more work overall than a single monolithic transaction could — the trade-off is added complexity in tracking state and failures across steps.

Error Handling, Monitoring, and Retries with Finalizers

An unhandled exception in execute fails the job outright — there’s no partial-batch recovery the way there can be in Batch Apex. The Finalizer interface, attached inside execute via System.attachFinalizer, is the reliable way to react to a job’s outcome, including uncatchable failures like limit exceptions:

public class SyncFinalizer implements Finalizer {
    public void execute(FinalizerContext ctx) {
        if (ctx.getResult() == ParentJobResult.UNHANDLED_EXCEPTION) {
            // Log the failure, alert a team, or re-enqueue with backoff
        }
    }
}
public void execute(QueueableContext context) {
    System.attachFinalizer(new SyncFinalizer());
    // main job logic
}

A Finalizer is the only mechanism guaranteed to run after the parent job completes, whether it succeeded, failed with a handled exception, or died to something uncatchable — which makes it the standard place to implement retry logic rather than wrapping the entire execute body in a broad try/catch. For visibility outside code, querying AsyncApexJob (status, NumberOfErrors, ExtendedStatus) or checking the Apex Jobs page in Setup covers day-to-day monitoring.

Bulkification and Performance Considerations

Queueable jobs are still regular Apex, so the same bulkification rules apply: no SOQL or DML inside loops, and queries built to retrieve exactly the records needed rather than broad, unfiltered sets. Where Queueable-specific performance judgment comes in is choosing job size — a single job handling too many records risks hitting heap or CPU limits, while over-splitting work into many tiny chained jobs adds queuing latency and makes failure tracking harder to reason about. As a practical guideline, a few hundred to a couple of thousand records per job, with chaining used for genuinely sequential steps rather than as a substitute for Batch Apex’s record-splitting, tends to hold up well in production.

Best Practices for Production Use

  • Pass only the data a job actually needs through its constructor — IDs rather than full records, where possible, to keep heap usage low.
  • Cap chain depth explicitly with AsyncOptions for any chain triggered from record changes, to prevent runaway loops if the triggering condition doesn’t resolve.
  • Attach a Finalizer to any job with side effects (DML, callouts) that must not fail silently.
  • Declare Database.AllowsCallouts only on classes that actually need it, to keep the class’s capabilities explicit and easy to audit.
  • Test chained jobs with Test.startTest()/Test.stopTest(), keeping in mind that only one level of chaining reliably executes within a single test context.
  • Monitor AsyncApexJob for failed jobs proactively rather than relying on users to report missing updates.

When Should You Choose Queueable Apex?

Queueable Apex is suitable for cases when the background process requires more flexibility than a Future method offers but does not have the complexity of a Batch Apex job. It is beneficial for the process that involves the need to use a custom object or collection of sObjects, a callout to an external service after a transaction in Salesforce is committed, or a set of dependent sequential operations.

An example of such a scenario would be a situation when records in Salesforce need to be updated, this change has to be sent to another application with a following step of receiving data from this application.

This process would require multiple Apex jobs to be chained together, Salesforce transaction in one job committed before making a callout to an external service which then replies with data that has to be processed in Salesforce.

Queueable Apex should not be used when an operation can be easily scheduled as a single unit. If the process requires extensive data processing at a scale that is not feasible in one job, consider using Batch Apex with its chunking mechanism. Chaining jobs in Queueable Apex could also bloat the system if not planned correctly. The best choice for job scheduling depends on how much data needs processing, if the jobs are dependent on each other, if any external callouts are required in the process, and how the system will report errors in case of failure. All these factors should be considered to get the best performance out of Apex Queues for an application that grows in complexity over time.

Conclusion

Queueable Apex earns its place as the default asynchronous mechanism in modern Apex development by combining what Future methods lack — object parameters, chaining, and proper job monitoring — without the structural overhead of Batch Apex’s three-method lifecycle. Used well, with disciplined chain-depth management, a Finalizer for reliable error handling, and job sizing that respects governor limits, it handles the majority of background-processing needs in a Salesforce org. The decision that matters most isn’t how to write the execute method — that part is straightforward — but recognizing when record volume or complexity has outgrown Queueable Apex and Batch Apex is the more appropriate tool.

Additional Resources

Get a Fast Estimate on Your Software
Development Project

Chat With Us

Frequently Asked Questions

Queueable Apex is a way to run Apex code asynchronously in a separate transaction. It is useful when a task does not need to finish before the user's request is completed, such as processing related records, performing calculations, or making callouts to an external system. A queueable class implements the Queueable interface and contains an execute method where the required logic is performed. The job is then added to the queue using System.enqueueJob(), which returns an AsyncApexJob ID that can be used to track its execution.

Queueable Apex is generally better for a single unit of work or a short sequence of dependent jobs involving a moderate number of records. It is useful when the processing can be completed in one asynchronous transaction without the need to divide a large dataset into multiple batches. Batch Apex is more appropriate when the record volume is large enough to require processing in separate chunks through its start, execute, and finish lifecycle. Choosing between the two mainly depends on the amount of data, the complexity of the processing, and whether the work needs to be split into manageable batches.

Yes. Queueable Apex supports HTTP callouts when the queueable class also implements Database.AllowsCallouts. This makes it useful for sending data to or receiving data from external systems without blocking the original transaction. For example, a queueable job can be used after a Salesforce record is saved to send information to an external API. Salesforce allows up to 100 callouts in a single transaction, with a cumulative callout timeout of up to 120 seconds.

Yes. A Queueable job can enqueue another Queueable job from its execute method, allowing developers to create a sequence of dependent steps. This can be useful when one operation needs to finish before another begins, such as preparing data first and then sending the processed information to another system. Only one child queueable job can be added from an executing queueable job. In production orgs, chain depth is unlimited, although developers should still control chaining when there is a risk of creating unnecessary or runaway job sequences.

Developers can use a Finalizer to respond when a Queueable job completes or fails, including failures that cannot be handled through a normal try/catch block. A finalizer can be used to log failures, trigger alerts, or implement controlled retry logic when appropriate. The AsyncApexJob record can also be used to check the job's status, number of errors, and execution details. For production applications, proactively monitoring queueable jobs helps teams identify failed or repeatedly delayed processing before it affects downstream systems or business workflows.