Salesforce Development Best Practices to Learn in 2024

Salesforce Development Best Practices

Quick Summary:

The Top 12 Salesforce Best Practices are described in this article. It will assist Salesforce developers in improving and upgrading their coding skills to compete with the finest in the business.

Salesforce is a cloud-based software as a service (SaaS) provider, which means it maintains an online application that customers can use. It is based on a subscription-based pay-as-you-go business model. Salesforce is available from any device since all data and information is saved in the cloud. It also enables real-time data tracking and accurate updates.

Salesforce offers all customers in the multi-tenet cloud a powerful core set of services. You get access to the same tools regardless of your size. To ensure that you and your customers have equal access to these resources, Salesforce has established policies to ensure that everyone has access to the majority of the Salesforce.

Salesforce’s popularity may be evidenced by 62.1 percent of Salesforce CRM being used in the United States alone. Walmart, UnitedHealth Group, and McKesson Corporation are just a few of the well-known customers.

Some other stats are as below:

  • Over 150,000 companies use Salesforce, and Salesforce generated $21.25 billion in revenue in 2021.
  • Salesforce has a 19.8% CRM market share, which is higher than the combined market share of the top four competitors.
  • Salesforce employs 56,606 people.
  • Salesforce’s revenue has climbed by more than 24.27 percent over the previous year.
  • Over the last two decades, Salesforce revenue has increased at a CAGR of 51.22 percent.
  • Salesforce divides its revenue into two categories. The first is revenue from subscriptions and support, while the second is from professional services and other sources.
  • Salesforce’s primary revenue stream is subscription and support money. Cloud services, software licenses, and related technical support are all included in this sector.
  • Salesforce’s subscription and support division generates 93.99 percent of the company’s overall revenue, which is $19.98 billion. This represents a 24.51 percent increase over the 2020 fiscal year.
  • In 2021, this category accounted for 6.01 percent of overall Salesforce revenue. The $1.28 billion in professional services and other revenues is a 21.9 percent annual growth.

Now that we’ve established Salesforce’s popularity let’s look at what it’s used for.

What Is Salesforce Used For?

Another feature of the CRM platform is that it is cloud-based software rather than hardware. This means that a company’s departments can be linked regardless of where they are in the actual world. Salesforce develops and maintains CRM software that helps firms break down technology boundaries across departments, giving them a holistic view of their customers across all channels. A CRM platform allows for a better understanding of customers and a better customer experience when interacting with a company.

How does it work?

Salesforce has several cloud platforms to choose from, each with its own set of features. The following are a handful of them:

Sales Cloud

Sales, marketing, and customer service resources are all part of a sales cloud. It enables organizations to give tailored purchase experiences to customers in both business-to-business (B2B) and business-to-consumer (B2C) situations.

Marketing Cloud

Businesses may communicate with customers through the relevant channels at the right time with the marketing cloud. It creates personalized content for each user depending on their interests, online behavior, and other factors. Data can be gathered through a person’s social media accounts, buying habits, and other sources.

Analytics Cloud

The Einstein Sales Analytics cloud, often known as the Analytics cloud, assists organizations in interacting with data and, as a result, closing more deals. Companies can notice patterns, act on opportunities, measure team performance, and more with simple data visualization features. The list goes on and on. Advanced artificial intelligence is finding a home at the heart of the platform’s efficiency. Salesforce has evolved since its inception, and new features are being added all the time. As a result, more organizations rely on the platform to help them take their efforts to the next level.

Let’s look at the benefits and drawbacks of using a Salesforce.

Pros & Cons of SalesForce

Pros Cons
Incredible powerful CRM Platform Expensive
You can add /remove features to suit your needs Complex configuration & time consuming
Importing leads and contacts Clutter interface, but makes simple tasks complex
Track the level of leads in marketing pipelines Complex learning curve
Check the history of leads & contacts and their engagement in the organizations. API limitations
Allows Reporting Confusing permissions
User-Friendly UI Latency in Performance & Speed

Salesforce Development Best Practices for Developers

The top 12 Salesforce configuration best practices are listed below. Force.com is a great place for Expert Salesforce engineers to show off their knowledge of the Salesforce platform. To ensure the proper use of resources on the force.com platform, SalesForce has imposed several restrictions. As a result, when creating Force.com, a developer must keep the Salesforce.com apex coding standard in mind to ensure the highest quality product. Let’s take a look at the Salesforce coding best practices.

1. Bulkify your code

Bulkifying Apex code refers to the process of ensuring that the code is handled correctly for multiple records at once. A single instance of Apex code is performed when a batch of records launches Apex, but it must handle all of the records in the batch. A trigger may be triggered by a Force.com SOAP API call that inserted a batch of records, for example. To design scalable code and avoid hitting governor restrictions, all of those records must be handled in bulk if a batch of records calls the same Apex function.

Poorly written code


trigger accountTestTrggr on Account (before insert, before update) {
   //This only handles the first record in the Trigger.new collection
   //But if more than one Account initiated this trigger, those additional records
   //will not be processed
   Account acct = Trigger.new[0];
   List contacts = [select id, salutation, firstname, lastname, email
              from Contact where accountId = :acct.Id];
}

Because the code expressly reads only the first record in the Trigger, only one Account record is handled. new collection by using the syntax Trigger. new[0]. Instead, the trigger should be able to handle the whole collection of Accounts in the Trigger.

Best Practice


trigger accountTestTrggr on Account (before insert, before update) {
   List<String> accountNames = new List<String>{};
   //Loop through all records in the Trigger.new collection
   for(Account a: Trigger.new){
      //Concatenate the Name and billingState into the Description field
      a.Description = a.Name + ':' + a.BillingState
   }
}

Notice how this updated version of the code uses a for loop to iterate over the full Trigger.new collection. All records are now properly handled whether this trigger is used with a single Account or up to 200 Accounts.

2. Avoid SOQL Queries or DML statements inside FOR Loops

The previous Salesforce coding standards stressed the significance of batch processing all incoming records. The for loop was used to go over all of the trigger records and the new collection in that example. Queries for DML statements are frequently inserted inside the for-loop, which is a typical mistake. There is a governor limit that specifies the maximum number of DML statements that can be executed. Database operations are invoked per iteration of the loop when these actions are placed inside a for loop, making it simple to approach the governor limits.

Rather, relocate any database activities outside of the loop. If you need to query, do so once, get all the data you need in one query, and then iterate through the results. If you need to change the data, batch it up into a list and run your DML on that list.

trigger accountTestTrggr on Account (before insert, before update) {
   //For loop to iterate through all the incoming Account records
   for(Account a: Trigger.new) {
      //THIS FOLLOWING QUERY IS INEFFICIENT AND DOESN'T SCALE
      //Since the SOQL Query for related Contacts is within the FOR loop, if this trigger is initiated
      //with more than 100 records, the trigger will exceed the trigger governor limit
      //of maximum 100 SOQL Queries.
        List<Contact> contacts = [select id, salutation, firstname, lastname, email
                        from Contact where accountId = :a.Id];
      for(Contact c: contacts) {
         System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '],
                                         LastName[' + c.lastname +']');
         c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
         //THIS FOLLOWING DML STATEMENT IS INEFFICIENT AND DOESN'T SCALE
         //Since the UPDATE DML operation is within the FOR loop, if this trigger is initiated
         //with more than 150 records, the trigger will exceed the trigger governor limit
         //of 150 DML Operations maximum.
        update c;
      }     
   }
}

A query will be conducted for each Account because there is a SOQL query within the for loop that iterates through all the Account objects that began this trigger. Before surpassing the governor limit, an Apex request can receive a maximum of 100 SOQL queries. The governor limit will throw a runtime exception if this trigger is called by a batch of more than 100 Account records.

Because each request is limited to 150 DML operations, a governor limit will be exceeded when the 150th contact is changed in this case.

Here’s how to ‘bulkify’ the code so that you may query all of the contacts in a single query and only execute one DML update transaction.


trigger accountTestTrggr on Account (before insert, before update) {
  //This queries all Contacts related to the incoming Account records in a single SOQL query.
  //This is also an example of how to use child relationships in SOQL
  List<Account> accountsWithContacts = [select id, name, (select id, salutation, description,
                                                                first name, last name, email from Contacts)
                                                                from Account where Id IN: Trigger.newMap.keySet()];
  List<Contact> contactsToUpdate = new List<Contact>{};
  // For loop to iterate through all the queried Account records
  for(Account a: accountsWithContacts){
     // Use the child relationships dot syntax to access the related Contacts
     for(Contact c: a.Contacts){
      System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
      c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
      contactsToUpdate.add(c);
     }      
   }
   //Now outside the FOR Loop, perform a single Update DML statement.
   update contactsToUpdate;
}

Only one SOQL query and one update statement are now done when triggered by a single account record or up to 200 account records.

3. Bulkify your Helper Methods

This apex coding practice is similar to the previous one: make sure that any code that conducts a query or DML action does so in bulk rather than iteratively or in a loop. When queries or DML actions are run within an iteration, the governor limitations are at risk of being exceeded. This is true for any helper or utility methods called by an Apex request.

Governor limits are calculated during runtime. Any Apex code performed in that transaction applies and shares the governor limitations after the request (trigger, Visualforce page, etc.). If a trigger calls Apex methods from a helper class, the shared Apex methods must be built to handle large records. These methods should be constructed so that they may be called with a set of records, especially if they contain a SOQL query or a DML operation.

If an Apex method runs a SOQL query, it should be given a collection of records (Array, List, Set, etc.) to run the query on all data in the Apex transaction. If the Apex method is invoked for each record being processed individually, the Apex transaction will perform queries inefficiently and may exceed the transaction’s maximum number of queries. DML statements in Apex methods are treated similarly.

As a result, please make sure that any utility or assistance methods are implemented in a way that effectively handles record collections. Inefficient queries and DML operations would not be executed unnecessarily as a result.

4. Using Collections, Streamlining Queries, and Efficient For Loops

To effectively query and store data in memory, Apex collections are essential. Using collections and streaming SOQL queries together can significantly aid in the creation of effective Apex code while avoiding governor constraints.

Poorly written code


trigger accountTrigger on Account (before delete, before insert, before the update) {
    //This code inefficiently queries the Opportunity object in two seperate queries
    List<Opportunity> opptysClosedLost = [select id, name, closedate, stagename
            from Opportunity where
            accountId IN :Trigger.newMap.keySet() and StageName='Closed - Lost'];
    List<Opportunity> opptysClosedWon = [select id, name, closedate, stagename
            from Opportunity where
            accountId IN :Trigger.newMap.keySet() and StageName='Closed - Won'];
   for(Account a : Trigger.new){
      //This code inefficiently has two inner FOR loops
      //Redundantly processes the List of Opportunity Lost
        for(Opportunity o: opptysClosedLost){
        if(o.accountid == a.id)
           System.debug('Do more logic here...');
        }
        //Redundantly processes the List of Opportunity Won
        for(Opportunity o: opptysClosedWon){
        if(o.accountid == a.id)
           System.debug('Do more logic here...');
        }
    }
}

The fundamental flaw in the above example is that the opportunity records are queried twice in two consecutive queries. Use the where clause in SOQL to query all of the data you need in a single query. Another issue is using two inner loops that loop over the entire list of opportunity records to find those tied to a given account.

Best Practice

trigger accountTrigger on Account (before delete, before insert, before the update) {
    //This code efficiently queries all related Closed Lost and
    //Closed Won opportunities in a single query.
    List<Account> accountWithOpptys = [select id, name, (select id, name, closedate,
         stagename  from Opportunities  where accountId IN :Trigger.newMap.keySet()
         and  (StageName='Closed - Lost' or StageName = 'Closed - Won'))
         from Account where Id IN :Trigger.newMap.keySet()];
    //Loop through Accounts only once
    for(Account a : accountWithOpptys){
       //Loop through related Opportunities only once
       for(Opportunity o: a.Opportunities){
        if(o.StageName == 'Closed - Won'){
          System.debug('Opportunity Closed Won...do some more logic here...');
        }else if(o.StageName =='Closed - Lost'){
          System.debug('Opportunity Closed Lost...do some more logic here...');
        }
       }
    }
}

This revised sample only executes one query for all related opportunities. It only has one inner for loop to apply the same logic but is much more efficient and governor-friendly.

5. Streamlining Multiple Triggers on the Same Object

When using numerous triggers on the same object, it’s critical to eliminate redundancy and inefficiencies. It is feasible to have redundant assertions if they are generated independently. Note that when many triggers are deployed on the same object, it’s critical to specify how governor limits are enforced. To begin with, you don’t have any explicit control over which triggers fires first. Second, each activated trigger is not given its governor limits.

Instead, all of the processed code, including the additional triggers, shares the available resources. As a result, rather than a single trigger receiving a maximum of 100 queries, all triggers on the same object will share those 100 queries. As a result, it’s vital to ensure that the many triggers are effective and have no redundancies.

6. Large Data Sets Querying

SOQL queries in a single request can return a total of 50,000 records. If returning a high number of searches exceeds your heap limit, you must utilize a SOQL query for loop instead. Internal calls to query and queryMore allow it to process numerous batches of records.

The syntax below, for example, causes a runtime error if the results are too large:

//A runtime exception is thrown if this query returns enough records to exceed your heap limit.
Account[] accts = [SELECT id FROM account];

Instead, use a SOSQL query for loop as in one of the following examples

// Use this format for efficiency if you are executing DML statements
// within the for loop.  Be careful not to exceed the 150 DML statement limit.
Account[] accts = new Account[];
 for (List acct : [SELECT id, name FROM account
                          WHERE name LIKE 'Acme']) {
   // Your logic here
    accts.add(acct);
}
update accts;

Let the force.com platform chunk your large query results into batches of 200 records using this syntax where the SOQL query is in the for loop logic.

7. Use of the Limits Apex Methods to Avoid Hitting Governor Limits

Limits is a System class in Apex that allows you to print debug messages for each governor limit. Every method has two versions: one returns the quantity of the resource utilized in the current context, and the other includes the word limit and returns the entire amount of the resource available for that context.

The example below explains how to include these statements in your code and use them to identify if or when you are likely to exceed any governor limits. You can assess the output using either the System Log or the Debug Logs to see how the individual code is performing compared to the governor limitations. Additionally, you may explicitly incorporate logic in Apex code to throw error messages before a governor limit is reached. An IF statement is used in the code example below to determine if the trigger will update too many Opportunities.

Here’s an example of how you can combine systems. debug statements with the Limitations Apex class to create valuable output regarding governor limits and general code performance.

trigger accountLimitExample on Account (after delete, after insert, after update) {
    System.debug('Total Number of SOQL Queries allowed in this Apex code context: ' +  Limits.getLimitQueries());
    System.debug('Total Number of records that can be queried  in this Apex code context: ' +  Limits.getLimitDmlRows());
    System.debug('Total Number of DML statements allowed in this Apex code context: ' +  Limits.getLimitDmlStatements() );
    System.debug('Total Number of CPU usage time (in ms) allowed in this Apex code context: ' +  Limits.getLimitCpuTime());
   // Query the Opportunity object
    List opptys =
        [select id, description, name, accountid,  closedate, stagename from Opportunity where accountId IN: Trigger.newMap.keySet()];
    System.debug('1. Number of Queries used in this Apex code so far: ' + Limits.getQueries());
    System.debug('2. Number of rows queried in this Apex code so far: ' + Limits.getDmlRows());
    System.debug('3. Number of DML statements used so far: ' +  Limits.getDmlStatements());   
    System.debug('4. Amount of CPU time (in ms) used so far: ' + Limits.getCpuTime());
    //NOTE:Proactively determine if there are too many Opportunities to update and avoid governor limits
    if (opptys.size() + Limits.getDMLRows() > Limits.getLimitDMLRows()) {
            System.debug('Need to stop processing to avoid hitting a governor limit. Too many related Opportunities to update in this trigger');
            System.debug('Trying to update ' + opptys.size() + ' opportunities but governor limits will only allow ' + Limits.getLimitDMLRows());
            for (Account a : Trigger.new) {
                a.addError('You are attempting to update the addresses of too many accounts at once. Please try again with fewer accounts.');
            }
    }
    else{
        System.debug('Continue processing. Not going to hit DML governor limits');
        System.debug('Going to update ' + opptys.size() + ' opportunities and governor limits will allow ' + Limits.getLimitDMLRows());
        for(Account a : Trigger.new){
            System.debug('Number of DML statements used so far: ' +  Limits.getDmlStatements());
            for(Opportunity o: opptys){
                if (o.accountid == a.id)
                   o.description = 'testing';
            }
        }
        update opptys;
        System.debug('Final number of DML statements used so far: ' +  Limits.getDmlStatements());
        System.debug('Final heap size: ' +  Limits.getHeapSize());
    }
}

Here is the output after running this trigger by updating an account record through the user interface.

DEBUG|Total Number of SOQL Queries allowed in this Apex code context: 100
DEBUG|Total Number of records that can be queried in this Apex code context: 10000
DEBUG|Total Number of DML statements allowed in this Apex code context: 150
DEBUG|Total Number of CPU usage time (in ms) allowed in this Apex code context: 10000
DEBUG|1. Number of Queries used in this Apex code so far: 1
DEBUG|2. Number of rows queried in this Apex code so far: 0
DEBUG|3. Number of DML statements used so far: 0
DEBUG|4. Amount of CPU time (in ms) used so far: 9
DEBUG|Continue processing. Not going to hit DML governor limits
DEBUG|Going to update 3 opportunities, and governor limits will allow 10000
DEBUG|Number of DML statements used so far: 0
DEBUG|Final number of DML statements used so far: 1
DEBUG|Final heap size: 1819

When debugging and analyzing the efficiency of your code, the Limit Apex class can be really useful. It also shows how to check ahead of time to see if you’ll hit governor restrictions and how to handle those situations better.

8. Apex Governor Limit Warning Emails

You can designate a person in your organization to receive email notification of the event with more data when an end-user invokes Apex code that exceeds more than 50% of any governor limit. To activate email warnings, follow these steps:

Log in to Salesforce as an administrator user.
Click Setup | Manage Users | Users.
Click Edit next to the name of the user who should receive the email notifications.
Select the Send Apex Warning Emails option.
Click Save.

9. Use @future Appropriately

As stated throughout this post, it is vital to construct your Apex code so that it can efficiently process large numbers of records at once. Asynchronous Apex methods (those tagged with the @future keyword) are also affected. Governors in Apex Code#Synchronous vs. Asynchronous Apex explains the distinctions between synchronous and asynchronous Apex. Even though Apex written asynchronously has its own set of higher governor restrictions, it still has them. In addition, in a single Apex transaction, no more than ten @future methods can be called.

 

The following is a list of governor restrictions that are special to the @future annotation:

  • Per Apex invocation, no more than 10 method calls are allowed.
  • Per Salesforce license, no more than 200 method calls can be made in 24 hours.
  • The parameters must be primitive data types, primitive datatype arrays, or primitive datatype collections.
  • The future annotation prevents methods from accepting sObjects or objects as parameters.
  • In Visualforce controllers, methods with the future annotation cannot be used in the getMethodName or setMethodName methods, nor in the function Object() { [native code] }.

It’s critical to ensure that the asynchronous methods are called quickly and that the code in the methods is lightweight. The Apex trigger in the following example inefficiently calls an asynchronous function for each Account record it wishes to process:

trigger accountAsyncTrigger on Account (after insert, after update) {
  for(Account a: Trigger.new){
    // Invoke the @future method for each Account
    // This is inefficient and will easily exceed the governor limit of
    // at most 10 @future invocation per Apex transaction
    asyncApex.processAccount(a.id);
   }    
}

Here is the Apex class that defines the @future method

global class asyncApex {
 
  @future
  public static void processAccount(Id accountId) {
       List contacts = [select id, salutation, firstname, lastname, email
                from Contact where accountId = :accountId];
      
         for(Contact c: contacts){
      System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
                  c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
        }
        update contacts;       
  }  
}

The @future function will be called N times because it is used within the for loop (depending on the number of accounts processed). If there are more than ten accounts, this code will throw an exception because the governor maximum of 10 @future invocations per Apex transaction has been exceeded.

Instead, the @future function should be called with a batch of records so that it only needs to be called once for all of the records it needs to process:

trigger accountAsyncTrigger on Account (after insert, after update) {
//Bypassing the @future method a set of Ids, it only needs to be
//invoked once to handle all of the data.
asyncApex.process account(Trigger.newMap.keySet());
}

And now, the @future method is designed to receive a set of records.

global class asyncApex {
 @future
  public static void processAccount(Set accountIds) {
       List contacts = [select id, salutation, firstname, lastname, email from Contact where accountId IN :accountIds];
       for(Contact c: contacts){
   System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
                          c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
        }
        update contacts;
  }
}

Take note of the modest code changes required to handle a batch of records. Although it takes less code to manage a set of data than it does to handle a single record, this is an important design principle that should be applied to all of your Apex code, regardless of whether it runs synchronously or asynchronously.

10. Writing Test methods to verify large datasets

Because Apex code executes in bulk, test scenarios are required to ensure that the Apex being tested can handle huge datasets rather than single records. A data activity through the user interface or a data operation from the Force.com SOAP API can both trigger an Apex trigger. The API can send numerous records to each batch, causing the trigger to fire multiple times. As a result, having test methods to ensure that all Apex code is appropriately designed to handle larger datasets and does not violate governor restrictions.

The following example shows a badly constructed trigger that doesn’t handle bulk correctly and hits a governor limit. Later, the trigger is updated to handle bulk datasets properly.

Here’s an example of a badly written contact trigger. The trigger runs a SOQL query for each contact to obtain the associated account. The part of this illegal trigger is that the SOQL query is inside the for loop, which means that if more than 100 contacts are inserted/updated, a governor limit exception will be thrown.

trigger contactTest on Contact (before insert, before update) {
for(Contact ct: Trigger.new){
Account acct = [select id, name from Account where Id=:ct.AccountId];
if(acct.BillingState=='CA'){
System.debug('found a contact related to an account in california...');
ct.email = 'test_email@testing.com';
//Apply more logic here....
}
}
}

Test method for testing whether this trigger properly handles volume dataset.

public class sampleTestMethodCls {
   static testMethod void testAccountTrigger(){
   //First, prepare 200 contacts for the test data
    Account acct = new Account(name='test account');
    insert acct;
    Contact[] contactsToCreate = new Contact[]{};
    for(Integer x=0; x<200;x++){
        Contact ct = new Contact(AccountId=acct.Id,lastname='test');
        contactsToCreate.add(ct);
    }
    //Now insert data causing an contact trigger to fire.
    Test.startTest();
    insert contactsToCreate;
    Test.stopTest(); 
  }
}

This test method builds and adds a 200-contact array. The trigger will fire as a result of the insert when this test procedure encounters a governor limit, a System, and the exception will be fired. This test method throws the exception ‘Too many SOQL queries: 101’ because the trigger above conducts a SOQL query for each contact in the batch. A trigger can only run 100 queries at a time.

It’s worth noting that Test. starTest and Test.stopTest are used. Code called before and after Test. starTest receives a different set of governor restrictions than code invoked between Test.startTest and Test.stopTest when running tests. This allows any data to be set up without compromising the governor restrictions accessible to the tested code.

Now we’ll fix the trigger so that bulk actions may be handled properly. The key to resolving this trigger is to move the SOQL query outside of the for loop and to perform only one SOQL query:

trigger contactTest on Contact (before insert, before update) {
   Set accountIds = new Set();
   for(Contact ct: Trigger.new)
       accountIds.add(ct.AccountId);
   //Do SOQL Query    
   Map<Id, Account> accounts = new Map<Id, Account>(
        [select id, name, billingState from Account where id in :accountIds]);
   for(Contact ct: Trigger.new){
       if(accounts.get(ct.AccountId).BillingState=='CA'){
           System.debug('found a contact related to an account in california...');
           ct.email = 'test_email@testing.com';
           //Apply more logic here....
       }
   }    
}
}

It’s worth noting that the SOQL query that retrieves the accounts is now only run once. If you re-run the test as mentioned earlier, it will now complete properly with no errors and 100% code coverage.

11. Avoid Hardcoring IDs

Avoid hardcoding IDs in Apex code when deploying Apex code between sandbox and production environments or downloading Force.com AppExchange packages. If the record IDs change between contexts, the logic will dynamically identify the correct data to act on and will not fail.

Here’s an example of a conditional statement that hardcodes the record type IDs. This will work great in the development environment, but there is no guarantee that the record type identifiers will be the same if the code is deployed in a different org (e.g. as part of an AppExchange package).

for(Account a: Trigger.new){
//Error - hardcoded the record type id
if(a.RecordTypeId=='012500000009WAr'){
//do some logic here.....
}else if(a.RecordTypeId=='0123000000095Km'){
//do some logic here for a different record type...
}
}

To appropriately manage the dynamic nature of the record type IDs, the following example asks the code for record kinds, saves the dataset in a map collection for simple retrieval, and avoids any hardcoding.

//Query for the Account record types
List<RecordType> rtypes = [Select Name, Id From RecordType
where sObjectType='Account' and isActive=true];
//Create a map between the Record Type Name and Id for easy retrieval
Map<String,String> accountRecordTypes = new Map<String,String>{};
for(RecordType rt: rtypes)
accountRecordTypes.put(rt.Name,rt.Id);
for(Account a: Trigger.new){
//Use the Map collection to dynamically retrieve the Record Type Id
//Avoid hardcoding Ids in the Apex code
if(a.RecordTypeId==accountRecordTypes.get('Healthcare')){
//do some logic here.....
}else if(a.RecordTypeId==accountRecordTypes.get('High Tech')){
//do some logic here for a different record type...
}
}

By ensuring that no IDs are saved in Apex code, you are making the code far more dynamic and versatile and guaranteeing that it can be safely distributed to many environments.

12. Write one trigger per object to prevent.

  • If a single object has many triggers for the same event, the Salesforce execution engine may organize the triggers in any order. This can cause issues if there are dependencies between your trigger logic or require the triggers to be executed in a specific order.
  • There is no guarantee of trigger sequence if there are three triggers on the same object, all of which are “Before Insert” event triggers.
  • As a result, implementing all trigger logic for a single event in a single trigger per object is a Coding best practice.
  • This also aids in code and system maintenance by making it easier to comprehend the logic flow and make adjustments.

13. Engage in source-driven development

“Source-driven development” refers to the technique of employing version control technologies such as Git as part of Salesforce Developer Experience (DX), Salesforce’s new way for maintaining and building apps on the Lightning Platform. Because it regularly logs changes to your system, source-driven development makes it easier to develop applications in a collaborative context. So, if one developer deletes another’s code by accident, you’ll have a record of both the modification and the original code, preventing your team from committing a costly error.

14. Create Multiple Sandboxes

Developers should have separate, dedicated environments for developing, testing, and training, according to Salesforce. Isn’t that correct? You can keep better track of every stage of the development lifecycle and enforce better overall sandbox management by designating various use cases for each sandbox and determining how often they should be refreshed and who should have access to them.

15. Expedite the testing process with automation

Manual testing can be a time-consuming procedure, but it is vital to guarantee that your Salesforce deployment smoothly. You may save time and money by automating the testing process and reducing the risk of human mistake that comes with manual testing.

have a unique app Idea?

Hire Certified Developers To Build Robust Feature, Rich App And Websites

Salesforce Best Practices For Users

Let’s take a look at the best practices for different users.

Salesforce Development Best Practices: Executives

  • Establish Metrics
  • Empower user efficiency with automation
  • Make data gathering more efficient.
  • Create a plan to support user adoption
  • Break down data silos.

Salesforce Development Best Practices: Sales Team

  • Segment the market
  • Better sales decisions with data analytics

Salesforce Development Best Practices: Administrator

  • Never stop learning.
  • Be on top of all things cutting edge
  • Make the most of Salesforce’s reporting tools

Summary

Many of the most important Salesforce best practices are covered in this article. To develop efficient, scalable code, these ideas should be implemented into your Apex code. We spoke about how to bulk up your code by dealing with all incoming records rather than just one. We also showed how to avoid using SOQL searches within a loop to avoid governor limits. There are also examples of how to generate useful governor limit debugging statements and a variety of other Salesforce development best practices. You’ll be well on your way to success with Apex programming if you stick to these guidelines.

This post was last modified on December 11, 2023 4:01 pm

Saurabh Barot: Saurabh Barot, CTO at Aglowid IT Solutions, brings over a decade of expertise in web, mobile, data engineering, Salesforce, and cloud computing. Known for his strategic leadership, he drives technology initiatives, oversees data infrastructure, and leads cross-functional teams. His expertise spans across Big Data, ETL processes, CRM systems, and cloud infrastructure, ensuring alignment with business goals and keeping the company at the forefront of innovation.
Related Post