Implement the Database.Batchable<SObject> interface with three methods: start(), execute(), and finish()
In start(), return a Database.QueryLocator using Database.getQueryLocator('SELECT Id, ... FROM Object WHERE ...') to let the platform paginate up to 50 million records
In execute(Database.BatchableContext bc, List<SObject> scope), process the current batch chunk; the default and maximum batch size is 200 records per execute call
In finish(Database.BatchableContext bc), perform post-processing such as sending a summary email or enqueuing a follow-up job using AsyncApexJob query on bc.getJobId()
Submit the batch using Database.executeBatch(new MyBatch(), batchSize), specifying a smaller batch size (e.g., 10–50) if the execute method makes callouts or performs complex processing
Monitor job progress by querying AsyncApexJob where Id = :bc.getJobId() to check JobItemsProcessed, TotalJobItems, and Status fields
Known gotchas
Each execute() call is an independent transaction with its own governor limits; SOQL queries inside execute() should still be batched against the scope list, not the global dataset
Batch Apex cannot make HTTP callouts unless the class also implements Database.AllowsCallouts, and the batch size must be set to 1 when calling external services to avoid callout limit violations
Scheduling a batch job inside finish() using System.scheduleBatch() is allowed, but chaining more than five levels of batch jobs is architecturally problematic and difficult to monitor
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp