Salesforce Development Best Practices

In this blog I will share the Salesforce Development best practices that I have learned over the years. I have tried to give a few code snippets to provide examples. Feel free to use the code as-is.

Salesforce Development Best Practices

Salesforce Development Best Practices


One of the key constraints that we all need to deal with is the Governor limits. As Salesforce applications run on cloud and thus all the Salesforce orgs utilize shared infrastructure resources and that is the reason Salesforce makes sure the efficient use of the resources. To implement this, it enforces governor limits on Force.com platform.
Therefore, developing custom Salesforce application, keeping governor limits in mind collectively define Salesforce best practices.

DML operation

For data processing operations, such as inserting, updating, deleting, upserting, etc. Salesforce provides DML statements and Database statements. The total number of DML operations allowed in one transaction are 150 as per the governor limit. Therefore, for bulk data processing tasks we should perform DML operation on multiple records at a time using list or maps.
We should never perform DML operation in for loop because if the number of iterations go beyond 150 then it will hit governor limit and cause limit exception.

Exception Handling

1. Exception handling is also a good practice for visual force page as if we do not handle exception and an error occurs then the VF page will be redirected to the ugly exception page and if it has form then we will loose the form field’s values.

2. We should also take care of null pointer exception which occurs if we access any member variable, method or object field by an object which has not been initialized and thus it has not been allocated any memory. The typical scenario where it occurs is explained below through apex code:
Try{
List conlist = [SELECT id,lastname FROM Contact WHERE AccountId=’abc’];
System.debug(‘number of contacts are ’+conlist.size());
}
Catch(NullPointerException ex){
System.debug(‘Null pointer exception occurred ’+ex.getMessage());
}
Now in above code contact list has been assigned by SOQL query but if there is no contact record available in org with AccountId value ‘abc’ then the conlist variable would be null and the code is accessing the list size in debug and therefore it will throw null pointer exception. So it is always recommended to initialize the object or list of object first by calling default constructor such as :

List conlist = new List(); //initializing contact list object
conlist = [SELECT id,lastname FROM Contact WHERE AccountId=’abc’];

Contact conobj = new Contact();
3. We should always write DML operation in try catch block in Visualforce controller and apex class to find the root cause of the error which has caused the exception and it will help developers to resolve the error. Salesforce provides specific exception for specific error.
It has been explained in detail in below code example:

Try{
List conlist =[SELECT id, Lastname From Contact WHERE Lastname=’abc’ limit 10];

Update conlist;
}
Catch(ListException ex){
System.debug(‘List exception error ’+ex.getMessage());
}
Catch(QueryException ex){
System.debug(‘Query exception has occurred ’+ex.getMessage());
}
Catch(DMLException ex){
System.debug(‘Update error has occurred ’+ex.getMessage());
}
Catch(NullException ex){
System.debug(‘Null pointer error has occurred ’+ex.getMessage());
}

SOQL and SOSL

Following are the best practices for using SOQL and SOSL query language:

1. We should never use SOQL and SOSL query inside for loop as the maximum number of SOQL queries allowed in one transaction is 100 for synchronous and 200 in asynchronous and maximum number of SOSL queries allowed is 20.

For example: If we want to perform DML operation on child records of a parent object then typical method used by Salesforce beginners of doing this is given below using following code:

List acclist = new List();
acclist=[SELECT id,name FROM Account LIMIT 10];
for(Account acc:acclist)
{
List conlist=[SELECT id,Lastname FROM Contact WHERE AccountID=:acc.id];
For(Contact con:conlist){
//perform DML on child contacts
}
}
The above way of doing this is wrong as it uses SOQL inside for loop which may lead to Limit Exception and hit governor limit. The correct way of doing this is explained below:

List acclist = new List();
List conlist = new List();

acclist=[SELECT id,name,( SELECT id,Lastname FROM Contact) FROM Account LIMIT 10];

for(Account acc:acclist)
{
For(Contact con:acc.Contact){
Con.lastname=’abc’;
Conlist.add(con);
}
}
Update conlist;
This is the correct way of fulfilling this requirement

2. In one transaction one SOQL query can fetch only maximum of 50,000 records and if it exceeds then it will throw limit exception. To avoid this, we should use where clause in the query if possible.

3. We should also use indexed fields such as ID, name, phone, email in where clause as it contributes to the Salesforce application performance.

Triggers

Following are the best practices for triggers:
1. For writing trigger, we should ensure that trigger is short and we should create a separate class (treated as trigger handler) and call that class from trigger. The separate class can be used to fulfill other business requirement which is similar to the requirement covered by the trigger.
2. While writing update trigger (either before or after) if we have a requirement that the trigger code should only be executed if a specific condition is true i.e only if a particular field changes to a new value then it’s a good practice that we should compare field’s previous value with new value otherwise the trigger code would be executed on every update operation which is not appropriate. Example given below:

Trigger accounttrigger on Account(after update){
For(Account acc:trigger.new){
If(trigger.oldMap.get(acc.id).name!= trigger.newMap.get(acc.id).name){
//execute the trigger code here
}
}
}
3. If we have the requirement to update a field based on some criteria during insertion or updation operation, then we should write before trigger because if we try to accomplish this task in after trigger then it will lead to trigger looping.
For example:

Trigger Accounttrigger on Account(after update){
List acclist = new List();
For(Account acc:trigger.new){
If(acc.name=’abc’){
Acc.name=’xyz’;
Acclist.add(acc);
}
}
Update acclist;
}
The above approach will cause trigger looping as it is updating Account list in after trigger means after the Account list have been updated and thus it will cause the above trigger to fire again.

The following code sample is correct for this scenario:

Trigger Accounttrigger on Account(before update){
List acclist = new List();
For(Account acc:trigger.new){
If(acc.name=’abc’){
Acc.name=’xyz’;
}
}
}
4. We should avoid calling future within future. For example, if we have a trigger handler which is having a future method and as per the requirement we have to execute DML operation and then it will lead same trigger to be executed again which then might call future method again, which will throw asynchronous exception.
Visualforce pages: In Visualforce controller, we should take care of viewstate memory which is 135KB.
View State: If we have a form in VF page then the form fields values are stored in viewstate in encrypted format to maintain the state of the page.

Following are the factors which contribute to viewstate memory:
a. If we have some variables which we do not need to use in VF page UI then we should declare it as transient variable otherwise it will add to the view state memory and may lead to view state error
b. In VF page, we should try to keep minimum number of forms
c. If we want to display list of object records on VF page, then we should use where clause on indexed fields in SOQL query.

Future methods: While writing apex code which contains future method we should pay special attention towards its governor limits. In one apex invocation no more than 10 future calls are allowed and not more than 200 future calls per Salesforce license per 24 hour are allowed. Following is the example of inefficient apex code for future method:
Trigger testtrigger on Account (after insert){
For(Account acc:trigger.new){
Asynchapexclass.testfuture(acc.id);
}
}
Global class asynchapexclass{
@future
Public static void testfuture(ID acc_id){
List conlist=new List();
Conlist=[SELECT id,lastname FROM Contact WHERE AccountId:=acc_id];
For(Contact c:conlist){
c.lastname=’abc’;
}
Try{
Update conlist;
}
Catch(DMLException ex){
System.debug(‘update error ’+ex.getMessage());
}
}
}
The above code is inefficient because it is calling future method within for loop in trigger and therefore it is more likely to hit governor limit of maximum number of future calls allowed in one transaction which is 10. Following code is efficient and appropriate for this kind of requirement:
Trigger testtrigger on Account (after insert){
Asynchapexclass.testfuture(trigger.newMap.keyset());
}
Global class asynchapexclass{
@future
Public static void testfuture(Set accid_set){
List conlist=new List();
Conlist=[SELECT id,lastname FROM Contact WHERE AccountId IN :accid_set];
For(Contact c:conlist){
c.lastname=’abc’;
}
Try{
Update conlist;
}
Catch(DMLException ex){
System.debug(‘update error ’+ex.getMessage());
}
}
}

Test Class

While creating test class we should not use org data but rather we should we should create test data. To create test data we should create a specific method with the annotation @Testsetup and create test data and then the test data(created in @testsetup method) would be available in all the other methods existing in the test class. Therefore, we do not have to create separate test data for every method existing in test class.

Posted in apex develeopment, Apex Development, Salesforce, salesforce administrator, salesforce certified developer, Salesforce Challenges, salesforce consultant, salesforce customization, salesforce development, Salesforce.com, sfdc. Tagged with , , , , .

Leave a Reply

Your email address will not be published. Required fields are marked *

*