| name | batch-apex-patterns |
| description | Use when designing, reviewing, or debugging Batch Apex contracts, scope sizing, stateful behavior, chaining, and AsyncApexJob monitoring. Triggers: 'Database.Batchable', 'Database.Stateful', 'executeBatch', 'batch scope', 'AsyncApexJob'. NOT for generic async choice discussions where Queueable or Future might still be the better tool. |
| category | apex |
| salesforce-version | Spring '25+ |
| well-architected-pillars | ["Scalability","Performance","Reliability"] |
| tags | ["batch-apex","database-batchable","stateful","asyncapexjob","batch-scope"] |
| triggers | ["when should I use Batch Apex","Database.Stateful pattern for batch","batch scope size and performance","AsyncApexJob monitoring for batch","chaining batch jobs safely","choosing QueryLocator vs Iterable for large data volumes","respecting batch concurrency and flex queue limits"] |
| inputs | ["expected record volume and whether query locator is needed","whether callouts, state accumulation, or chaining are required","operational monitoring and retry expectations"] |
| outputs | ["Batch Apex design recommendation","review findings for scope, lifecycle, and state risks","batch scaffold with monitoring and error accumulation guidance"] |
| dependencies | [] |
| version | 1.1.0 |
| author | Pranav Nagrecha |
| updated | "2026-07-07T00:00:00.000Z" |
Use this skill when Queueable is no longer enough and the workload genuinely needs chunked processing across many transactions. Batch Apex is powerful because each execute() scope receives fresh limits, but that same power introduces lifecycle, state, chaining, and monitoring choices that teams routinely under-design.
Before Starting
- Will the record volume exceed what one Queueable or synchronous transaction should safely handle?
- Does the job need
Database.getQueryLocator(), custom iterable input, callouts, or cross-scope state?
- How will operations know the batch succeeded, partially failed, or should be retried?
Core Concepts
start, execute, And finish Are Separate Responsibilities
start defines the workload, execute processes each scope, and finish handles summary or follow-up actions. Treating them as one blurred method creates monitoring and retry pain. Keep start lightweight, execute idempotent, and finish focused on reporting or safe handoff.
Scope Size Is A Throughput Tradeoff
The default batch size is commonly 200, but that is not always optimal. Large scopes can increase throughput for simple DML work. Smaller scopes may be safer for heavy processing or callouts. Scope sizing is a performance choice tied to payload weight, lock contention, and external system tolerance.
Database.Stateful Is Useful But Not Free
Stateful batch classes retain instance state between execute() calls. That is useful for counters, failed IDs, and summary metrics, but it also means more serialization overhead. Use it when the accumulated state changes the outcome or the reporting story, not by default.
AsyncApexJob Is Part Of The Pattern
Operationally, a batch job is not complete just because Database.executeBatch() returned an ID. Job status, processed counts, and error counts live in AsyncApexJob, and serious batch designs account for that from the start.
Governor Limits Are The Real Constraint At Large Data Volumes
For a large-data-volume design, the hard platform ceilings dictate the shape of the solution far more than code style does. Know these before you commit to an approach:
- Concurrency: up to 5 batch jobs can be queued or active at the same time in an org. Beyond that, submissions enter the Apex flex queue, which holds up to 100 jobs in the state. A design that fires many batches at once must serialize or schedule them to stay under the 5-slot ceiling.