Governor Limits in Salesforce: Broken Code and the Fix
Governor limits in Salesforce explained with code that breaks each limit and the fix: SOQL, DML, CPU, heap and callouts, plus interview answers.
·8 min read·Salesforce AI
Key takeaways
- Governor limits in Salesforce are per-transaction caps, such as 100 SOQL queries and 150 DML statements in synchronous Apex, that stop one tenant from hogging shared resources.
- A limit exception can't be caught, so the only fix is design: query and write in bulk, outside loops.
- Code that passes with one record in a sandbox usually fails when 200 records arrive in a trigger.
- Test with 201 records, watch the
Limitsclass, and move heavy work to Batch or Queueable Apex.
"Too many SOQL queries: 101" is the most famous error message in Salesforce development. It's also the one that fails interviews.
Governor limits in Salesforce are the reason code that looks fine on your laptop breaks in production. Candidates can recite the numbers, but few can show the code that breaks a limit and the fix. This page does both.
My argument is simple. Limits aren't a list to memorise. They're a design contract: write every piece of Apex as if 200 records are coming, and most limit errors never happen.
What are governor limits in Salesforce?
Governor limits are runtime caps that Salesforce enforces per transaction so no single customer can monopolise shared resources.
Salesforce is multitenant, so many orgs share the same servers. The Apex Developer Guide says the runtime enforces limits so that runaway code doesn't monopolise them. If your code exceeds one, the governor throws a runtime exception that can't be handled.
Most limits are per transaction. A few, such as 24-hour limits, aren't. Certified managed packages have their own per-transaction limits section in the Apex Developer Guide.
What are the main governor limits in Salesforce?
Five limits cause nearly all real-world failures.
| Limit | Synchronous | Asynchronous | Error you'll see |
|---|---|---|---|
| SOQL queries | 100 | Too many SOQL queries: 101 | |
| Records retrieved by SOQL | 50,000 | 50,000 | Too many query rows: 50001 |
| DML statements | 150 | 150 | Too many DML statements: 151 |
| Records processed by DML | 10,000 | 10,000 | Too many DML rows: 10001 |
| CPU time | 10,000 ms | Apex CPU time limit exceeded |
Numbers can change between releases, so check the official limits table before you quote one in an interview or a design document.
How do you fix "Too many SOQL queries: 101"?
This is the loop-query mistake, and the fix is one query and a Map.
Here's a trigger that works perfectly when you create one Contact:
trigger ContactTrigger on Contact (before insert) {
for (Contact c : Trigger.new) {
Account a = [SELECT Id, Industry FROM Account WHERE Id = :c.AccountId];
c.Description = a.Industry;
}
}
Load 200 contacts with Data Loader and the 101st iteration throws the exception. Triggers can receive more than one record at a time, as the Salesforce Developers bulk processing article explains.
The fix:
trigger ContactTrigger on Contact (before insert) {
Set<Id> accountIds = new Set<Id>();
for (Contact c : Trigger.new) {
if (c.AccountId != null) accountIds.add(c.AccountId);
}
Map<Id, Account> accounts = new Map<Id, Account>(
[SELECT Id, Industry FROM Account WHERE Id IN :accountIds]
);
for (Contact c : Trigger.new) {
Account a = accounts.get(c.AccountId);
if (a != null) c.Description = a.Industry;
}
}
One query for any batch size. The pattern is always the same: collect IDs, query once, look up in a Map. My bulk-safe trigger walkthrough builds this from scratch.
How do you fix "Too many DML statements: 151"?
Collect records in a list and run one DML statement after the loop.
Breaks at record 151:
for (Account a : Trigger.new) {
insert new Task(WhatId = a.Id, Subject = 'Welcome call');
}
Fix:
List<Task> tasks = new List<Task>();
for (Account a : Trigger.new) {
tasks.add(new Task(WhatId = a.Id, Subject = 'Welcome call'));
}
insert tasks;
This is one statement and 200 rows, well inside both the 150-statement and the 10,000-row limits.
How do you get past the 50,000 query row limit?
If one transaction needs more than 50,000 rows, you don't need a cleverer query. You need Batch Apex.
This fails in an org with 80,000 pending cases:
List<Case> pending = [SELECT Id, Status FROM Case WHERE Status = 'Pending'];
Batch Apex splits the work into separate transactions, each with fresh limits. A query locator can reach far past 50,000 rows.
public class CaseCloser implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator(
'SELECT Id, Status FROM Case WHERE Status = \'Pending\'');
}
public void execute(Database.BatchableContext bc, List<Case> scope) {
for (Case c : scope) c.Status = 'Closed';
update scope;
}
public void finish(Database.BatchableContext bc) {}
}
// Run it:
Database.executeBatch(new CaseCloser(), 200);
Each execute call is its own transaction. That's the whole trick.
How do you fix "Apex CPU time limit exceeded"?
CPU limit errors almost always come from loops inside loops, and a Map removes the inner loop.
for (Contact c : contacts) {
for (Account a : accounts) { // 200 x 5,000 = 1,000,000 comparisons
if (c.AccountId == a.Id) { c.Description = a.Name; }
}
}
Build a Map<Id, Account> once, then call accounts.get(c.AccountId). The work drops from a million comparisons to 200 lookups. Synchronous Apex gets 10,000 ms of CPU time, so a loop that does a million comparisons per record chunk will find the ceiling quickly.
How do you avoid the Apex heap size limit?
Select only the fields you use and let a SOQL for loop hand you records 200 at a time.
// Risky: loads every row and every field you ask for into memory at once
List<Account> all = [SELECT Id, Name, Description FROM Account];
// Safer: processes 200 records per iteration
for (List<Account> chunk : [SELECT Id, Name FROM Account]) {
// work with up to 200 records
}
A SOQL for loop helps with memory. It doesn't let you slip past the 50,000-row limit.
What is the callout limit in Apex?
Batch the payload into one request, or move the callout into a Queueable.
Calling an external API once per record is the same loop mistake in a different costume. Send a list in one request if the other system supports it. If not, use Queueable with Database.AllowsCallouts so each job makes a manageable number of calls.
My take: Stop teaching limits as a table to memorise. I've worked on Salesforce since 2008, and I think the numbers matter far less than one habit: write every trigger as if 200 records arrive. Developers who do that rarely meet the 101 error, and the ones who memorise 20 limits still write queries inside loops.
How do you check governor limits in Apex?
Use the Limits class in code and the cumulative limits section of the debug log.
System.debug('SOQL used: ' + Limits.getQueries() + ' of ' + Limits.getLimitQueries());
System.debug('DML rows: ' + Limits.getDmlRows() + ' of ' + Limits.getLimitDmlRows());
System.debug('CPU ms: ' + Limits.getCpuTime() + ' of ' + Limits.getLimitCpuTime());
Use these while developing, not as runtime branching in production. Since a limit exception can't be caught, the check is a diagnostic. The design is the fix.
Flow follows the same rules. A flow in the same transaction as your trigger spends from the same budget. Trailhead's Avoid Flow Limits unit covers the loop patterns to avoid, and my guide on how to debug a Salesforce Flow shows how to find the element that burns the budget. To see why trigger, flow and validation logic share one budget, read order of execution in Salesforce.
Common mistakes that burn limits
Almost every limit failure is one of five habits.
- Queries or DML inside a
forloop. - Testing with one record, so the bulk case is never exercised.
- A trigger and a flow on the same object both running queries, so each looks fine alone but together they exceed the budget.
SELECTwith noWHEREorLIMITon a large object.- Calling the same helper method that queries from inside another loop.
AI-written code makes this more urgent
A coding assistant will happily write the one-record version unless you make the tests catch it.
A Salesforce blog post dated 5 November 2026 argues for governor limit testing for AI-generated code, treating limits as architectural contracts and rejecting inefficient code automatically. I agree with the direction. In my judgement, assistant code that passes a single-record test is the most common limit-breaker in a modern codebase.
My take: A bulk test with 201 records should be a merge requirement, not a nice-to-have. It's the cheapest review you'll ever add. It catches in a sandbox what would otherwise surface on a Friday afternoon in production.
Try this week:
- Open your three biggest triggers and search for
[SELECTandinsert,updateordeleteinside loops. - Add one test per trigger that inserts 201 records, and assert on the result.
- Put
Limits.getQueries()debug lines in one handler and read the numbers in a debug log. - Practise explaining the SOQL-in-a-loop fix out loud in 60 seconds, since interviewers ask for it. My Salesforce developer interview questions page has more like it.
If you want to work through your own org's limit problems with a practitioner, see one of my sessions.
Frequently asked questions
What are governor limits in Salesforce, in one line?
They're per-transaction caps on resources such as queries, DML, CPU time and memory, enforced because Salesforce is multitenant. If code exceeds one, it fails with a runtime exception.
Can you catch a governor limit exception in Apex?
No. Salesforce documents that the exception raised when a limit is exceeded can't be handled. That's why the fix is bulk-safe design rather than try/catch.
What's the SOQL limit in Salesforce?
Synchronous Apex allows 100 SOQL queries per transaction. Separately, one transaction can retrieve at most 50,000 rows in total.
Do governor limits reset in test classes?
Yes, partly. Code between Test.startTest() and Test.stopTest() gets a fresh set of limits, so set up test data before startTest and run the code you're testing inside it.
How do I avoid hitting governor limits?
Bulkify triggers (collect IDs, query once, use Maps), keep DML out of loops, select only needed fields, and move large jobs to Batch or Queueable Apex. Test with at least 200 records.
Want to put this to work in your business?
Join one of my live sessions, or tell me what you're trying to do with AI.
Newsletter
Get the next one by email.
New writing on Salesforce AI, AI agents and the AI-Native Enterprise. Confirm by email; unsubscribe any time.
Keep reading
21 Oct 2026 · Salesforce AISalesforce Developer Interview Questions 2026: 15 Answers15 Salesforce developer interview questions for 2026 with answers: Apex, triggers, async jobs and security, with code and official doc links.8 min14 Nov 2026 · Salesforce AISalesforce Data Loader: Install, Import, Export, Fix ErrorsSalesforce Data Loader guide for admins: install it, import and export CSV files, and fix the errors you'll actually hit, with the cause of each.10 min
12 Nov 2026 · Salesforce AISalesforce AI Associate Certification: 2026 Study GuideSalesforce AI Associate certification retired on 2 February 2026. Here's a topic-by-topic study guide and the official path that replaced it.7 min