Bulkification in Apex is the discipline of designing code that efficiently processes huge amounts of data in a single execution while adhering to Salesforce governor constraints (e.g., 50,000 records queried, 10,000 DML rows per transaction). Bulkification is crucial in Salesforce since Apex is frequently used in situations such as triggers, batch tasks, and connectors that process several records at once. Poorly bulkified code may exceed governor limitations, cause performance concerns, or fail.
Key Principles of Bulkification
1. Process Records in Collections
Developers should work on collections (List, Set, Map) to handle multiple records at once instead of processing records individually. This approach minimizes governor limit usage and boosts performance.
2. Minimize SOQL Queries
All necessary data should be retrieved using a single query. Try to avoid multiple SOQL queries to fetch related data. Instead of separate queries, use relationship queries to fetch related data efficiently.
3. Minimize DML Operations
Perform DML operations (insert, update, delete) on collections rather than individual records. Consolidate DML statements outside loops. Use List<sObject> for different types of object collections for DML operations.
4. Leverage Maps for Efficient Lookups
Use a Map to store and access related data quickly, and also to avoid repeated queries. Instead of querying the same record multiple times, you can use a Map<Id, sObject>
to store query results once and reference them quickly, reducing SOQL calls and improving performance.
5. Handle Triggers Efficiently
Triggers can process up to 200 records at a time in bulk operations (e.g., data imports). We should ensure trigger logic scales to handle this. We can use a trigger framework that handles bulk operations. Always create trigger handler classes for logic creation. Don’t write logic in the trigger class code.
6. Handle Mixed Operations (Insert, Update, Delete) Carefully
When handling mixed operations (Insert, Update, Delete) in Apex, it’s crucial to separate the logic for each operation type to avoid data conflicts and governor limit issues. Use conditional blocks to check if the trigger is for insert, update, or delete to isolate logic. Additionally, group records into separate lists (e.g., recordsToInsert
, recordsToUpdate
, recordsToDelete
) and perform DML only once per group, after processing is complete. This ensures cleaner logic, avoids duplicate processing, and keeps your code bulk-safe and maintainable.
7. Avoid DML Statements Inside Loops
Never place DML statements inside loops to avoid hitting governor limits in Apex. Instead, collect all records that need to be inserted, updated, or deleted into a list during the loop, and perform the DML operation once outside the loop. This approach ensures your code is bulk-safe and avoids the risk of exceeding the limit of 150 DML statements per transaction.
8. Test with Bulk Data (200+ Records)
Always write test classes that simulate large data volumes to catch bulkification issues early. Ensure your logic handles Trigger.new, collections, and maps correctly. Always assert that the code processes all records without hitting governor limits and performs bulk-safe DML and SOQL operations.
Check out Apex Code best practices for other key points.
Real-World Use Cases with Apex Code Examples
Use Case 1: Update Related Contacts When Accounts Are Updated
Scenario: When the Industry field of an Account is updated, all related Contacts’ Description fields should be updated to match the new industry. This logic should be handled by a trigger that supports bulk operations, such as when 200 Accounts are updated through a data import.
Non-Bulkified Trigger Logic for Contact Field Update
Issues with the above code:
- SOQL query inside a loop (hits query limit if many Accounts are processed).
- DML operation (update) inside a loop (hits DML limit).
Bulkified Trigger Logic for Contact Field Update
Why It’s Bulkified:
- Created separate Trigger and Trigger Handler classes for code best practices.
- Uses a set to collect AccountId values for querying.
- Single SOQL query to fetch all related Contacts. Only required fields are fetched.
- Single DML operation (update) outside the loop. Only the field assignment is done in a loop.
- Handles bulk data (e.g., 200 Accounts with multiple contacts each).
Refer to the post Optimizing Loop in Salesforce Apex for loop optimization.
Use Case 2: Sync Opportunities with External System
Scenario: When opportunities are updated, sync their Amount and CloseDate to an external system via an API call. The code must handle bulk updates without hitting governor limits.
Non-Bulkified Code:
Issues with the above code:
- SOQL query inside a loop.
- API call (callout) inside a loop (hits callout limit of 100 per transaction). After 100 callouts, an exception will be thrown.
Bulkified Code for external callout
Why It’s Bulkified:
- Single SOQL query to fetch all related Accounts.
- Uses a Map for efficient account lookups.
- Consolidates API payloads into a single call (or batches for large datasets).
- Handles bulk opportunities efficiently.
Use Case 3: Batch Apex for Processing Large Datasets
Scenario: Update the Status__c field of all Cases older than 30 days to ‘Closed’. The dataset is too large for a single transaction, so use Batch Apex.
Non-Bulkified Apex Code
Issues in code:
- DML Statement in loop
Bulkified Batch Apex
Why It’s Bulkified:
- Uses Batch Apex to process records in chunks (default batch size is 200).
- Single DML operation per batch.
- Scales to handle thousands or millions of records without hitting governor limits.
Summary
When working with Salesforce Apex, it’s important to follow best coding practices to ensure your code runs reliably at runtime. One key practice is bulkification, which allows your code to handle both single records and large record sets (like data imports) efficiently. This post includes practical examples, but there are many other essential bulkification techniques developers should consider.
Need an Expert to Code Bulkification?
Check out our gig – development of Lightning Web Components, Apex, or Integration.
References
Similar post
- Optimizing Salesforce Apex Code
- Limiting data rows for lists
- Disable Debug Mode in production
- Caching of the described object in Apex
- How to Use Filter in SOQL
- Avoid Heap Size in Salesforce Apex
- How to Optimize Salesforce Trigger
- Understanding Foreign Key Relationship in SOQL
- Defer Sharing Rules
- Avoid Hardcoding in Apex
- Use Platform Caching to enhance performance