Skip to main content

SF-0363 · Concept · Easy

What is a batch apex?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Batch Apex is Salesforce’s mechanism for processing large data volumes asynchronously by breaking the job into chunks. Each chunk runs as its own transaction with its own governor limits, so the platform can process 50 million records without any single transaction blowing the 10-second CPU ceiling or the 100-SOQL limit.

The three-method shape

A Batch class implements Database.Batchable<sObject> and three methods:

public class AccountCleanupBatch implements Database.Batchable<sObject> {

    public Database.QueryLocator start(Database.BatchableContext ctx) {
        return Database.getQueryLocator(
            'SELECT Id, Industry FROM Account WHERE Industry = null'
        );
    }

    public void execute(Database.BatchableContext ctx, List<Account> scope) {
        for (Account a : scope) {
            a.Industry = 'Unspecified';
        }
        update scope;
    }

    public void finish(Database.BatchableContext ctx) {
        // Send completion email, kick off the next job, log results
    }
}
  • start runs once. It returns either a Database.QueryLocator (for queries up to 50 million records) or an Iterable<sObject> (for in-memory or custom-source data, capped at 50,000).
  • execute runs once per chunk. The default chunk size is 200 records; you can change it from 1 to 2,000 in the executeBatch call.
  • finish runs once after all chunks complete. Use it for completion notifications, follow-up jobs, or final aggregation.

Why batches matter

A synchronous transaction in Salesforce gets:

LimitSyncBatch (per execute)
CPU time10,000 ms60,000 ms
Heap6 MB12 MB
SOQL queries100200
Records returned by SOQL50,00050,000
DML statements150150

By splitting work into 200-record chunks, each chunk gets the higher async ceiling. A million-record cleanup that would never fit in sync completes as 5,000 transactions.

Kicking off a batch

Id jobId = Database.executeBatch(new AccountCleanupBatch(), 200);

Database.executeBatch(batchClass, scope) queues the job. The optional scope parameter sets the chunk size; defaults to 200. The return value is the JobId, queryable in AsyncApexJob:

AsyncApexJob job = [
    SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors
    FROM AsyncApexJob WHERE Id = :jobId
];

When to use Batch over Queueable

The dividing line is data volume:

  • Queueable — work that fits in one transaction’s governor limits (up to ~10,000 records depending on complexity).
  • Batch — work that doesn’t, or that needs the safety of chunking so a single bad row doesn’t fail the whole job.

Batch also has built-in retry semantics. If execute() throws on chunk #47, chunks 1–46 are already committed; only chunk #47 fails. Queueable is all-or-nothing per invocation.

Limits worth memorizing

  • Up to 5 active or queued batch jobs per org at the same time.
  • Up to 100 jobs in the Apex Flex Queue (Holding status, waiting to enter the Queued state).
  • Up to 50 million records per batch job via Database.QueryLocator.
  • Chunk size: 1–2,000 records. Default 200.

Common interview follow-ups

  • Can Batch make callouts? — Yes, if the class implements Database.AllowsCallouts and you only call out from execute() or finish().
  • Do chunks run in order? — No. Salesforce makes no ordering guarantee between executes. Don’t write logic that depends on order.
  • Can I maintain state across chunks? — Yes, with Database.Stateful. Member variables persist across executes if the class implements it.

Verified against: Apex Developer Guide — Batch Apex. Last reviewed 2026-05-17 for Spring ‘26.