Introduction
This post outlines essential Apex best practices for Salesforce developers preparing for interviews or working on production solutions. It covers performance, maintainability, testing, and security — all framed with practical tips you can apply immediately.
Why Apex Best Practices Matter
Apex runs on a multi-tenant platform with strict governor limits. Following best practices prevents runtime exceptions, improves performance, simplifies maintenance, and ensures secure, scalable solutions. Keywords: Apex best practices, Salesforce Apex, governor limits, bulkification.
Core Best Practices
1. Bulkify Your Code
Always design Apex to handle multiple records. Avoid SOQL or DML inside loops. Use collections (List, Set, Map) and perform single SOQL and DML operations wherever possible.
// Bad: SOQL inside loop
for(Account a : accounts){
Contact c = [SELECT Id FROM Contact WHERE AccountId = :a.Id LIMIT 1];
// ...
}
// Good: Single query with map
Map
for(Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]){
accountToContact.put(c.AccountId, c);
}
2. One Trigger per Object (Trigger Framework)
Implement a single trigger per sObject and delegate logic to handler/service classes. This keeps triggers thin and easier to debug and maintain.
// trigger AccountTrigger on Account (before insert, before update){
// AccountTriggerHandler.handle(Trigger.new, Trigger.oldMap);
// }
3. Use Efficient SOQL
Select only required fields, use selective WHERE clauses, and leverage indexed fields. Avoid wildcard SELECT * patterns — select the fields you need.
4. Avoid Unnecessary DML
Accumulate records to update in a List and perform a single DML statement. Use Database.update(records, false) when partial success is acceptable and handle SaveResult errors.
5. Use Collections and Maps for Relationship Handling
Maps provide O(1) access and reduce nested loops. Use them to relate parent-child records efficiently.
6. Respect Governor Limits & Monitor Usage
Use the Limits class (Limits.getQueries(), Limits.getDMLStatements(), etc.) and write code to work well within limits. Consider using batchable or queueable Apex for large data sets.
7. Use Asynchronous Processing When Appropriate
Use Queueable, Batchable, @future, and Scheduled Apex to process large volumes or long-running work. Prefer Queueable over @future for complex jobs and chaining.
8. Implement Proper Error Handling & Logging
Catch predictable exceptions, log meaningful messages, and avoid swallowing exceptions silently. Use custom exceptions and centralized logging (Custom Object or Platform Events) for traceability.
9. Enforce CRUD/FLS and Sharing
Respect field- and object-level security. Use Security.stripInaccessible in new code and enforce sharing using with sharing/without sharing appropriately in classes.
10. Testing Best Practices
Write comprehensive unit tests with @isTest, use @TestSetup to create common test data, set SeeAllData=false, and aim for meaningful assertions. Test bulk scenarios, positive & negative cases, and exception handling.
@isTest
private class AccountServiceTest {
@TestSetup
static void setup(){
// Create reusable test data
}
static testMethod void testBulkUpdate(){
// Insert 200 records, run the logic, assert results
}
}
11. Keep Methods Small & Single-Responsibility
Follow clean code principles: small methods, clear naming, and separation of concerns. This improves readability and testability.
12. Avoid Hardcoding & Use Custom Metadata/Settings
Place configurable values in Custom Metadata Types or Custom Settings. This enables declarative updates and avoids repeated code changes.
13. Use Describe Calls Sparingly
Describe calls (Schema.sObjectType) are useful but can be expensive. Cache describe results where possible and avoid excessive usage in loops.
14. Optimize for Heap & CPU Time
Be mindful of heap size and CPU usage. Use transient for Visualforce controller properties that shouldn’t be serialized, use streaming/limits for large string operations, and avoid loading unneeded data.
Sample Trigger Handler Pattern
public with sharing class AccountTriggerHandler {
public static void handle(List
// Collect ids
Set
for(Account acc : newList){ accountIds.add(acc.Id); }
// Query related records once
Map
for(Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]){
acctToContact.put(c.AccountId, c);
}
// Business logic
List
for(Account acc : newList){
// ... modify acc and add to toUpdate
}
if(!toUpdate.isEmpty()) update toUpdate;
}
}
Interview Tips
When answering interview questions, explain why each practice matters (governor limits, maintainability), give a short code sample, and mention trade-offs (e.g., describe call costs vs. dynamic behavior). Be ready to discuss real-world examples where following these practices prevented issues.
Conclusion
Following Apex best practices ensures your Salesforce applications are secure, scalable, and maintainable. Emphasize bulkification, proper trigger frameworks, efficient SOQL/DML usage, robust testing, and respect for platform limits in interviews and real projects.
Leave a Reply