Salesforce Apex, SOQL & Test Class Best Practices
About This Cheat Sheet
Hey there! Welcome to your complete guide to Apex, SOQL & Test Class Best Practices for Salesforce development. I've put together everything you need to know โ from Apex Best Practices (Bulkification, Avoid SOQL in Loops, Collections, Governor Limits, Exception Handling, Separation of Concerns) to SOQL Best Practices (Selective Queries, Indexing, Bind Variables, Security Enforcement) and Test Class Best Practices (@isTest, Test.startTest/stopTest, System.runAs, Assert Methods). Whether you're preparing for interviews, certifications, or building production-grade applications, this cheat sheet covers common mistakes, real-world examples, and shortcut techniques to help you write clean, efficient, and maintainable code.
โก Apex Best Practices
- Bulkification - Process multiple records at once
- Avoid SOQL in Loops - Query outside loops
- Use Collections - Maps, Sets, Lists
- Exception Handling - Try-Catch blocks
- Separation of Concerns - Separate business logic
- Static Methods - Utility classes
- Avoid Hardcoding - Custom Labels, Settings
- Governor Limits - Stay within limits
๐ SOQL Best Practices
- SELECT Required Fields - Only what you need
- Use WHERE Clauses - Filter at database level
- Bind Variables - Prevent SOQL injection
- WITH SECURITY_ENFORCED - Enforce FLS & sharing
- Use LIMIT - Limit query results
- FOR Loop - Process query results
- Avoid Formula Fields - In WHERE clause
- Selective Queries - Use indexed fields
๐งช Test Class Best Practices
- @isTest - Mark test classes
- Test.startTest/stopTest - Isolate execution
- System.runAs() - Test user permissions
- SeeAllData=False - Isolated test data
- Assert Methods - Verify outcomes
- @testSetup - Create test data
- Test Positive/Negative - Both scenarios
- Bulk Testing - Test bulk scenarios
๐ซ Common Mistakes
- SOQL in Loops - Governor limit violation
- Hardcoding IDs - Use constants
- Nested Loops - Performance issues
- Inefficient DML - DML in loops
- No Exception Handling - Unhandled errors
- No Tests - Code without test coverage
- SeeAllData=True - Coupled tests
- No Bulk Testing - Fails in production
๐ Governor Limits
- SOQL Queries - 100 per transaction
- DML Statements - 150 per transaction
- Heap Size - 6 MB (Sync) / 12 MB (Async)
- CPU Time - 10,000 ms
- Records - 50,000 per DML
- Total Records - 10,000 (Sync) / 50,000 (Async)
- SOQL Rows - 50,000 rows
- Callouts - 100 per transaction
๐ Key Takeaways
- Think Bulk - Always process multiple records
- Query Outside Loops - Fetch once, use many
- Use Maps - For efficient lookups
- Test Everything - 75%+ coverage required
- Security First - WITH SECURITY_ENFORCED
- Handle Exceptions - Always use try-catch
- No Hardcoding - Use Custom Labels/Settings
- Optimize Queries - Use selective filters
โก Bulk
Process multiple records
Always think bulk๐ SOQL
Query outside loops
Use Bind Variables๐งช Tests
@isTest annotation
75%+ coverage๐ซ Avoid
SOQL in loops
Hardcodingโก Apex Best Practices
๐ Practice
- Bulkification
- Avoid SOQL in Loops
- Use Collections
- Exception Handling
- Separation of Concerns
- Static Methods
- Avoid Hardcoding
- Governor Limits
๐ Description
- Process multiple records at once
- Query outside loops to avoid governor limits
- Maps, Sets, Lists for efficient data handling
- Try-Catch blocks for error handling
- Separate business logic from triggers
- Use utility classes for common operations
- Use Custom Labels, Custom Settings, Custom Metadata
- Stay within limits
๐ก Example
- DML on List not one by one
- Map
accMap = new Map (); - Map
accMap = new Map (); - try { ... } catch (Exception e) { ... }
- Use Handler classes
- public static void updateAccounts()
- CustomLabel__c or Custom_Setting__c
- SOQL queries โค 100 per transaction
โ Check
- Always process List
- No SOQL inside for loops
- Use Map for lookups
- Catch all exceptions
- Separate logic from triggers
- Use static for utilities
- No hardcoded IDs
- Monitor CPU, Heap, Query limits
๐ SOQL Best Practices
๐ Practice
- SELECT Required Fields
- Use WHERE Clauses
- Use Bind Variables
- WITH SECURITY_ENFORCED
- Use LIMIT
- Use FOR Loop
- Avoid Formula Fields in WHERE
- Selective Queries
๐ Description
- Only query fields you need
- Filter data at database level
- Prevent SOQL injection
- Enforce FLS and sharing rules
- Limit query results
- Efficiently process query results
- Formula fields can't use indexes
- Use indexed fields for filtering
๐ก Example
- SELECT Id, Name FROM Account
- WHERE Name LIKE 'Acme%'
- :accountName
- SELECT Id FROM Account WITH SECURITY_ENFORCED
- LIMIT 10000
- for (Account acc : [SELECT Id FROM Account])
- Avoid: WHERE Discount__c > 10
- Use fields with indexes
โ Check
- No SELECT *
- Filter in query
- Use :variable
- Always use WITH SECURITY_ENFORCED
- Limit results
- Use for loops
- Check field indexes
- Use custom indexes
๐งช Test Class Best Practices
| ๐ Practice | ๐ Description | ๐ก Example |
|---|---|---|
| @isTest Annotation | Mark test classes and methods | @isTest class MyTest { } |
| Test.startTest/stopTest | Isolate test execution | Test.startTest(); // code Test.stopTest(); |
| System.runAs() | Test with different user permissions | System.runAs(user) { // code } |
| SeeAllData=False | Create isolated test data | @isTest(SeeAllData=false) |
| Assert Methods | Verify expected outcomes | System.assertEquals(expected, actual); |
| @testSetup | Create test data once, reuse | @testSetup static void setup() { } |
| Test Positive/Negative | Test both success and failure | Test positive: success, negative: failure |
| Bulk Testing | Test bulk scenarios | List |
| Test.setMock() | Mock external API calls | Test.setMock(WebServiceMock.class, new MyMock()); |
| Database.Saveresult | Test DML partial success | Database.SaveResult[] results = Database.insert(records, false); |
๐ Governor Limits
๐ Limit
- SOQL Queries
- DML Statements
- Heap Size (Sync)
- Heap Size (Async)
- CPU Time
- Records per DML
- Total Records (Sync)
- Total Records (Async)
- SOQL Rows
- Callouts
๐ Limit Value
- 100 per transaction
- 150 per transaction
- 6 MB
- 12 MB
- 10,000 ms
- 50,000 per DML
- 10,000 records
- 50,000 records
- 50,000 rows
- 100 per transaction
๐ก Best Practice
- Query outside loops
- Batch DML operations
- Use efficient data types
- Monitor heap usage
- Optimize code performance
- Batch processing
- Use Batch Apex
- Use Batch Apex
- Use LIMIT and WHERE
- Use async callouts
๐ซ Common Mistakes to Avoid
โ Mistake
- SOQL in Loops
- Hardcoding IDs
- Nested Loops
- Inefficient DML
- No Exception Handling
- No Tests
- SeeAllData=True
- No Bulk Testing
- No Security Enforcement
- Formula Fields in WHERE
โ ๏ธ Problem
- Governor limit violation
- Fails in different orgs
- Performance issues
- DML limit exceeded
- Unhandled errors
- No test coverage
- Couples tests to org data
- Fails in production
- Security vulnerabilities
- No index usage
โ Solution
- Query outside loops
- Use Constants or Custom Settings
- Use Maps for lookups
- Batch DML operations
- Try-Catch blocks
- Write test classes
- SeeAllData=False
- Test with bulk data
- WITH SECURITY_ENFORCED
- Use indexed fields
๐ก Example
- Map
accMap = new Map (); - final String ACCOUNT_ID = '001...';
- for (Id key : map.keySet())
- Database.update(accounts, false);
- try { } catch (Exception e) { }
- @isTest class MyTest { }
- @isTest(SeeAllData=false)
- List
accounts = new List (); - SELECT Id FROM Account WITH SECURITY_ENFORCED
- WHERE CreatedDate = TODAY
๐ป Real-World Examples
// โ GOOD: Bulkified Trigger Handler public class AccountTriggerHandler { public static void updateRelatedContacts(ListnewList) { // Query once outside loop Map accMap = new Map (newList); List contactsToUpdate = new List (); for (Contact con : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accMap.keySet()]) { con.AccountId = accMap.get(con.AccountId).Id; contactsToUpdate.add(con); } if (!contactsToUpdate.isEmpty()) { try { update contactsToUpdate; // Single DML } catch (Exception e) { System.debug(e.getMessage()); } } } } // โ GOOD: SOQL with Security Enforcement List accounts = [SELECT Id, Name FROM Account WITH SECURITY_ENFORCED]; // โ GOOD: Test Class @isTest public class AccountTest { @testSetup static void setup() { Account acc = new Account(Name = 'Test'); insert acc; } @isTest static void testBulkify() { List testAccounts = new List (); for (Integer i = 0; i < 200; i++) { testAccounts.add(new Account(Name = 'Bulk' + i)); } Test.startTest(); insert testAccounts; Test.stopTest(); List results = [SELECT Id FROM Account WHERE Name LIKE 'Bulk%']; System.assertEquals(200, results.size()); } }
๐ฏ Best Practices Quick Revision
The Golden Rule: Always think bulk, query outside loops, use collections, and test everything. Security and performance are the foundation of production-grade Salesforce applications.
Quick Memory Tricks:
- ๐ Apex: "Bulk, Collections, Exception, Separation"
- ๐ SOQL: "Selective, Security, Bind, Limit"
- ๐ Tests: "Isolated, Assert, Setup, Bulk"
- ๐ Limits: "100 Queries, 150 DML, 50k Rows"
- ๐ Security: "WITH SECURITY_ENFORCED, FLS, Sharing"
Checklist Before Deployment:
- โ No SOQL/SOSL in loops
- โ DML operations are bulkified
- โ Exception handling for all DML
- โ No hardcoded IDs
- โ WITH SECURITY_ENFORCED for all SOQL
- โ Test coverage โฅ 75%
- โ All test methods use SeeAllData=False
- โ Bulk scenarios tested
- โ Governor limits not exceeded
- โ Code is optimized for performance
๐ก Pro Tips for Best Practices
โ Always Think Bulk
Salesforce processes records in batches. Always design code that works with collections, not individual records.
โ Query Once, Use Many
Fetch all required data in a single SOQL query, then use Maps for efficient lookups throughout the transaction.
โ Test Everything
Test positive scenarios (success), negative scenarios (failure), and bulk scenarios (large data sets).
โ Use Security Enforcement
Always use WITH SECURITY_ENFORCED in SOQL queries to enforce FLS and sharing rules.
โ Handle Exceptions Gracefully
Always use try-catch blocks for DML operations and external calls. Log errors and show user-friendly messages.
โ Write Readable Code
Use meaningful variable names, add comments for complex logic, and follow Salesforce coding conventions.
๐ Quick Reference: Common Mistakes Summary
โ Mistake
- SOQL in Loops
- Hardcoding IDs
- Nested Loops
- Inefficient DML
- No Exception Handling
- No Tests
- SeeAllData=True
- No Bulk Testing
- No Security Enforcement
- Formula Fields in WHERE
โ ๏ธ Risk
- Governor Limit Violation
- Breaks in Different Orgs
- Performance Issues
- DML Limit Exceeded
- Unhandled Errors
- No Test Coverage
- Test Data Dependency
- Fails in Production
- Security Vulnerability
- No Index Usage
โ Fix
- Query Outside Loops
- Use Constants/Custom Settings
- Use Maps for Lookups
- Batch DML Operations
- Try-Catch Blocks
- Write Test Classes
- SeeAllData=False
- Test with Bulk Data
- WITH SECURITY_ENFORCED
- Use Indexed Fields
๐ Quick Reference: SOQL Best Practices
๐ Practice
- SELECT Required Fields
- Use WHERE Clauses
- Use Bind Variables
- WITH SECURITY_ENFORCED
- Use LIMIT
- Use FOR Loop
- Use IN Operator
- Avoid Formula Fields
๐ Why
- Reduce heap usage
- Filter at database level
- Prevent SOQL injection
- Enforce FLS and sharing
- Limit results
- Efficient processing
- Filter by multiple values
- Can't use indexes
๐ก Example
- SELECT Id, Name FROM Account
- WHERE Name LIKE 'Acme%'
- :accountName
- WITH SECURITY_ENFORCED
- LIMIT 10000
- for (Account a : [SELECT Id FROM Account])
- WHERE Id IN :idSet
- WHERE Discount__c > 10
โ Check
- No SELECT *
- Filter in query
- Use :variable
- Always use
- Limit results
- Use for loops
- Use Sets
- Check field indexes
Download Cheat Sheet
Click the button below to download this high-resolution PNG cheat sheet.
Download PNG (1 MB)Preview
Still have questions?
We're here to help! Reach out to our support team for any queries.