Best Practices & Common Pitfalls
42.1 Coding standards
One trigger per object, logic in handler/service classes.
Bulkify everything: collections in, no SOQL/DML in loops, maps for lookups.
Declare sharing on every class (with sharing by default, inherited sharing for reusable code, without sharing only with a documented reason).
Enforce CRUD/FLS with user mode or stripInaccessible.
Never hard-code IDs, URLs, or credentials — use custom metadata, Named Credentials, and describe calls.
Keep methods small; name classes by role (AccountService, AccountsSelector, AccountTriggerHandler, AccountServiceTest).
Use the latest API version for new classes and periodically upgrade old ones.
Handle errors deliberately; log them persistently.
Write tests that assert behavior, cover bulk and negative paths, and run in parallel.
Run static analysis in CI; review code with a checklist.
42.2 Common pitfalls
SOQL/DML inside loops Hits 100 SOQL / 150 DML limits Query once, use maps, DML once Hard-coded record IDs Break across orgs/sandboxes Query by DeveloperName, custom metadata Multiple triggers per object One trigger + handler framework Static Boolean recursion flags Skip records after the first 200
Track processed IDs or compare old/new values Ignoring FLS/CRUD Data leaks; AppExchange review failure
WITH USER_MODE, as user, stripInaccessible Dynamic SOQL with concatenated input
Bind variables, queryWithBinds, allowlists SeeAllData=true tests Fragile, environment-dependent tests Test data factories, @testSetup Tests without assertions Coverage without confidence Meaningful Assert calls Callouts after DML "Uncommitted work pending" Callout first or async
Swallowing exceptions (catch (Exception e) {}) Silent data corruption Handle, log, or rethrow Non-selective queries on large objects Timeouts, trigger failures Indexed filters, query plan Using @future for everything
No job ID, no chaining, primitive params Queueable with finalizers Heavy logic in Schedulable execute Locked class, hard to redeploy
Schedulable only starts a batch/queueable global everywhere Locks API forever in managed packages public unless cross-namespace Large view state / heap Slow pages, heap limit errors
transient, SOQL for loops, smaller queries Mixed DML in one transaction Separate via async or runAs in tests