Apex Best Practices in Salesforce — Interview Guide

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 accountToContact = new 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 newList, Map oldMap){
// Collect ids
Set accountIds = new Set();
for(Account acc : newList){ accountIds.add(acc.Id); }

// Query related records once
Map acctToContact = new Map();
for(Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]){
acctToContact.put(c.AccountId, c);
}

// Business logic
List toUpdate = new 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.