Skip to main content

How to Avoid Errors in Salesforce: 6 Essential Best Practices for Developers & Admins

In plain words: Avoiding errors in Salesforce comes down to writing bulkified code that respects Governor Limits, testing with realistic data volumes, logging failures systematically, and enforcing strict data security rules across your org.

Salesforce is a multi-tenant cloud platform, meaning every transaction shares memory, CPU time, and database resources with other processes. When custom automation, Apex triggers, or integrations aren't built defensively, teams run into unhandled exceptions, locking issues, and broken user workflows. Following disciplined architecture principles keeps your org healthy and error-free.

1. Adopt a Bulkified Testing Strategy

Unit testing in Salesforce isn't just about reaching the 75% deployment threshold—it's about validating how your code behaves under peak transaction loads. A robust testing approach validates positive paths, negative edge cases, and 200-record batch triggers.

  • Test for 200 Records: Never write unit tests that insert only a single record. Always test triggers and batch handlers with at least 200 records to expose bulkification defects.
  • Use the Modern Assert Namespace: Replace legacy System.assert() statements with the strongly typed Assert.areEqual() and Assert.isTrue() methods for clearer error diagnostics.
  • Isolate Test Contexts: Wrap execution blocks in Test.startTest() and Test.stopTest() to reset governor limits and test asynchronous processes reliably.

2. Respect Salesforce Governor Limits

Governor limits prevent runaway transactions from hogging multi-tenant resources. Most production errors stem from unbulkified code hitting hard transaction thresholds.

  • No SOQL/DML Inside Loops: Moving queries and updates outside of for loops prevents the fatal System.LimitException: Too many SOQL queries: 101.
  • Enforce SOQL Selectivity: Filter large data volumes using indexed fields (like Id, Name, or custom External IDs) to avoid non-selective query runtime timeouts.
  • Leverage Asynchronous Apex: Delegate CPU-heavy workloads, external API calls, and large calculations to Queueable Apex or Batch Apex.
Real-World Example: Bulkified Processing & Custom Error Logging
Collecting record IDs in a Set, querying once, and persisting caught errors without rolling back the entire user transaction:
public with sharing class AccountProcessor {
    public static void updateVipStatus(Set<Id> accountIds) {
        if (accountIds == null || accountIds.isEmpty()) {
            return;
        }

        List<Account> accountsToUpdate = new List<Account>();

        // 1. Single SOQL Query outside loops with User Mode enforcement
        for (Account acc : [SELECT Id, AnnualRevenue, Is_VIP__c FROM Account WHERE Id IN :accountIds WITH USER_MODE]) {
            if (acc.AnnualRevenue >= 1000000 && !acc.Is_VIP__c) {
                acc.Is_VIP__c = true;
                accountsToUpdate.add(acc);
            }
        }

        // 2. Partial Success DML with graceful error capture
        if (!accountsToUpdate.isEmpty()) {
            Database.SaveResult[] results = Database.update(accountsToUpdate, false, AccessLevel.USER_MODE);
            for (Integer i = 0; i < results.size(); i++) {
                if (!results[i].isSuccess()) {
                    for (Database.Error err : results[i].getErrors()) {
                        System.debug(LoggingLevel.ERROR, 'Failed Account ID: ' + accountsToUpdate[i].Id + ' | Error: ' + err.getMessage());
                    }
                }
            }
        }
    }
}
360 Architecture Governance Card:
  • Synchronous SOQL Limit: 100 queries per transaction (200 in asynchronous).
  • Total DML Statements: Maximum 150 DML calls affecting up to 10,000 records.
  • Apex CPU Time Limit: 10,000 ms (synchronous) / 60,000 ms (asynchronous).
  • Heap Size Limit: 6 MB (synchronous) / 12 MB (asynchronous).

3. Implement Resilient Error Handling & Logging

Unhandled exceptions disrupt user workflows and result in lost data. Building a defensive error-handling strategy allows applications to fail gracefully and self-diagnose issues quickly.

  • Use Partial-Success DML: Use Database.insert(records, false) instead of plain DML statements so valid records succeed even if individual rows fail validation rules.
  • Centralize Exception Logging: Capture caught exceptions into a custom object (Error_Log__c) or an event stream via Platform Events to preserve error details when standard database rollbacks occur.
  • Avoid Silent Catch Blocks: Catching an exception without logging or displaying meaningful user notifications makes production bugs nearly impossible to trace.

4. Enforce Security & Permission Boundaries

Security misconfigurations lead to both data vulnerability and runtime authorization failures when users access restricted fields.

  • Query with User Mode: Enforce Field-Level Security (FLS) and Object-Level Permissions natively in SOQL queries using the WITH USER_MODE clause.
  • Use Named Credentials: Never hardcode API keys, client secrets, or integration credentials in Apex classes or custom settings.
  • Declare Class Sharing Modes: Explicitly declare every Apex class as with sharing, without sharing, or inherited sharing to prevent unintentional privilege escalation.

5. Common Traps & Proactive Maintenance

Developer Trap: Mixing Callouts and DML Operations
Executing database operations (such as record inserts) before an HTTP callout in the same synchronous transaction causes a runtime exception: System.CalloutException: You have uncommitted work pending. Structure all external callouts before DML or offload them to Queueable Apex.
Core Takeaway: Write code that plans for scale from day one—always bulkify collections, enforce User Mode security, and log errors to dedicated monitoring objects rather than relying solely on debug logs.
  • Run Regular Health Checks: Use Salesforce Optimizer and the Health Check dashboard to detect unused fields, over-privileged profiles, and technical debt.
  • Monitor Org Limits: Use the Salesforce Limits API to set up proactive alerts before your org reaches its 24-hour API request or streaming event limits.
  • Engage with the Community: Leverage the Salesforce Trailblazer Community and official Developer documentation to keep up with seasonal release updates and retired features.

Summary

Maintaining a reliable, error-resistant Salesforce environment requires a mix of clean coding standards, proactive limit management, and disciplined monitoring. By testing with realistic bulk data, querying securely with User Mode, and handling exceptions gracefully, you protect your org from downtime and deliver a seamless experience to your users.