APEX Code Best Practices
Best practice recommended by Salesforce because apex run in a multi-tenant environment, the Apex runtime engine strictly enforces a number of Salesforce Governor limits to ensure that runways apex code does not monopolize shared resources.
1. Bulkify Apex Code
Bellow code only fetch 0 Index record by Trigger.New[0], it’s will work only for single record Insert operation. but it will not work for all records if we try to insert more then one Contacts via Data Loader or Workbench. on. Always try to update only 0 Index record.
Not Bulkified
Trigger ContactTrigger on Contact(before insert){
Contact con= Trigger.New[0];
if(con.Email != null){
// DO someything
}
}
Bulkified:-
Trigger.New is list of records that’s are iterating by for loop, so below code will for bulk records
Trigger ContactTrigger on Contact(before insert){
for(Contact con : Trigger.New){;
if(con.Email != null){
// DO someything
}
}
}
2. Avoid SOQL & DML inside for Loop
Do not place DML or SOQL statements (insert / update / delete / delete) within the loop. because salesforce have governor limit, theses governor limits are a boundary line of execution where ever our code hit the governor limit then we will face error. because Total number of SOQL queries issued 100, if loop is run more then 100 then will face error:-
System.LimitException: Too many SOQL queries: 101 error
To fix the issue, change your code so that the number of SOQL fired is less than 100.
WRONG :-
for(Account account: Trigger.new){
for(Contact contact:[select id from contact where accountId = :account.Id]){
}
}
CORRECT:-
Map<Id, Account> accMap=new Map<id, Account>([select id,name, (select id from contacts) from account where id in:trigger.newmap.keyset()]);
for(Account acc: accMap.values()){
For(Contact con:acc.Contacts){
}
}
3 . Querying Large Data Sets:-
SOQL queries can return 50,000 records. If we are working on large data sets that result in a bulk size limit, in that case the SOQL loop query should be used. Below are the examples.
for( List<Account> accountList: [select id, name from Account where BillingCountry LIKE ‘%USA%’]) {
// Add logic here.
}
Below are the best bractise of SOQL:-
- Building Efficient & Selective Queries
- Avoid Common Causes of Non-Selective SOQL Queries
- Use Query Optimizer
- Avoiding querying on formula fields
- Custom Indexes Containing null Rows
- Avoid SOQL injection
- Optimize SOQL Queries to avoid Timeout Issues
4. Use of Map of Sobject:-
We need to get the value of records from different sobject based on looped sobject record. In that case, we can use Map of Sobjects to get the values and avoid SOQL queries in for loop.
- Use Apex Collections to efficiently query data and store the data in memory
- A combination of using collections and streamlining SOQL queries can substantially help writing efficient Apex code and avoid governor limits
Map<Id, Account> accountMap=new Map<Id, Account> ([select id, name from Account where name like ‘%pvt%’]);
for(Contact con: [select id,AccountId, name from Contact where AccountId in:accountMap.keyset()]{
//Get Related Account data using Contact field
Account acc=accountMap.get(con.AccountId);
}
5. Avoid Hardcoding IDs:-
Don’t hard code Id in Apex Code. When you deploying apex code between sandbox and production it may failed because id may not same.
To avoid this, it is an Apex coding best practice is to create custom settings or custom metadata where one can store the credentials and configuration values and retrieve those values dynamically inside the apex.
6. Use of the Limits Apex Methods:-
Use Apex Limits Methods to Avoid Striking SF Governor Limits. Most of us face the error of the governor limits in starting trigger/ classes/ test classes. The following are just a few of the governor’s limitations: –
1. Too many DML rows 10001
2. Multiple SOQL queries: 101
3. Many query rows 50001.
System.debug('Total No. of SOQL allowed in this Apex code: ' + Limits.getLimitQueries()); // output:- 100
Account account =[select id, name from Account where BillingCountry!=null order by createdDate desc limit 1];
System.debug('1. No. of Queries used in this Apex code so far: ' + Limits.getQueries()); // output:- 99
7. Use Database Methods while doing DML operation
It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records.
// Create two accounts, one of which is missing a required field
Account[] accts = new List<Account>{
new Account(Name='Account1'),
new Account()};
Database.SaveResult[] srList = Database.insert(accts, false);
// Iterate through each returned result
for (Database.SaveResult sr : srList) {
if (sr.isSuccess()) {
// Operation was successful, so get the ID of the record that was processed
System.debug('Successfully inserted account. Account ID: ' + sr.getId());
}
else {
// Operation failed, so get all errors
for(Database.Error err : sr.getErrors()) {
System.debug('The following error has occurred.');
System.debug(err.getStatusCode() + ': ' + err.getMessage());
System.debug('Account fields that affected this error: ' + err.getFields());
}
}
}
8. Exception Handling in Apex Code
Exception handling is the term, used to specify statements, written to ensure code failures are handled gracefully.
public class Example {
public void exampleMethod(){
try {
//Try block of code
//Perform logic here
}catch (Exception e) {
// You can have Optional additional catch statement for other exception types.
// Note that the general exception type, 'Exception'
// Exception must be the last catch block when it is used.
//Perform logic to handle exception
} finally {
// Finally block.
// Must have a finally statement or at least one catch statement
//Perform logic that must happen regardless of if exception occurs
}
}
}
9. Write One Trigger per Object per event
Always prefer to have one trigger per object. In case of multiple triggers per object, you don’t have control over the order of execution for triggers.
10. Use Aync process
Apex written within an asynchronous method gets its own independent set of higher governor limits. For example, the number of SOQL queries is doubled from 100 to 200 queries when using asynchronous calls. The total heap size and maximum CPU time are similarly larger for asynchronous calls.
11. Make reusability of Apex Code
13. Code coverage
Unit tests are the test performed by the developers to ensure that functionality is working as expected considering Both positive & Negative tests. We should not focus on the percentage of code coverage. But it should follow the test class best practices.
- Bulk Records
- Positive & Negative testing.
- Restricted User testing.
- One Assert Statement per method
- should not use @isTest(seeAllData=true)