How to Write Bulkified Apex Code in Salesforce to Avoid Governor Limits

By | July 31, 2026

Salesforce runs Apex in a multi-tenant environment, where multiple organizations share the same computing resources. To ensure fair resource usage and maintain platform performance, Salesforce enforces Governor Limits hard limits on database operations, CPU usage, and memory consumption.

Some of the most important governor limits include:

  • SOQL queries per transaction: 100 (Synchronous) / 200 (Asynchronous)
  • DML statements per transaction: 150
  • Records processed per DML statement: 10,000
  • CPU time: 10,000 ms (Synchronous) / 60,000 ms (Asynchronous)

This is where Bulkification becomes essential.

Bulkification is the practice of writing Apex code that efficiently processes multiple records in a single transaction, regardless of whether one record or hundreds of records trigger the execution.

Triggers, in particular, can execute on batches of up to 200 records at once – whether initiated through Data Loader, Salesforce Flow, API integrations, or bulk imports. If your code performs SOQL queries or DML operations inside loops, it scales linearly with the number of records and quickly exceeds governor limits.

In this blog, we’ll explore common bulkification mistakes, learn the correct implementation patterns, and review best practices for writing governor limit-safe Apex code.

The Classic Anti-Pattern

One of the most common mistakes developers make is placing a SOQL query inside a loop.

trigger AccountTrigger on Account (before update) {
for (Account acc : Trigger.new) {

// SOQL inside a loop
    List<Contact> contacts = [
        SELECT Id
        FROM Contact
        WHERE AccountId = :acc.Id
    ];

    if (!contacts.isEmpty()) {
        acc.Description = 'Has ' + contacts.size() + ' contacts';
    }
}

}

This may work perfectly while testing with a single record in the Developer Console.

However, updating 200 Accounts causes the trigger to execute 200 SOQL queries, immediately exceeding Salesforce’s 100 SOQL query limit.

The same issue occurs with DML operations:

for (Account acc : accountsToUpdate) {
update acc; // DML inside loop
}

Performing DML inside loops quickly reaches the 150 DML statement limit, causing runtime exceptions.

The Bulkified Version

Instead of querying once for every record, query all required data outside the loop.

trigger AccountTrigger on Account (before update) {
Set<Id> accountIds = Trigger.newMap.keySet();

// One query outside the loop
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();

for (Contact c : [
    SELECT Id, AccountId
    FROM Contact
    WHERE AccountId IN :accountIds
]) {

    if (!contactsByAccount.containsKey(c.AccountId)) {
        contactsByAccount.put(c.AccountId, new List<Contact>());
    }

    contactsByAccount.get(c.AccountId).add(c);
}

for (Account acc : Trigger.new) {

    List<Contact> related = contactsByAccount.get(acc.Id);

    acc.Description = related != null
        ? 'Has ' + related.size() + ' contacts'
        : 'Has 0 contacts';
}
}

The outcome remains the same, but the trigger now executes only one SOQL query, regardless of whether the batch contains 1 record or 200 records.

This approach dramatically improves scalability and keeps your code within Salesforce governor limits.

Bulkifying DML Operations

The same principle applies to DML statements.

Instead of updating records inside the loop, collect them first and perform a single DML operation afterward.

List<Contact> contactsToUpdate = new List<Contact>();

for (Account acc : Trigger.new) {

    List<Contact> related = contactsByAccount.get(acc.Id);

    if (related != null) {

        for (Contact c : related) {
            c.Description = 'Parent account updated';
            contactsToUpdate.add(c);
        }
    }
}

if (!contactsToUpdate.isEmpty()) {
    update contactsToUpdate;    // Single DML statement
}

This approach performs only one update statement, regardless of how many Contact records are modified.

A Practical Bulkification Checklist

When reviewing Apex code for bulk safety, always verify the following:

  • Never place SOQL or SOSL queries inside loops. Query all required records once using IN :collectionOfIds.
  • Never perform DML operations inside loops. Collect records first, then perform a single insert, update, delete, or upsert.
  • Use Maps for fast lookups instead of repeatedly scanning Lists.
  • Always assume Trigger.new may contain up to 200 records, not just one.
  • Test your Apex with bulk data, not only individual records.
  • Protect against recursive trigger execution using static variables or a Trigger Handler framework.
  • Review helper classes and utility methods to ensure they are also bulkified. Even a bulk-safe trigger can fail if it calls non-bulkified methods internally.

Conclusion

Bulkification isn’t an advanced optimization technique – it’s a fundamental requirement for writing production-ready Apex code.

The guiding principle is simple: database operations such as SOQL queries and DML statements should occur once per transaction, not once per record.

By following bulkification best practices, using collections effectively, and minimizing database interactions, you can build scalable Apex solutions that stay within Salesforce governor limits and perform reliably in high-volume production environments.

By following the above blog instructions, you will be able to learn “How to Write Bulkified Apex Code in Salesforce to Avoid Governor Limits“. If you still have queries or any related problems, don’t hesitate to contact us at salesforce@greytrix.com. More details about our integration product are available on our website and Salesforce AppExchange.
We hope you may find this blog resourceful and helpful. However, if you still have concerns and need more help, please contact us at salesforce@greytrix.com.

About Us

Greytrix – a globally recognized and one of the oldest Sage Development Partner and a Salesforce Product development partner offers a wide variety of integration products and services to the end users as well as to the Partners and Sage PSG across the globe. We offer Consultation, Configuration, Training and support services in out-of-the-box functionality as well as customizations to incorporate custom business rules and functionalities that require apex code incorporation into the Salesforce platform.

Greytrix has some unique solutions for Cloud CRM such as Salesforce Sage integration for Sage X3, Sage 100 and Sage 300 (Sage Accpac). We also offer best-in-class Cloud CRM Salesforce customization and development services along with services such as Salesforce Data Migration, Integrated App development, Custom App development and Technical Support business partners and end users. Salesforce Cloud CRM integration offered by Greytrix works with Lightning web components and supports standard opportunity workflow. Greytrix GUMU™ integration for Sage ERP – Salesforce is a 5-star rated app listed on Salesforce AppExchange.
The GUMU™ Cloud framework by Greytrix forms the backbone of cloud integrations that are managed in real-time for processing and execution of application programs at the click of a button.

For more information on our Salesforce products and services, contact us at salesforce@greytrix.com. We will be glad to assist you.

Related Post: