Skip to main content

SF-0364 · Scenario · Medium

How to use a batch apex?

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

Using Batch Apex is a three-step process:

  1. Write a class that implements Database.Batchable<sObject>.
  2. Implement the three required methodsstart, execute, finish.
  3. Invoke it with Database.executeBatch(new YourBatch(), batchSize).

Step 1: Write the class

public class InactiveCaseCleanup implements Database.Batchable<sObject> {

    public Database.QueryLocator start(Database.BatchableContext ctx) {
        return Database.getQueryLocator(
            'SELECT Id, Status FROM Case WHERE Status = \'New\' ' +
            'AND CreatedDate < LAST_N_DAYS:90'
        );
    }

    public void execute(Database.BatchableContext ctx, List<Case> scope) {
        for (Case c : scope) {
            c.Status = 'Auto-Closed';
            c.Description = 'Closed by 90-day cleanup batch ' + Date.today();
        }
        update scope;
    }

    public void finish(Database.BatchableContext ctx) {
        // Optional: send completion email, chain another batch
    }
}

Step 2: Understand each method’s role

MethodRunsPurpose
startOnceDefine the dataset (QueryLocator or Iterable)
executeOnce per chunk (default 200 records)Process this chunk
finishOnceNotify / chain / cleanup

Step 3: Invoke it

From the Developer Console, anonymous Apex, or any class:

// Default chunk size of 200 records
Id jobId = Database.executeBatch(new InactiveCaseCleanup());

// Custom chunk size (1 to 2000)
Id jobId = Database.executeBatch(new InactiveCaseCleanup(), 100);

Database.executeBatch returns the JobId synchronously. The actual batch runs async — you can monitor it via Setup → Apex Jobs or query AsyncApexJob.

Choose the right chunk size

WorkloadSuggested chunk size
Simple field updates, no related-record DML200 (default)
Triggers/flows on related records cascade50–100
Heavy callouts (one per record)10–25
Mass deletes of cold records200–500

Smaller chunks = less risk of hitting CPU/SOQL limits per chunk, but more chunks total. Larger chunks = fewer transactions, but higher chance of governor blowouts.

When to add Database.Stateful

If you need to track totals or accumulated errors across chunks, add Database.Stateful:

public class InactiveCaseCleanup implements Database.Batchable<sObject>, Database.Stateful {
    private Integer totalProcessed = 0;
    // ... totalProcessed survives across executes
}

When to add Database.AllowsCallouts

If execute makes HTTP callouts:

public class SyncBatch implements Database.Batchable<sObject>, Database.AllowsCallouts {
    public void execute(Database.BatchableContext ctx, List<Account> scope) {
        Http http = new Http();
        // ... callouts allowed here
    }
}

Common pitfalls

  • No bulkified DML in execute — you’ll blow SOQL/DML limits fast. Process the entire scope in one update, not per-record.
  • No @future from execute — throws AsyncException. Use Queueable instead (from finish).
  • Mixing data setup in startstart is for the query, not for side effects. Side effects belong in execute/finish.

Common interview follow-ups

  • What’s the smallest chunk size? — 1.
  • What’s the largest? — 2,000.
  • How many batches can run concurrently? — 5 active + 100 holding in the Apex Flex Queue.
  • Can I chain another batch? — Yes — call Database.executeBatch from finish.

Verified against: Apex Developer Guide — Using Batch Apex. Last reviewed 2026-05-17.