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 typedAssert.areEqual()andAssert.isTrue()methods for clearer error diagnostics. - Isolate Test Contexts: Wrap execution blocks in
Test.startTest()andTest.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
forloops prevents the fatalSystem.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.
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());
}
}
}
}
}
}
- 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_MODEclause. - 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, orinherited sharingto prevent unintentional privilege escalation.
5. Common Traps & Proactive Maintenance
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.
- 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.