100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Apex Best Practices

The core disciplines — bulkification, selective SOQL, and layered architecture — that keep Apex code safe at production scale.

PracticeIntermediate9 min readJul 10, 2026
Analogies

Why Apex Best Practices Matter

Apex runs on Salesforce's multi-tenant platform, where every org shares finite computing resources enforced through governor limits — caps on SOQL queries (100 synchronous), DML statements (150), CPU time (10,000ms), and heap size (6MB synchronous). Writing Apex without discipline around these constraints leads to code that works in a sandbox with 5 test records but throws LimitException in production when a user imports 10,000 leads via Data Loader. Best practices exist not as academic style preferences but as direct defenses against runtime failures that only appear at scale.

🏏

Cricket analogy: A bowler who can deliver six perfect yorkers in the nets but has never bowled a full 10-over spell in a real match will break down under sustained IPL pressure, just as untested Apex collapses only when volume — not logic — is the real test.

Bulkify Your Code

Bulkification means writing Apex so that a trigger or method processes an entire collection (List<SObject>) in one pass rather than looping through records one at a time and issuing a query or DML statement per record. The canonical failure is placing a SOQL query or DML operation inside a for loop over Trigger.new — this works fine when one record is updated but throws 'Too many SOQL queries: 101' the moment 200 records are updated in a single Data Loader batch. The fix is to collect IDs into a Set, query once outside the loop, then use a Map for O(1) lookups inside the loop.

🏏

Cricket analogy: A team that sends a separate runner to fetch water for each batsman individually wastes overs, while one drinks break serving the whole XI at once is the efficient move — bulkified Apex queries data once for the whole batch instead of once per record.

Use Selective, Efficient SOQL

Selective SOQL means the WHERE clause filters on indexed fields (Id, Name in some cases, external ID fields, or custom fields marked as indexed) so the query optimizer can use an index instead of a full table scan, which matters once an object holds millions of rows. Avoid over-fetching by listing only the fields actually used downstream, since developers often list every field on the object out of laziness, bloating heap size unnecessarily. Salesforce also flags a query non-selective in the Query Plan tool if it would scan more than roughly the greater of 200,000 rows or a percentage threshold of the table, which is exactly the profiling you should do before shipping a query in a high-volume org.

🏏

Cricket analogy: A scorer who flips through every page of a decade's scorebook to find one player's strike rate against spin, instead of jumping straight to the indexed statistics table, wastes minutes a TV broadcast doesn't have — a non-selective SOQL query scans the whole table instead of using an index.

apex
// One trigger per object, delegating to a handler
trigger AccountTrigger on Account (before insert, after update) {
    new AccountTriggerHandler().run();
}

public class AccountTriggerHandler {
    public void run() {
        if (Trigger.isBefore && Trigger.isInsert) {
            AccountService.setDefaultRatings(Trigger.new);
        }
        if (Trigger.isAfter && Trigger.isUpdate) {
            AccountService.syncHotAccounts(Trigger.newMap, Trigger.oldMap);
        }
    }
}

Separate Logic Layers (Trigger, Handler, Service)

Salesforce's own recommended pattern is one trigger per object that delegates immediately to a handler class, which in turn calls stateless service or domain classes containing the actual business logic — this keeps the trigger itself to a single line like TriggerHandlerFactory.execute() and makes logic unit-testable independent of DML. Mixing business rules directly into trigger bodies means every future change requires touching a fragile, hard-to-test file, and having multiple triggers on the same object creates non-deterministic execution order that is nearly impossible to debug. This layered architecture mirrors standard enterprise separation of concerns and is one of the most tested patterns in Apex certification exams and interviews.

🏏

Cricket analogy: A captain doesn't personally bowl every over, set every field, and bat at every position — they delegate to specialist bowlers and fielders while retaining strategic control, just as a trigger delegates to a handler instead of containing all logic itself.

  • Governor limits (100 SOQL queries, 150 DML statements, 10,000ms CPU time) force disciplined Apex design, not just style preference.
  • Always bulkify: collect record data into Lists/Sets/Maps and issue exactly one SOQL query and one DML statement per transaction, never inside a loop.
  • Write selective SOQL by filtering on indexed fields (Id, external IDs, indexed custom fields) and query only the fields you need.
  • Follow the one-trigger-per-object pattern, delegating immediately to a handler class and pushing business logic into testable service classes.
  • Use maps keyed by Id (e.g., Trigger.oldMap) for O(1) lookups instead of nested loops.
  • Guard recursive trigger execution with static Boolean flags to prevent duplicate processing within the same transaction.
  • Write unit tests with realistic bulk data volumes (200 records) to catch governor-limit violations before they reach production.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ApexSalesforceStudyNotes#ApexBestPractices#Apex#Matter#Bulkify#Code#StudyNotes#SkillVeris#ExamPrep