• Tony Nelson

Reviewing Apex Trigger Code

Have you ever had a chance to write an Apex trigger?

Have you ever had a chance to review the trigger code you wrote?

Many Apex triggers have been implemented and in use in our clients’ systems built by

Looking back over the past years of my work, I noticed that we have been implementing Apex triggers for almost all projects.

This means Apex triggers have become widely used for many occasions. Today I would like to summarize the important points you should consider when reviewing the Apex trigger code.

Below are the 3 points you need to look at:

  1. Whether you have taken the Multi-Record Update function into account upon implementation

  2. Whether you have fully understood governor limits upon implementation

  3. Whether you have properly considered the execution order of Apex trigger upon implementation

※The validity of business logic should also be reviewed.

1. Consider the Multi-Record Update function

A record list is stored in a ‘Trigger.New’ and ‘Trigger.Old’ variables in an Apex trigger context. This list could hold 200 records at maximum.

When you release the application, make sure if you have implemented the trigger considering the possibility of a list update, even if you don’t plan to update any record using Bulk API, SOAP API or WebService, or you save 1 record each per page.

You might say, “When I updated the screen, the Apex trigger worked as expected, but when I added records using DataLoader, only the first record was updated by the Apex trigger. How come it didn’t work?” That’s one of the common mistakes I have heard and known, so please be careful. If bulk update functionality is available for use, then users can also update multiple records at once on the screen.

Consider carefully when you assign a fixed index to a trigger variable, like Trigger.New[0].

trigger AccountTrigger on Account (before update) {

//Obtain an opportunity linked to the updated account List<Opportunity> opportunities = [SELECT id,Amount FROM Opportunity WHERE AccountId=:Trigger.New[0].Id];

//As Trigger.New[0] is selected, only the process for the updated first record will be executed.}

2. Governor Limits

I’m sure those of you who are developers are very familiar with this term, “governor Limits”. By mastering the features of governor limits, effective system construction is feasible.

※For details on Salesforce governor limits, refer to here. Now, let’s go through the review points by each of the governor limits.

# of Times Issued SOQL and DML

There is a limit of the issue for SOQL and DML within one context. Check if SOQL and DML are not issued in a ‘for’ sentence. In the example below, the SOQL in the 6th row is called for 200 times at maximum. Thus it clashes with the governor limits.

trigger AccountTrigger on Account (before update){

//Aggregate the total amount of opportunities won which are linked to all updated accounts for(Account acc:Trigger.New){ Decimal amountSum = 0; for(Opportunity opp:[SELECT Id,Amount FROM Opportunity WHERE AccountId = :acc.Id AND isWon = true]){ amountSum += opp.Amount; } acc.amountSum__c = amountSum;}}

Script Statement

Nest the ‘for’ sentence and verify that there have been records matched by key value.

※Try not to nest a ‘for’ sentence other than the part using ChildRelationShipQuery.

Check the points where a ‘for’ sentence is nested

Create a Map with a Key as a string linked currency code value and year/month value when extracting currency rate record. Change the code by using the get() method of Map to the ‘for’ sentence on the 6th row.

//Obtain the currency rate master List<CurrencyRate__c> rates = [SELECT Id,Name,CurrencyISO__c, YYYYMM__c FROM CurrencyRate__c];

//Build the currency rate lookup relationship in Opportunity for(Opportunity opp:Trigger.New){ for(CurrencyRate__c cr:rates){ if(opp.CurrencyISO__c == cr.CurrencyISO__c // The currency code matches, and && String.valueOf(opp.CloseDate.year()) + String.valueOf(opp.CloseDate.month()) == cr.YYYYMM__c) // Year and Month match opp.CurrencyRate__c = cr.Id; break;}}

Selective SOQL

This can be often overlooked at the first release. In an Apex trigger, you need to include a valid index field in a WHERE phrase when you throw SOQL to an object which holds more than 100K records.

Even if the number of records does not reach 100K at the time of release, if it has a possibility to exceed the number of 100K in the future, please ensure that a valid index field is contained in the WHERE phrase.

3. Execution Order

You can learn more about the order of Apex execution in one of our previous blog posts found here.

The problem is that if cross-object field update is implemented by workflow rule, Apex trigger gets fired after the regular update. For example, there is an Apex trigger set to fire when saving a record for object A and a record is inserted to object B. If cross-object field update is set to object A, a record might be inserted to object B twice. Also, as the trigger gets fired twice, there is a possibility that almost the doubled amount of the script statement is consumed. Avoid double process by using static variables that keep the condition of triggers.

The above 3 points are basics of implementing Apex code. Please come back some time to refresh your memory, so that you can write Apex code in a good manner.


© 2019 TerraSky. All Rights Reserved, TerraSky, Inc.