- 1 Salesforce Programmatic Tools: Salesforce's programmatic tools, including Apex, Visualforce, and Lightning Web Components, empower developers to create highly customized solutions, automate complex processes, and seamlessly integrate with external systems for enhanced CRM functionality.
- 2 APIs for Seamless Integration: Salesforce's REST and SOAP APIs facilitate robust and versatile integration with external applications, ensuring smooth data exchange and creating a unified ecosystem that enhances the overall efficiency and interoperability of enterprise systems.
- 3 Customization and Scalability: Salesforce's programmatic tools provide unparalleled customization and scalability, allowing developers to implement advanced business logic, create dynamic user interfaces, and optimize performance for growing organizations with unique and complex requirements.
Introduction to Salesforce Apex
Salesforce Apex, the powerful programming language for the Salesforce platform, enables developers to build robust and scalable applications. Salesforce imposes specific limits on Apex code execution known as “governor limits” to maintain a stable and reliable environment. These limits prevent poorly written or resource-intensive code from negatively impacting the performance of the Salesforce platform. In this blog post, we’ll explore the Salesforce Apex governor limits, understand their significance, and discuss strategies for optimizing code to ensure a smooth and efficient application.
Understanding Governor Limits
Governor limits in Salesforce are essentially a set of runtime constraints that restrict the amount of resources and data that a piece of Apex code can consume during execution. These limits exist to prevent monopolization of resources and to ensure that all organizations sharing the Salesforce infrastructure have a fair and predictable experience. Common governor limits include:
| Governor Limit | Description |
|---|---|
| CPU Time Limit | Every Apex transaction is allocated a certain amount of CPU time. |
| SOQL and SOSL Query Limits | Control the number of records that can be queried in a single transaction. |
| DML Statement Limits | Restrictions on the number of records that can be inserted, updated, deleted, or undeleted. |
| Heap Size Limit | Restricts the amount of memory (heap) available for Apex code execution. |
| Governor Limits on Callouts | Limits the number of callouts that can be made in a transaction to external services. |
Why Governor Limits Exist in the First Place
It’s easy to see governor limits as an obstacle, but they’re really a byproduct of how Salesforce is built. Unlike a traditional application where you control the server, Salesforce runs on a multi-tenant architecture — meaning your org shares the same physical infrastructure with thousands of other organizations at the same time.
Without hard limits, a single inefficient trigger or a runaway loop in one company’s org could soak up CPU and memory that other customers depend on. Governor limits act as a fairness mechanism, making sure no single transaction can degrade performance for anyone else sharing that infrastructure.
This is also why governor limits are enforced per-transaction rather than per-user or per-org in most cases. A transaction — whether it’s triggered by a button click, an API call, or a scheduled job — gets its own fresh allocation of resources, and once it crosses a threshold, Salesforce throws an uncatchable LimitException and rolls back everything in that transaction. There’s no partial credit; the whole operation fails as a unit, which is precisely why designing around these limits from the start matters more than trying to patch around them later.
Optimizing Code for Governor Limits
To build efficient and scalable applications on the Salesforce platform, developers need to be mindful of governor limits. Here are some best practices for optimizing code:
| Best Practice | Description |
|---|---|
| Bulkify Your Code | Design your code to handle bulk data processing rather than focusing on individual records. |
| Limit the Use of SOQL Queries in Loops | Minimize the number of SOQL queries inside loops to avoid hitting the query limit. |
| Use Asynchronous Processing | Consider using asynchronous processing with features like Batch Apex or Queueable Apex. |
| Avoid Recursive Triggers | Implement trigger controls to prevent recursion and avoid hitting CPU time limits. |
| Monitor and Analyze Performance | Regularly monitor your application’s performance using Salesforce’s built-in tools. |
Bulkification: The Concept Behind Most Limit Issues
If there is one concept that fixes more governor limit issues than any other single practice, it is bulkification. The idea can’t be represented concisely in a best practices table, so we need to explain it in more detail.
The important detail here is that Salesforce triggers do not fire once per record. They fire once per transaction, where a transaction can include anywhere from 1 record to 200 (the default batch size). Triggers at this level often have a common logical error: developers who are used to working with single record operations in other languages such as Java or C# may write the Apex as if it is being executed for only one record, resulting in queries or DML operations inside loops over Trigger.new
The correct approach is to always write Apex triggers in such a way that a single record being processed is representative of all possible records being processed in a trigger. What this usually means is:
Querying related records once, outside of any loop, using a single SOQL query with an IN clause
Storing collections of records that need to be inserted, updated, or deleted in lists, and performing single DML operations on these lists
Using maps to associate related records (often using the record ID as a key) rather than querying or looping
Once you develop this muscle memory, most of these issues will appear less frequently, regardless of how much data is flowing through your application.
Tools for Monitoring and Debugging Governor Limits
Being aware of the limits is one thing, but being able to identify when a certain process is going to exceed the limits is another. Some tools that I find helpful to ensure that I’m not reaching the limits are:
Debug Logs: Salesforce’s debug logs tell you how many SOQL queries, DML statements, and CPU time in milliseconds, a transaction has used, and on what line it was executed at. Reviewing these when developing code helps me to see if what I’m doing is going to cause an error.
Limits Class: In Apex, there is something called the Limits class, that allows a developer to check the usage of the limits without having to go and manually look through debug logs. With this class you can write methods such as: `Limits.getQueries()` and `Limits.getLimitQueries()` which will tell you information about your queries.
Salesforce Optimizer: A free tool that comes from Setup, that advises you on what to change and what you’re doing correctly/incorrectly to improve your Salesforce instance’s performance, including many other aspects. It’ll help with things such as triggers, and other things that could be sources of limit exceptions.
Event Monitoring (with the add-on): For bigger instances, the Event Monitoring tool will give you information about your API’s usage, Apex, and other things relevant to your organization to analyze and improve.
Looking through Debug Logs while reviewing code, instead of waiting for an exception to occur, is what I find to be the best way of identifying potential limit exceptions.
Salesforce Governor Limits:
Salesforce imposes various governor limits to ensure the efficient use of resources and to prevent abuse that could negatively impact the performance of the platform. Below is a comprehensive list of Salesforce governor limits:
| Category | Limit | Purpose |
| Apex CPU Time | 10,000 milliseconds per transaction (synchronous) | Controls the total execution time for all Apex code in a transaction. |
| SOQL Queries | 100 SOQL queries per transaction | Restricts the number of queries to the database to prevent inefficient data retrieval. |
| SOSL Queries | 20 SOSL queries per transaction | Governs the number of Salesforce Object Search Language queries to the search index. |
| DML Statements | 150 DML statements (insert, update, delete, merge) per transaction | Ensures that transactions don’t manipulate excessive records in a single operation. |
| Total Heap Size | 6 MB for synchronous Apex, 12 MB for asynchronous Apex | Restricts the amount of memory (heap) that Apex code can use during execution. |
| Email Invocations | 10 email messages sent per transaction | Controls the number of email messages that can be sent in a single transaction. |
| Future Method Invocations | 50 future method invocations per transaction | Governs the number of asynchronous (future) methods that can be queued in a transaction. |
| Queueable Jobs | 50 Queueable jobs added to the queue per transaction | Restricts the number of jobs that can be added to the queue for asynchronous processing. |
| Batch Apex | 100 batch jobs queued or active concurrently | Governs the number of batch jobs that can be processed concurrently. |
| Streaming API Events | 20,000 events per 24-hour rolling window | Controls the number of Streaming API events that can be published within a specified timeframe. |
| Platform Events | 2,000,000 published events per rolling 24-hour window | Governs the number of platform events that can be published within a specified timeframe. |
| Callouts | 100 callouts per transaction (synchronous), 200 callouts per transaction (asynchronous) | Restricts the number of HTTP callouts that can be made in a single transaction. |
| External Services | 5,000 external service callouts per 24 hours | Governs the number of callouts to external services via External Services in a specified timeframe. |
| Dynamic Apex | 500,000 characters for the total size of Apex code | Restricts the size of dynamically generated Apex code. |
| Push Notifications | 2,000,000 push notifications per rolling 24-hour window | Governs the number of push notifications that can be sent within a specified timeframe. |
Synchronous vs Asynchronous Apex: Why the Limits Differ
You’ll notice in the table above that limits like heap size and callouts have different thresholds depending on whether the code runs synchronously or asynchronously. This isn’t arbitrary — it reflects how each type of execution behaves.
Synchronous Apex (like a trigger firing when a user saves a record) has to complete immediately, while the user is waiting. Because of that, Salesforce keeps its resource ceiling comparatively tighter to avoid tying up shared infrastructure with long-running operations that block a user interface.
Asynchronous Apex — Future methods, Queueable Apex, Batch Apex, and Scheduled Apex — runs in the background in its own transaction, decoupled from any user waiting on a response. Since there’s no one staring at a loading spinner, Salesforce affords it more generous limits, like double the heap size and double the callout allowance.
This is a big part of why “use asynchronous processing” shows up so often as a best practice. If you’re working with large data volumes, complex integrations, or operations that naturally take longer to complete, shifting that logic into a Queueable or Batch Apex context often solves limit issues without touching your core logic much at all.
Conclusion
In conclusion, mastering the intricacies of Salesforce Apex Governor Limits is paramount for any developer aiming to create robust and efficient applications on the platform. As we’ve explored, these limits are in place to ensure the stability and performance of Salesforce environments, preventing resource abuse and optimizing overall system functionality. By understanding and navigating these limits judiciously, developers can strike a balance between innovation and adherence to best practices. Continuous monitoring, optimization, and leveraging Salesforce tools for performance analysis are essential components of successful development within the confines of Governor Limits. Ultimately, embracing these limits as guidelines rather than restrictions fosters the creation of scalable, reliable, and high-performing applications, enhancing the overall Salesforce development experience.
