Writing efficient, scalable, and maintainable Apex code is critical to the long-term success of your Salesforce org. Below are essential Apex best practices, explained with “Do this ✅” and “Don’t do this ❌” examples.
1. 🧠 Bulkify Your Code
❌ Don’t: Process records one at a time.
public class AccountHelper {
public static void updateNames(List<Id> ids) {
for (Id id : ids) {
Account acc = [SELECT Id, Name FROM Account WHERE Id = :id];
acc.Name += ' - Updated';
update acc;
}
}
}
✅ Do: Query and update records in bulk.
public class AccountHelper {
public static void updateNames(List<Id> ids) {
List<Account> accList = [SELECT Id, Name FROM Account WHERE Id IN :ids];
for (Account acc : accList) {
acc.Name += ' - Updated';
}
update accList;
}
}
2. 🔁 Avoid DML or SOQL in Loops
❌ Don’t:
for (Contact c : contacts) {
insert c;
}
✅ Do:
insert contacts;
3. 📦 Use Maps for Efficient Lookups
❌ Don’t: Re-query inside loops.
for (Opportunity opp : oppList) {
Account acc = [SELECT Name FROM Account WHERE Id = :opp.AccountId];
opp.Description = acc.Name;
}
✅ Do: Use Maps to avoid redundant queries.
Set<Id> accIds = new Set<Id>();
for (Opportunity opp : oppList) {
accIds.add(opp.AccountId);
}
Map<Id, Account> accMap = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :accIds]
);
for (Opportunity opp : oppList) {
opp.Description = accMap.get(opp.AccountId).Name;
}
4. 📂 One Trigger Per Object
❌ Don’t: Create multiple triggers on the same object.
🛑 This causes unpredictable execution order.
✅ Do: Use a single trigger and delegate logic to a handler class.
trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler.handle(Trigger.new, Trigger.isInsert);
}
5. 🧰 Use Trigger Handlers
❌ Don’t: Put logic directly in the trigger.
trigger ContactTrigger on Contact (before insert) {
for (Contact con : Trigger.new) {
con.Description = 'New Contact';
}
}
✅ Do: Use a separate handler class.
trigger ContactTrigger on Contact (before insert) {
ContactTriggerHandler.beforeInsert(Trigger.new);
}
public class ContactTriggerHandler {
public static void beforeInsert(List<Contact> newContacts) {
for (Contact con : newContacts) {
con.Description = 'New Contact';
}
}
}
6. 🧪 Write Effective Test Classes
❌ Don’t: Write tests without asserts.
@isTest
static void testInsert() {
Account acc = new Account(Name='Test');
insert acc;
}
✅ Do: Validate actual outcomes with assertions.
@isTest
static void testInsert() {
Account acc = new Account(Name='Test');
insert acc;
Account inserted = [SELECT Name FROM Account WHERE Id = :acc.Id];
System.assertEquals('Test', inserted.Name);
}
7. 🔐 Avoid Hardcoding IDs
❌ Don’t:
if (UserInfo.getProfileId() == '00e1x000000abcD') {
// logic
}
✅ Do:
Profile p = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1];
if (UserInfo.getProfileId() == p.Id) {
// logic
}
8. ⏳ Use Asynchronous Apex for Long-Running Jobs
❌ Don’t: Do heavy operations synchronously.
public void sendEmails() {
// Loops with large DML or callouts
}
✅ Do: Use @future, Queueable, or Batch Apex.
@future
public static void sendEmailsAsync() {
// Perform callout/DML
}
9. ⚠️ Handle Exceptions Gracefully
❌ Don’t:
update accList; // Might throw DmlException
✅ Do:
try {
update accList;
} catch (DmlException e) {
System.debug('Error: ' + e.getMessage());
}
10. 🔂 Prevent Trigger Recursion
❌ Don’t: Let logic run endlessly due to updates within triggers.
✅ Do:
public class RecursionControl {
public static Boolean hasRun = false;
}
if (!RecursionControl.hasRun) {
RecursionControl.hasRun = true;
// update logic
}
11. 🧼 Avoid Nested Loops for Performance
❌ Don’t:
for (Account acc : accounts) {
for (Contact con : contacts) {
if (con.AccountId == acc.Id) { ... }
}
}
✅ Do:
Map<Id, List<Contact>> accountContacts = new Map<Id, List<Contact>>();
for (Contact con : contacts) {
if (!accountContacts.containsKey(con.AccountId)) {
accountContacts.put(con.AccountId, new List<Contact>());
}
accountContacts.get(con.AccountId).add(con);
}
12. 🛡️ Defensive Programming
❌ Don’t: Assume data always exists.
update accounts;
✅ Do:
if (!accounts.isEmpty()) {
update accounts;
}
13. 🧾 Follow Naming Conventions & Document Code
❌ Don’t: Use vague variable names.
List<Account> a1 = new List<Account>();
✅ Do:
List<Account> customerAccounts = new List<Account>();
✅ Add Comments:
// Set default value before insert
acc.Status__c = 'New';
Final Word 💬
These best practices ensure:
- Scalability and performance
- Readability and maintainability
- Fewer bugs and better testability


2 Responses
How can I connect with you to know more?
Please connect here: https://www.linkedin.com/in/aman-garg-417426149/