3

Apex Code Best Practices

In this post we will talk about Salesforce apex best practices. Apex code is used to write custom and robust business logic. As with any language, there are key coding principles and best practices that will help you write efficient, scalable code. In this post we will talk about best practices for writing designing Apex Code.

Why we need Apex Code best practices

Best practice recommended by Salesforce because apex run in a multitenant environment, the Apex runtime engine strictly enforce a number of limits to ensure that runways apex code does not monopolize shared resources.

Lets talk about some example of Salesforce apex code best practices.

1. Bulkify Apex Code


Very first principle is write code for more then one record at a time. We should write scalable code and avoid hitting governor. Here is example of bad code.

Account acc= Trigger. New [0];

Solution

In above line, the code explicitly access only the first record in the trigger.new collection by using the syntax Trigger.New[0]. Instead, trigger should properly handle the entire collection of Account using Trigger.new collection.

for(Account acc: trigger.New){
// add your logic here
}

2. Avoid SOQL & DML inside for Loop


Do not place SOQL or DML(insert/update/delete/undelete) statments inside a loop. When these operations are placed inside a for loop, database operations are invoked once per iteration of the loop making it very easy to reach these SFDC governor limits.

For(Account acc: Trigger.new){
 for(Contact con:[select id from contact where accountId = :acc.Id]){
 }
}

Solution

  1. Solution: Move SOQL/DML out of loops
  2. Query: If you need query results, get all the records using a single query and iterate over the resultset
  3. Update: If you need to update, batch up the data into a collection and invoke DML once for that collection

Here is example of puting SOQL Query outside of for loop.

Map<Id, Account> accountMap=new Map<id, Account>([select id,name, (select id from contacts) from account where id in:trigger.newmap.keyset()]);

for(Account acc: accountMap.values()){
	For(Contact con:acc.Contacts){ 
	}
}

3. Querying Large Data Sets


In Salesforce we have a governance limit that SOQL queries can returns 50,000 records. If we are working on large data sets which cause exceed heap size limit, in that case SOQL query for loop must be used. Here is example of SOQL query for loop as in one the following examples.

Solution : Use a SOQL query for loop.

for( List<Account> accList: [select id, name from Account where BillingCountry  LIKE ‘%United%’]) {
	// add your logic here.
}

Check this post to learn about SOQL Best Practices. Here are best practices

  1. Building Efficient & Selective Queries
  2. avoid Common Causes of Non-Selective SOQL Queries
  3. use Query Optimizer
  4. Avoiding querying on formula fields
  5. Custom Indexes Containing null Rows
  6. avoid SOQL injection.

Optimize SOQL Queries to avoid Timeout Issues

  • While Querying unnecessary records and fields, the transaction will take additional time to retrieve the result from the SOQL query. This might lead to a Timeout issues.
  • Sometimes, these small mistakes lead to issues which can take a lot of time and effort in debugging and fixing. 
  • As an Apex coding best practice,  In SOQL queries, avoid querying columns that are not used in the code – e.g. In a process, if the need is only the LastName of the contact, query only the last name, there is no need to query all the fields from the contact record.
  • Similarly query the required number of records using right filters and joins so that unwanted records are not retrieved

4. Use of Map of Sobject


In few cases, 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.

Solution

  1. Use Apex Collections to efficiently query data and store the data in memory
  2. 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 ‘%Inc.%’]);

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. Use of the Limits Apex Methods


Use Apex Limts Methods to Avoid Hitting SF Governor Limits. Many of us facing governor limit error in trigger/classes/test classes. Few of the governor limit errors as follow:-

  1. Too many SOQL queries: 101
  2. Too many dml rows 10001
  3. too many query rows 50001.

Here is the snippet to how to use Apex limit Apex methods.

System.debug('Total Number of SOQL allowed in this Apex code: ' +  Limits.getLimitQueries());

Account acc=[select id, name from Account where BillingCity!=null  order by createdDate desc limit 1];

System.debug('1. Number of Queries used in this Apex code so far: ' + Limits.getQueries());

Now, using above Limit methods we can check how many SOQL queries we can issue in current Apex Context

If(Limits.getLimitQueries() - Limits.getQueries()>0) {
  // Execute SOQL Query here.
}
  1. System class Limits – use this to output debug message about each governor limit – 2 version
  2. First: (e.g. Limits.getHeapSize()) returns the amount of the resource that has been used in the current context.
  3. Second: Contains the word limit (e.g. Limits.getLimitDmlStatements()) and returns the total amount of the resource that is available for that context.
  4. Use in the Apex code directly to throw error messages before reaching a governor limit.
  5. When an end-user invokes Apex code that surpasses more than 50% of any governor limit, you can specify a user in your organization to receive an email notification of the event with additional details.

6. 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. For example for putting recordType Id.

Id accountRTId=’xxxxxxxxxx’;

This ensures that code can be deployed safely to different environments. Try to get record Type Id Using Schema methods get the Record Type Id of the sobject.

Id accountRTId= Schema.sobjectType.Account.getRecordTypeInfosByName().get(‘RTName’).getRecordTypeId();

Use of custom settings / custom metadata to avoid hard coding IDs. For example at times you need to handle login credentials in your code. 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.

7. Use Database Methods while doing DML operation


An Apex transaction represents a set of operations that are executed as a single unit. All DML operations in a transaction either complete successfully, or if an error occurs in one operation, the entire transaction is rolled back and no data is committed to the database. Apex gives you the ability to generate a savepoint, that is, a point in the request that specifies the state of the database at that time.

By using the Database class method, you can specify whether or not to allow for partial record processing if errors are encountered.

Database.SaveResult[] accountResults=Database.Insert(accountsToBeInsert, false);
// Using above code, we can work on failure records
  For(Database.SaveResult sr:accountResults) {
	If(!sr.isSuccess()) {
	// add your logic here 
	for(Database.Error err : sr.getErrors()) {
	}
  }
}

8. Exception Handling in Apex Code


DML statements return run-time exceptions if something went wrong in the database during the execution of the DML operations. Don’t forget to use Try catch blocks for exception handling. With Apex, you can write code that responds to specific exceptions.

try{
	// Apex Code 
}Catch(Exception e){
}

9. Write One Trigger per Object per event


Apex triggers within Salesforce are designed to help you automate certain tasks. Apex triggers allow you to perform custom actions before and after events in Salesforce. While writing Apex Trigger you should follow the best practice and create one Trigger per object.

A single Apex Trigger is all you need for one particular object. If you develop multiple Triggers for a single object, you have no way of controlling the order of execution if those Triggers can run in the same contexts.

Learn how to completely and cleanly control your Apex code with Trigger Frameworks, why they’re useful, and the various ways to implement them. Check this post to learn about Trigger Framework.

10. Use Asynchronous Apex


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.

Demystifying Asynchronous Processing

11. Security and Sharing in Apex Code


One of the Apex Code best practice is developers must understand to code secure applications on the Salesforce Platform. Like how to enforce data security and how to prevent SOQL injection attacks in Apex.

Enforcing Object & FLS Permissions in Apex

Apex doesn’t enforce object-level and field-level permissions by default. Let see how we can enforce the CRUD & FLS in Apex.

  1. Schema methods
  2. WITH SECURITY_ENFORCED
  3. Security.stripInaccessible()
  4. Database operations in user mode (pilot

Sharing Clauses for Apex Classes

Apex generally runs in system context means current user’s permissions and field-level security take in place during code execution. Our Apex code should not expose the sensitive data to User which is hidden via security and sharing settings. Hence, Apex security and enforcing the sharing rule is most important. Let see how we can enforce the sharing in Apex

  1. with sharing
  2. without sharing
  3. inherited sharing

Check this post to learn more about Security in Apex.

12. Make reusability of Apex Code


The best code is written away from the keyboard. Break methods into multiple smaller methods if possible, making your code more modular and easier to read and maintain. A key best practice with Apex Code is to always write scalable and reusable code so that it can be reused in another functionality or module. This entails making code generic, so that it can be reused with minimum modification.

Learn more about Apex Enterprise Patterns here.

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.

  1. Bulk Records
  2. Positive & Negative testing.
  3. Restricted User testing.
  4. One Assert Statement per method
  5. should not use @isTest(seeAllData=true)
Apex Code coverage best practices

14. Return Early Pattern


Return early is the way of writing functions or methods so that the expected positive result is returned at the end of the function and the rest of the code terminates the execution

static void someAction(Case record)
    {
        if (/*condition 1*/){
            // do stuff
            return;
        }
        if (/*condition2*/)
        {
            // do stuff
            return;
        }
        // do stuff
        return;
    }

15. Avoid nesting loops within loops


Nested loops should be avoided in Apex controllers because they may slow down the processing of the page or may hit the governing limits for the page. One easy way, and which gives some nice structure to the code is to make the inner loop a separate function, or minimize using loops altogether.

Often we encounter code where we have two or three or more nested Loops which definitely affect performance. A simple way of avoiding nested loops is using Maps. For example, you can find or create a key for each item of the second loop and put the key and the value into the Map.

16. Don’t mix Apex, Process Builders, Workflow Rules, and Record-Triggered flows


Every object should have an automation strategy based on the needs of the business and the Salesforce team supporting it. In general, you should choose one automation tool per object. One of the many inevitable scenarios of older orgs is to have Apex triggers mix in with Autolaunched flows/processes or, more recently, Process Builders mixed in with Record-Triggered flows. This can lead to a variety of issues:

  1. Poor performance
  2. Unexpected results due to the inability to control the order of operations in the ‘stack’
  3. Increase in technical debt with admins/devs not collaborating
  4. Documentation debt

One common approach is to separate DML activity by Apex, and let declarative tools handle non-DML activities like email alerts and in-app alerts — just be careful and ensure none of your Apex conflicts.

17. Naming Conventions.


Another important best practice surrounding Apex Code is the need to follow a proper naming convention. Naming convention in Salesforce is a rule to follow as you decide what to name your identifiers like class, variable, constant, method, etc. But, it is not forced to follow. So, it is known as convention not rule. Naming conventions make the application easier to read and maintain. Check this post to learn more.

18. Setup Code review check list and Code Review process


40%–80% of the lifetime cost of a piece of software goes to maintenance. To avoid the future tech debt we should follow the code review process in each project and should create one code review checklist for developers.

Summary

This article covers many of the core Apex coding best practices. We discussed how to bulkify your code by handling all incoming records instead of just one. We also illustrated how to avoid having SOQL queries inside a loop to avoid governor limits. By following these principles, you are on a great path for success with Apex code.

Comments(3)

  1. Reply
    Frederick Lane says:

    Point 10. I don’t understand the title?

    • Reply
      Amit Chaudhary says:

      its use asynchronous Apex. I will fix the typo

  2. Reply
    Steev says:

    About the last image in the code coverage section Test.startTest and Test.stopTest explanation is a kind of misleading for novice

Post a comment