Salesforce governor limits are rules that cap resource usage in Apex code to protect the multi-tenant environment. Because many customers share the same server resources, Salesforce enforces limits to prevent any single org’s code from hogging databases, memory, CPU, or network bandwidth.
For example, there’s a limit on how many queries or DML operations you can perform in one transaction. If a governor limit is exceeded, Salesforce immediately throws a runtime exception that cannot be caught – the execution halts. These limits ensure fairness and stability, but they can frustrate developers when hit unexpectedly. Below, we’ll discuss common governor limit errors and how to debug and fix them.
Salesforce allows at most 100 SOQL queries per synchronous Apex transaction. Exceeding this causes a System.LimitException. This often happens when a SOQL query is placed inside a loop, causing each iteration to run a query. The solution is to refactor the code to bulkify queries, i.e., move SOQL outside loops and query once for all needed data.
Similar to queries, there’s a limit of 150 DML operations (inserts/updates/deletes) per Apex transaction. This typically occurs by doing DML inside a loop. The fix is to use collections for DML – accumulate records in a list and perform one bulk insert/update at the end.
Salesforce limits the CPU time a transaction can use. In a synchronous context, the limit is 10,000 milliseconds. If your Apex code runs longer, you’ll get this error. Common causes include large loops, inefficient algorithms, or recursive logic. To avoid this, optimize your code, reduce loop iterations, or offload processing to asynchronous jobs.
Salesforce enforces a heap limit of 6 MB for synchronous and 12 MB for asynchronous transactions. If your code accumulates too much data in memory, you’ll hit this error. Prevent this by querying only needed fields, processing in small batches, and using SOQL for loops or Batch Apex.
A single transaction can perform up to 100 external callouts. This error often occurs when making callouts inside a loop. Resolve this by batching records using Batch Apex, or aggregating data for fewer callouts if the external system supports bulk input.
System.debug
statements with Limits methods to monitor real-time consumption.Violation: The code below performs a SOQL query inside a loop. If contacts contain more than 100 records, this will run a query for each contact and hit the SOQL limit.
// BAD Practice: SOQL query inside a loop
for (Contact c : contacts) {
// Querying related Account for each contact (inefficient!)
Account acc = [SELECT Name, Industry FROM Account WHERE Id = :c.AccountId];
// ... (do something with acc)
}
On a large data set, the above will throw “Too many SOQL queries: 101” as soon as the 101st query executes. The fix is to bulkify the code by moving the query outside the loop, querying all needed Accounts in one go, and using a collection (map) for lookup:
// GOOD Practice: Bulkified query outside the loop
// Gather all AccountIds from contacts
Set acctIds = new Set();
for (Contact c : contacts) {
acctIds.add(c.AccountId);
}
// Query all relevant Accounts in one query
Map accounts = new Map(
[SELECT Name, Industry FROM Account WHERE Id IN :acctIds]
);
// Loop through contacts and use the map to get corresponding Account
for (Contact c : contacts) {
Account acc = accounts.get(c.AccountId);
// ... (do something with acc)
}
Violation: This code creates a new Task for each contact and inserts it inside the loop:
// BAD Practice: DML inside a loop
for (Contact c : contacts) {
Task t = new Task(WhoId = c.Id, Subject = 'Follow Up');
insert t; // DML operation in loop
}
If contacts has over 150 records, the 151st insert will hit the DML limit and throw “Too many DML statements: 151”. The best practice is to batch these DML operations by using a list:
// GOOD Practice: Bulk DML with a list
List tasksToInsert = new List();
for (Contact c : contacts) {
Task t = new Task(WhoId = c.Id, Subject = 'Follow Up');
tasksToInsert.add(t); // add to list instead of inserting immediately
}
if (!tasksToInsert.isEmpty()) {
insert tasksToInsert; // single bulk DML for all tasks
}
If your team continues to face governor limit issues despite applying bulkification and asynchronous processing, it may be time to escalate through Salesforce support channels for deeper investigation.
By following these practices, Apex developers can prevent most governor limit problems. Always think about scale, and design your code with the assumption that it might process large data volumes in real-world scenarios. This helps you stay within limits and ensures smooth, efficient Salesforce performance.