Beksultan Islanbek.

Batch Apex Deep Dive: How Salesforce Processes Millions of Records

TL;DR

Batch Apex is Salesforce's built-in way to handle jobs too big to run in one go — think creating a week of attendance records for an entire program, or recalculating funding numbers across thousands of rows. It collects the work, processes it in small chunks of up to 200 records (each chunk getting a fresh allowance of system resources), then reports on the run and hands off to the next job. This guide explains how it all works in plain English — no code required — using two real jobs from a Bay Area early-childhood nonprofit's Salesforce: a weekly attendance job covering 5,000+ records across 50+ classrooms, and the calculator that turns those records into state-reimbursement numbers.

1. Why Batch Apex Exists: Salesforce Puts a Ceiling on Every Operation

Salesforce is a shared building. Thousands of organizations run on the same infrastructure, so the platform enforces strict caps — called governor limits — on how much any single operation can do: how many records it can look up, how many it can save, how much computing time it can use. Think of it like an elevator's weight limit. That's healthy; it stops one runaway process from slowing everyone down. But it means a job that touches thousands of records can't run as one big gulp.

Batch Apex is the platform's answer, and the idea is simple: don't raise the ceiling — take more trips. It splits a big job into small chunks and runs each chunk separately, so no single trip ever gets close to the weight limit.

Signs a job belongs in a batch:

  • It touches thousands of records — or will, as the organization grows.
  • It runs on a schedule (nightly, weekly, monthly) rather than when someone clicks a button.
  • Nobody is sitting at a screen waiting for it — so there's no reason to squeeze it into one risky operation.

2. The Three Phases: Collect, Process, Report

Every batch job has the same skeleton, and it's worth knowing even if you never write a line of code — because it's the vocabulary your developer will use. The three phases are named start(), execute(), and finish(), but they're easier to remember as collect, process, report.

What each phase does:

  • Collect — build the to-do list, and only the to-do list. The shorter the list, the less work everything downstream does.
  • Process — work through the list in stacks of up to 200 records, one stack at a time.
  • Report — run once at the very end: write down what happened, notify a human, and kick off the next job if there is one.

3. Every Chunk Starts Fresh — That's the Whole Magic Trick

Here's the part that makes "millions of records" possible: every chunk runs as its own separate operation, with a completely fresh allowance of system resources. The counters go back to zero 200 records at a time. So a job never has to fit the whole dataset under the platform's caps — it only has to fit one chunk. Whether the list holds five thousand records or five million, each individual chunk faces exactly the same, easily-met limits.

What this means in practice:

  • Chunk size is a math question: how much follow-on work does one record create? Records that trigger lots of downstream updates need smaller chunks.
  • A smaller chunk size just means more trips — it costs time, not safety. A too-big chunk size is the opposite.
  • The work list itself can be enormous: a batch job can collect up to 50 million records when they come straight from the Salesforce database.

4. Good Batch Jobs Keep Score

There's a catch to all those fresh starts: by default, a batch job has no memory. Each chunk starts blank, so a running total kept during one chunk reads zero in the next. Salesforce offers an opt-in fix — a setting called Database.Stateful — that carries the job's memory across chunks. With it, a job can keep score for the entire run: how many records were handled, how many were skipped, and exactly what went wrong where.

Score-keeping done right:

  • Carry only small things: counts, error notes, a short list of record references. Memory is carried between every chunk, so weight matters.
  • The score exists for the report phase — if the final report doesn't use it, the job shouldn't carry it.
  • A report built from a full-run score can say "completed with 3 errors" — far more useful than a bare pass/fail.

5. Chained Jobs — and What's Allowed to Break the Chain

A batch job's report phase can do one more thing: start the next job. That's called batch chaining, and it's how multi-day work schedules itself — Monday's run hands off to Tuesday's, and so on. The mechanics are trivial. The decision hiding inside them is not: under what conditions should the chain stop? That's a business question wearing a technical costume, and getting it wrong is expensive.

The rule of thumb:

  • Stop the chain only when the job itself broke — a real system failure.
  • A few records failing to save is normal life with real-world data: log them, report them, and keep going.
  • Whatever rule you choose, test it with bad data on purpose — the difference between the two rules only shows up when something fails.

6. One Job, Two Gears: The Weekly Run and the Do-Over

Every scheduled job eventually needs to run a second way: redo these specific records, cover an unusual date range, recalculate after someone corrects a mistake upstream. The tempting shortcut is to copy the job and tweak the copy — and now the same business logic lives in two places, and only one of them gets the next fix. The better pattern is one job with two gears: the same engine, started differently depending on the need.

The two gears:

  • The scheduled gear picks up only work that hasn't been done yet — which quietly makes it safe to re-run, since a second pass finds nothing left to do.
  • The do-over gear takes a specific list of records and redoes exactly those, no questions asked.
  • Both gears share one engine, so a fix to the logic fixes every way the job runs.

7. Built to Be Tested — Because Nonprofits Rarely Have a QA Team

Most nonprofits don't have a testing department; automated tests are the testing department. The catch is that a batch job's most important behaviors are its awkward ones — what happens when a save fails halfway, when a rate table changes, when the summary email needs to say something uncomfortable — and a job built carelessly gives tests no way to reach those moments. Good batch jobs are built with test doors: deliberate openings that let a test poke the hard parts safely.

The doors worth building:

  • A way to simulate failed saves — without actually breaking real data to do it.
  • A way to hand the job a pretend rate table, since tests can't touch the real configuration.
  • A way to check what the summary email would say — without sending anyone mail.
The Golden Rule

The hard part of Batch Apex isn't the ten-millionth record — it's deciding what one bad record is allowed to take down with it.

Common Questions

What is Batch Apex in Salesforce and when should I use it?

Batch Apex is Salesforce's built-in way of handling jobs that are too big to run in one go. Instead of trying to process an entire dataset at once, it works through the list in small chunks — up to 200 records at a time — and each chunk gets a fresh allowance of system resources. It's the right tool whenever a job touches thousands of records or more: nightly cleanups, weekly record creation, or recalculating values across a whole program year.

What is the default batch size in Batch Apex, and can I change it?

By default, Salesforce hands each processing step 200 records at a time. That number can be tuned anywhere from 1 to 2,000 when the job is launched. Smaller chunk sizes make sense when each record triggers a lot of follow-on work. The guiding question is how much total work one chunk creates — not how fast you'd like the job to finish.

Do governor limits reset between execute() calls in Batch Apex?

Yes. Salesforce caps how much any single operation can do — those caps are called governor limits — and every chunk in a batch job gets a brand-new allowance. That reset is the whole reason Batch Apex can handle millions of records: no single step ever has to stay under the caps for the entire dataset, only for its own small chunk.

What is the difference between Database.QueryLocator and Iterable in Batch Apex?

These are the two ways a batch job can collect its work list. A QueryLocator pulls records straight from the Salesforce database and can handle up to 50 million of them — a thousand times the normal query ceiling. An Iterable tops out at 50,000 records but can be built from any list, such as rows from an uploaded file. In practice: if the records already live in Salesforce, use a QueryLocator; if the source is a file or an outside system, use an Iterable.

What does Database.Stateful do in Batch Apex?

Normally a batch job has no memory — every chunk starts blank, so a running total kept in one chunk reads zero in the next. Adding Database.Stateful tells Salesforce to carry the job's memory across chunks, which is how a job can keep accurate totals — records processed, records skipped, errors hit — and report on the entire run at the end. The trade-off is a little overhead on every chunk, so well-built jobs carry only small things: counts and error notes, not piles of records.

Can a batch job start another batch job in Salesforce?

Yes — the wrap-up step of one job can launch the next, a pattern called batch chaining. It's how a Monday job can hand off to a Tuesday job automatically. The detail that matters is the rule for when the chain should stop: if the rule is too strict — stop unless everything was perfect — a single bad record can quietly cancel every job downstream, which is a much bigger problem than the bad record itself.

Should I use Batch Apex or Queueable Apex?

Both run work in the background. Queueable Apex is the lighter option — good for moderate amounts of work kicked off by something a user just did. Batch Apex is built for large or unpredictable volumes: it gives each chunk its own fresh limits and comes with a built-in lifecycle for collecting work, processing it, and reporting at the end. That's why scheduled, high-volume jobs almost always end up as Batch Apex.