Governor limits are the runtime quotas Salesforce enforces on every Apex transaction. They exist because Salesforce is multi-tenant — your code shares CPU, memory, and database connections with thousands of other customers on the same instance. Without hard caps, one runaway loop could starve every other org on the pod.
The limits are per-transaction, not per-day. A transaction is everything that runs from the moment your trigger or controller fires until it returns — including DML, callouts, and chained logic. Cross a limit, and the runtime throws a System.LimitException and rolls the entire transaction back.
The core per-transaction limits
Here are the limits a Salesforce developer is expected to know cold (synchronous, current as of Spring ‘26):
| Resource | Synchronous limit | Async (batch/queueable/future) |
|---|---|---|
| SOQL queries | 100 | 200 |
| Records retrieved by SOQL | 50,000 | 50,000 |
| SOSL queries | 20 | 20 |
| DML statements | 150 | 150 |
| Records processed by DML | 10,000 | 10,000 |
| CPU time | 10,000 ms | 60,000 ms |
| Heap size | 6 MB | 12 MB |
| Callouts | 100 | 100 |
| Callout timeout (cumulative) | 120 seconds | 120 seconds |
@future calls | 50 | 0 (can’t chain) |
| Queueable jobs | 50 | 1 (chained) |
A few are easy to miss:
- CPU time is Apex CPU only — time spent in your code, not waiting on SOQL or callouts. Aggregations, sorts, and large
Mapwalks burn it fast. - Heap is the live size of every variable in memory. Building a
List<sObject>of 50,000 records will not blow SOQL row limits but very likely will blow heap. - DML rows counts each saved record, not each
insertstatement. Oneinsertof 10,001 records fails.
Why async gets relaxed limits
Async transactions (batch, queueable, scheduled, future) run on a different worker pool. They’re allowed longer CPU and more SOQL queries because no user is waiting on a page response. The trade-off: they have their own concurrency caps (e.g., 5 concurrent batch jobs per org) and the platform schedules them when it has capacity.
How to stay under
- Bulkify — never SOQL or DML inside a
forloop. The platform fires triggers on batches of up to 200 records and bulk APIs push the same code through hundreds of thousands. - SOQL for-loops over List —
for (Account a : [SELECT ... FROM Account])streams 200 rows at a time so heap stays small. - Aggregate on the server —
SELECT COUNT(Id), SUM(Amount) FROM Opportunityruns in the database, not on your CPU budget. - Defer to async — long callouts, multi-million-row scans, or anything risking CPU belongs in
Database.BatchableorQueueable. - Inspect at runtime —
Limits.getQueries(),Limits.getCpuTime(),Limits.getHeapSize()return the running totals so you can defensive-check before a risky operation.
What interviewers are really looking for
The basic answer names a few limits. The senior answer explains why they exist (multi-tenancy), names the resource categories (SOQL/DML/CPU/heap/callouts), distinguishes sync vs async budgets, and connects them to patterns — bulkification, SOQL aggregation, async deferral. Mention Limits.getQueries() for runtime introspection and you’ve signaled production-debug experience.
Verified against: Apex Developer Guide — Execution Governors and Limits. Last reviewed 2026-05-17 for Spring ‘26 release.