Skip to main content

SF-0379 · Scenario · Medium

Can you give an example of maintaining the state of a batch apex?

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

Here’s a worked example: a batch that processes orders, tallies a running total of order amounts, collects every failed-record error, and emails a summary at the end. Everything that accumulates across chunks is an instance field that only works because of Database.Stateful.

The class

public class OrderTotalsBatch implements
    Database.Batchable<sObject>,
    Database.Stateful
{
    // State that survives across executes — needs Database.Stateful
    public Decimal totalRevenue = 0;
    public Integer totalProcessed = 0;
    public Integer totalErrors = 0;
    public List<String> errorMessages = new List<String>();
    public Datetime startTime;

    public Database.QueryLocator start(Database.BatchableContext ctx) {
        startTime = Datetime.now();
        return Database.getQueryLocator(
            'SELECT Id, Total__c, Status__c FROM Order__c WHERE Year__c = 2026'
        );
    }

    public void execute(Database.BatchableContext ctx, List<Order__c> scope) {
        for (Order__c o : scope) {
            try {
                // Accumulate revenue across chunks
                totalRevenue += (o.Total__c == null ? 0 : o.Total__c);

                // Update each order
                o.Status__c = 'Reconciled';
                totalProcessed++;
            } catch (Exception e) {
                totalErrors++;
                errorMessages.add('Order ' + o.Id + ': ' + e.getMessage());
            }
        }
        update scope;
    }

    public void finish(Database.BatchableContext ctx) {
        Long duration = (Datetime.now().getTime() - startTime.getTime()) / 1000;

        AsyncApexJob job = [
            SELECT CreatedBy.Email FROM AsyncApexJob WHERE Id = :ctx.getJobId()
        ];

        Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
        msg.setToAddresses(new String[] { job.CreatedBy.Email });
        msg.setSubject('Order Totals Batch Complete');
        msg.setPlainTextBody(
            'Orders processed: ' + totalProcessed + '\n' +
            'Total revenue: $' + totalRevenue + '\n' +
            'Errors: ' + totalErrors + '\n' +
            'Duration: ' + duration + ' seconds\n\n' +
            (totalErrors > 0
                ? 'First 10 errors:\n' + String.join(firstN(errorMessages, 10), '\n')
                : 'No errors.')
        );
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { msg });
    }

    private List<String> firstN(List<String> list, Integer n) {
        if (list.size() <= n) return list;
        List<String> top = new List<String>();
        for (Integer i = 0; i < n; i++) top.add(list[i]);
        return top;
    }
}

What’s stateful — and what’s not

FieldStateful?Why
totalRevenueYes (with interface)Instance member, mutated across chunks
totalProcessed, totalErrorsYesSame
errorMessagesYesList grows across chunks
startTimeYesAssigned once in start, read in finish
Any static variableNoStatics reset every transaction regardless
scope parameterN/AJust the chunk’s records, lives only in execute

The same class without Database.Stateful

// Without Database.Stateful
public class OrderTotalsBatch implements Database.Batchable<sObject> {
    public Decimal totalRevenue = 0; // resets every execute!
    // ...
}

After running, finish would show totalRevenue = 0 (or the last chunk’s value only) and errorMessages = []. Every chunk got a fresh instance, every increment was thrown away.

Other common stateful examples

  • Per-account rollupsMap<Id, Decimal> accumulating totals across chunks.
  • High-water marksoldestDate, largestAmount updated as more records arrive.
  • Summary by categoryMap<String, Integer> counting record types.
  • Detailed per-record results — list of “succeeded”, “failed”, “skipped” rows for an end-of-job report.

Common interview follow-ups

  • Are static variables stateful in batch? — No. Statics reset every transaction. Only instance fields plus Database.Stateful work.
  • Performance impact? — Slight. Salesforce serializes the instance between chunks. Don’t add it if you don’t need it.
  • Can I store anything? — Anything serializable — sObjects, custom Apex types, primitives, maps, lists. Avoid transient fields.

Verified against: Apex Developer Guide — Database.Stateful Interface. Last reviewed 2026-05-17.