Sitemap
Hrishikesh Wagh

Hrishikesh Wagh

Mastering Maps and Bulkification in Apex Triggers: A Practical Guide for Salesforce Developers

4 min readMay 22, 2026

--

Press enter or click to view image in full size

Salesforce triggers are one of the most powerful parts of Apex development, but they are also one of the most common reasons behind governor limit exceptions and performance issues.

A major difference between beginner and experienced Salesforce developers is the ability to write:

  • Bulkified triggers
  • Efficient SOQL queries
  • Optimized Map-based logic
  • Scalable trigger architectures

In this blog, we will understand:

  1. Why bulkification matters
  2. Why Maps are essential in triggers
  3. Common mistakes developers make
  4. Real-world trigger patterns
  5. Trigger best practices used in enterprise projects

Understanding Bulkification

In Salesforce, a trigger does not execute for a single record only.

A single trigger execution may process:

  • 1 record
  • 10 records
  • 200 records
  • Thousands of records through batches or integrations

For example:

  • Data Loader
  • API integrations
  • Flow updates
  • Batch Apex
  • Mass inline edits

All can invoke triggers in bulk.

This is why Salesforce imposes governor limits.

Some important limits are:

LimitValueSOQL Queries100DML Statements150CPU TimeLimitedHeap SizeLimited

If your trigger is not bulkified, it will fail in production.

The Most Common Beginner Mistake

SOQL Inside a Loop

trigger BadTrigger on Contact(after insert){
for(Contact con : Trigger.new){
Account acc = [
SELECT Id, Name
FROM Account
WHERE Id = :con.AccountId
];
acc.Description = 'Updated';
update acc;
}
}

Why This Is Bad

If 200 Contacts are inserted:

  • 200 SOQL queries executed
  • 200 DML statements executed

This immediately hits governor limits.

The Correct Bulkified Approach

The proper approach is:

  1. Collect IDs
  2. Query once
  3. Store records in a Map
  4. Process records
  5. Perform one DML operation

Why Maps Are So Important

A Map stores data as key-value pairs.

Map<Id, Account> accMap;

KeyValueAccount IdAccount Record

Maps provide extremely fast lookup access.

Instead of looping through records repeatedly, we can directly retrieve records using .get().

Real-World Bulkified Example

Scenario

Whenever a Contact is inserted, update the parent Account Description.

Optimized Solution

trigger ContactTrigger on Contact(after insert){
Set<Id> accountIds = new Set<Id>();
for(Contact con : Trigger.new){
if(con.AccountId != null){
accountIds.add(con.AccountId);
}
}
Map<Id, Account> accMap = new Map<Id, Account>(
[SELECT Id, Description
FROM Account
WHERE Id IN :accountIds]
);
for(Contact con : Trigger.new){
Account acc = accMap.get(con.AccountId);
if(acc != null){
acc.Description = 'Contact Added';
}
}
update accMap.values();
}

Understanding the Flow

Step 1 — Collect IDs

Set<Id> accountIds = new Set<Id>();

We gather all Account IDs first.

Step 2 — Query Once

Map<Id, Account> accMap

We query all Accounts in a single SOQL query.

Step 3 — Fast Retrieval Using Map

Account acc = accMap.get(con.AccountId);

This is the most important line.

Instead of looping through Accounts repeatedly, the Map gives direct access.

Trigger.oldMap and Trigger.newMap

Salesforce automatically provides maps during update and delete operations.

These are heavily used in enterprise projects.

Detecting Field Changes

Scenario

Create a Task only when Opportunity stage changes to Closed Won.

for(Opportunity opp : Trigger.new){
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if(opp.StageName == 'Closed Won' &&
oldOpp.StageName != 'Closed Won'){
// logic here
}
}

Why Trigger.oldMap Is Powerful

Without oldMap, you cannot compare previous and current values efficiently.

Get Hrishikesh Wagh’s stories in your inbox

Join Medium for free to get updates from this writer.

This pattern is extremely common in interviews.

Composite Key Pattern

One of the most useful advanced Map patterns is the composite key.

Scenario

Prevent duplicate Contacts with same Email under the same Account.

Example

String key = con.AccountId + '-' + con.Email;

Store it inside:

Map<String, Contact>

This allows uniqueness checks across multiple fields.

Aggregate Result with Maps

Scenario

Update Account with total Opportunity Amount.

Map<Id, Decimal> totalAmountMap = new Map<Id, Decimal>();
for(AggregateResult ar : [
SELECT AccountId accId,
SUM(Amount) total
FROM Opportunity
GROUP BY AccountId
]){
totalAmountMap.put(
(Id) ar.get('accId'),
(Decimal) ar.get('total')
);
}

This is one of the most frequently used enterprise trigger patterns.

Trigger Best Practices

1. One Trigger Per Object

Avoid multiple triggers on the same object.

Good:

AccountTrigger

Bad:

AccountTrigger1
AccountValidationTrigger
AccountUpdateTrigger

2. Keep Logic Out of Trigger

Triggers should delegate logic to handler classes.

Good trigger:

trigger AccountTrigger on Account(before insert, before update){
AccountTriggerHandler.handle(Trigger.new);
}

3. Never Use SOQL Inside Loops

Bad:

for(Account acc : Trigger.new){
[SELECT Id FROM Contact];
}

4. Never Use DML Inside Loops

Bad:

for(Account acc : accounts){
update acc;
}

Good:

update accounts;

5. Use Sets for Unique IDs

Set<Id> accountIds = new Set<Id>();

Sets automatically prevent duplicates.

6. Use Maps for Fast Lookup

Map<Id, Account> accMap

This is fundamental for scalable trigger development.

7. Handle Null Values Properly

Always check for nulls.

if(con.AccountId != null)

8. Avoid Recursive Trigger Execution

Use static variables when needed.

public static Boolean isRunning = false;

Enterprise Trigger Pattern

Most enterprise projects follow this structure:

Trigger
↓
Handler Class
↓
Service Class
↓
Utility Class

This improves:

  • Maintainability
  • Testability
  • Scalability
  • Reusability

Mental Framework for Trigger Questions

Whenever you see a trigger scenario, think in this order:

Step 1

Which IDs do I need?

Step 2

Can I collect them into a Set?

Step 3

Can I query all records at once?

Step 4

Should I store them in a Map?

Step 5

Can I perform one DML instead of many?

If you follow this framework consistently, most trigger questions become manageable.

Final Thoughts

Maps and bulkification are not advanced Apex concepts anymore — they are mandatory skills for Salesforce developers.

Most trigger failures in production happen because developers:

  • Use SOQL inside loops
  • Use DML inside loops
  • Ignore bulk operations
  • Avoid Maps due to confusion

The moment you become comfortable with Maps, your trigger logic becomes:

  • Cleaner
  • Faster
  • More scalable
  • More interview-ready

For Salesforce interviews, mastering Maps is one of the highest ROI skills you can develop.

Because ultimately, Apex trigger development is less about syntax and more about data handling patterns.

--

--