SlideShare a Scribd company logo
1 of 34
Download to read offline
new String[] {"hip”, "hip”};
(hip hip array!)
Q: What’s the object-oriented way to become wealthy?
A: Inheritance
To understand what recursion is, you must first understand recursion.
Q: Why don't jokes work in octal?
A: Because 7 10 11
Session: Write Bulletproof Trigger Code
Write Bulletproof Trigger Code
​ Steve Cox
​ Senior Software Developer
​ steve.cox@exponentpartners.com
​ @SteveCox67
​ 
Writing Apex triggers can be simple, safe, and fun!
​ Safe harbor statement under the Private Securities Litigation Reform Act of 1995:
​ This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties
materialize or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed
or implied by the forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-
looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any
statements regarding strategies or plans of management for future operations, statements of belief, any statements concerning new,
planned, or upgraded services or technology developments and customer contracts or use of our services.
​ The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new
functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our
operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of any
litigation, risks associated with completed and any possible mergers and acquisitions, the immature market in which we operate, our
relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new releases of our
service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization and selling to larger
enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com, inc. is included in our
annual report on Form 10-K for the most recent fiscal year and in our quarterly report on Form 10-Q for the most recent fiscal quarter.
These documents and others containing important disclosures are available on the SEC Filings section of the Investor Information section
of our Web site.
​ Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available
and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features
that are currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements.
Safe Harbor
Steve Cox
Senior Software Developer, Exponent Partners
1.  What are the “bullets”?
a)  Pop Quiz
b)  What makes trigger writing challenging?
2.  What’s our defense?!
a)  Don’t write a trigger
b)  Avoid DML
c)  Write great unit tests
d)  Use a good trigger framework
Agenda
1.  // When an account changes, set the description on all contacts associated with that account
2.  trigger MyAccountTrigger on Account(before insert, before update) {
3.  Account a = Trigger.new[0];
4.  Contact[] contacts = [SELECT Salutation, FirstName FROM Contact WHERE AccountId = :a.Id];
5.  for (Contact c : contacts) {
6.  c.Description = a.Name + ': ' + c.Salutation + ' ' + c.FirstName;
7.  update c;
8.  }
9.  }
How many “bullets” is this trigger code littered with?
“Bullets” Quiz
1.  A variable number of records may be passed to your trigger
2.  Your trigger may be called multiple times due to workflow, other triggers, process builder, or flows
3.  The order of execution of multiple triggers on the same object is not defined
4.  Performance may be an issue if the object contains many records
5.  Limits!
6.  Testing triggers can be very involved
7.  Writing great Apex code is difficult
Several factors make trigger writing challenging
“Bullets”
Dealing with “Bullets”
Defense #1: Don’t Write a Trigger - Configure
•  Formula fields
•  Validation rules
•  Workflow
•  Roll-up summary fields
•  Approval processes
•  Visual flow
•  Process builder…
When possible, use point and click solutions rather than custom code
Don’t Write a Trigger - Configure
. . . Use ANYTHING BUT a trigger
Dealing with “Bullets”
Defense #2: Avoid DML
•  Just because a trigger is called doesn’t mean there’s anything that needs to be done
•  Don’t update records that don’t need to be updated
•  Don’t execute code that doesn’t need to be executed
Avoiding DML protects us from several “bullets”
•  Limits
•  Multiple calls and trigger “loops”
•  Performance issues
P.S. Be careful relying on static variables to determine multiple executions
Avoid DML
1.  public class AccountHandler extends BaseHandler {
2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) {
3.  Account[] accountsToUpdate = new Account[]{};
4.  for (Account a : accounts) {
5.  accountsToUpdate.add(new Account(Id = a.Id, Description = a.Name + ': ' + a.AccountNumber);
6.  }
7.  update accountsToUpdate;
8.  }
9.  }
What changes could be made to the code below to postpone or isolate DML?
Avoid DML – But How?
1.  public class AccountHandler extends BaseHandler {
2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) {
3.  Account[] accountsToUpdate = new Account[]{};
4.  for (Account a : accounts) {
5.  Account oldAccount = (Account)oldAccounts.get(a.Id);
6.  if ((a.Name != oldAccount.Name) || (a.AccountNumber != oldAccount.AccountNumber)) {
7.  accountsToUpdate.add(new Account(Id = a.Id, Description = a.Name + ': ' + a.AccountNumber));
8.  }
9.  }
10.  update accountsToUpdate;
11.  }
12. }
Method 1: has a relevant field changed?
Avoid DML – But How?
Advantages / Disadvantages?
1.  public class AccountHandler extends BaseHandler {
2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) {
3.  Account[] accountsToUpdate = new Account[]{};
4.  for (Account a : (Account[])accounts.values()) {
5.  String newDescription = a.Name + ': ' + a.AccountNumber;
6.  if (a.Description != newDescription) {
7.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription);
8.  }
9.  }
10.  update accountsToUpdate;
11.  }
12.  }
Method 2: are we making a change?
Avoid DML – But How?
Advantages / Disadvantages?
1.  public class AccountHandler extends BaseHandler {
2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) {
3.  Map<Id,SObject> changedAccounts = accounts.clone(true);
4.  Account[] accountsToUpdate = new Account[]{};
5.  for (Account a : changedAccounts) {
6.  // logic / update a.field1
7.  // logic / update a.field2
8.  // ...
9.  // logic / update a.fieldN
10.  if (!accountsAreEqual(a, accounts.get(a.Id))) {
11.  accountsToUpdate.add(a);
12.  }
13.  }
14.  update accountsToUpdate;
15.  }
16.  }
Method 3: are we making a change (complex logic)?
Avoid DML – But How?
Advantages / Disadvantages?
Dealing with “Bullets”
Defense #3: Write Great Unit Tests
1.  Design for testability!
2.  Use asserts
3.  Always test in bulk
4.  Test the happy path
5.  Don’t test if the trigger handler is disabled
6.  (?) Test error and exception conditions
7.  (?) Cover all logical branches
Write Great Unit Tests
1.  trigger MyAccountTrigger on Account(before insert, before update) {
2.  Account[] accountsToUpdate = new Account[]{};
3.  for (Account a : (Account[])Trigger.new) {
4.  String newDescription = a.Name + ': ' + a.AccountNumber;
5.  if (a.Description != newDescription) {
6.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription);
7.  }
8.  }
9.  update accountsToUpdate;
10. }
Design: why is this code difficult to test?
Write Great Unit Tests
Suggestions?
1.  public class AccountHandler extends BaseHandler {
2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) {
3.  update Accounts.setDescriptions((Account[])accounts.values());
4.  }
5.  }
6.  public class Accounts extends Domain { // enterprise patterns
7.  public static Account[] setDescriptions(Account[] accounts) { // reusable!
8.  Utils.preCondition(null != accounts, 'a list of accounts is required');
9.  Account[] accountsToUpdate = new Account[]{};
10.  for (Account a : accounts) {
11.  String newDescription = a.Name + ': ' + a.AccountNumber;
12.  if (a.Description != newDescription) {
13.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription);
14.  }
15.  }
16.  return accountsToUpdate; // no DML!
17.  }
Design: fixes
Write Great Unit Tests
Advantages / Disadvantages?
1.  @IsTest class TestAccounts extends BaseTest { // base does setup, creates myAdminUser, etc.
2.  static testMethod void testSetDescriptions() {
3.  System.runAs(myAdminUser) {
4.  Account[] accounts = newAccounts('name'); // no DML!
5.  Test.startTest();
6.  // no accounts
7.  System.assert(Accounts.setDescriptions(new Account[]{}).isEmpty());
8.  // bulk accounts with changed descriptions
9.  Account[] results = Accounts.setDescriptions(accounts);
10.  Test.stopTest();
11.  for (Integer i = 0; i < accounts.size(); ++i) {
12.  //TODO assert description was correctly set
13.  }
14.  }
15.  . . .
Create a base class; always test in bulk
Write Great Unit Tests
Advantages / Disadvantages?
1.  @IsTest class TestAccounts extends BaseTest {
2.  static testMethod void testSetDescriptions() {
3.  System.runAs(myAdminUser) {
4.  Test.startTest();
5.  //TODO test with null values for Name, AccountNumber
6.  //TODO test with blank values for Name, AccountNumber
7.  //TODO test with long values for Name, AccountNumber
8.  //TODO test case where description doesn’t change
9.  //TODO test Accounts.setDescriptions(Accounts.setDescriptions())
10.  Test.stopTest();
11.  }
12.  }
13.  }
Cover all logic
Write Great Unit Tests
1.  @IsTest class TestAccounts extends BaseTest {
2.  static testMethod void testTriggerHandlerAssertions() {
3.  System.runAs(myAdminUser) {
4.  Test.startTest();
5.  try {
6.  Accounts.setDescriptions(null);
7.  System.assert(false, ‘records are required');
8.  } catch (PreConditionException ex) {}
9.  Test.stopTest();
10.  }
11.  }
12.  }
Test error conditions & exceptions
Write Great Unit Tests
1.  @IsTest class TestAccounts extends BaseTest {
2.  static testMethod void testTriggerHandler() {
3.  if (Utils.triggerIsEnabled('MyAccountTrigger', 'SetAccountDescriptions')) {
4.  System.runAs(myAdminUser) {
5.  Account[] accounts = newAccounts('name');
6.  insert accounts;
7.  //TODO assert descriptions are NOT set
8.  Test.startTest();
9.  update accounts;
10.  Test.stopTest();
11.  //TODO assert descriptions ARE set
12.  }
13.  . . .
Test the trigger
Write Great Unit Tests
Dealing with “Bullets”
Defense #4: Use a Good Trigger Framework
A framework can provide consistency and
save loads of time
•  Allows you to specify the processing order
•  Makes enabling/disabling individual handlers
a snap
•  Provides centralized diagnostics and error
handling
•  Decreases development and ramp-up time
Use a Good Trigger Framework
Framework Features
Use a Good Trigger Framework
1.  trigger MyObj on MyObj__c(after delete, after insert, after undelete,
2.  after update, before delete, before insert, before update) {
3.  new TriggerFactory().handle(MyObj__c.SObjectType); // non-testable / non-reusable code
4.  }
// Trigger body is reduced to a single line
1.  public static Handler__c[] handlers {
2.  get {
3.  if (null == handlers) {
4.  handlers = [SELECT ... FROM TriggerSettings WHERE Active__c = true ORDER BY Type__c, Order__c];
5.  //categorize as desired – SObjectType, subsystem, feature, etc.
6.  }
7.  return handlers;
8.  }
9.  private set;
10. }
// Handlers are declared in custom settings, where they can be disabled or the order set
Framework Features
Use a Good Trigger Framework
1.  public void handle(SObjectType o) { // centralized trigger handler
2.  final String type = String.valueOf(o);
3.  if (!Boolean.valueOf(TriggerSettings.getInstance().EnableTriggers__c)) return;
4.  try {
5.  if (!Boolean.valueOf(TriggerSettings.getInstance().get(‘Trigger_’ + type)) return;
6.  } catch (Exception ex) {} // if no setting exists, handlers are enabled by default
7. 
8.  for (Handler__c h : handlers) {
9.  if (h.Active__c && h.Type__c.equalsIgnoreCase(type)) {
10.  final String handlerName = h.Name.trim();
11.  try {
12.  ITrigger t = (ITrigger)Type.forName(handlerName).newInstance();
13.  if (null != t) {
14.  execute(t);
15.  }
16.  } catch (NullPointerException ex) {}
17.  }
18.  }
19. }
// Centralized dispatcher enforces disabling
Framework Features
Use a Good Trigger Framework
1.  private void execute(ITrigger handler) { // centralized trigger handler
2.  try {
3.  if (Trigger.isBefore) {
4.  if (Trigger.isInsert) {
5.  handler.bulkBeforeInsert(Trigger.newMap);
6.  for (SObject so : Trigger.new) {
7.  handler.beforeInsert(so);
8.  }
9.  } else if (Trigger.isUpdate) {
10.  handler.bulkBeforeUpdate(Trigger.oldMap, Trigger.newMap);
11.  for (SObject so : Trigger.old) {
12.  handler.beforeUpdate(so, Trigger.newMap.get(so.Id));
13.  }
14.  } else if (Trigger.isDelete) {
15.  handler.bulkBeforeDelete(Trigger.oldMap);
16.  for (SObject so : Trigger.old) {
17.  handler.beforeDelete(so);
18.  }
19.  }
20.  } else if (Trigger.isAfter) {
21.  . . .
// A common, logical ‘execute’ pattern provides consistent routing
Framework Features
Use a Good Trigger Framework
1.  public virtual class BaseHandler extends BaseClass implements ITrigger {
2.  /** the records that will be inserted when "andFinally" is called */
3.  protected SObject[] toInsert = new SObject[]{};
4.  . . .
5. 
6.  /** handle bulk "before" processing of insert */
7.  public virtual void bulkBeforeInsert(Map<Id,SObject> sos)
8.  { Utils.preCondition(Test.isRunningTest() || (Trigger.isBefore && Trigger.isInsert)); }
9. 
10.  /** handle bulk "before" processing of update */
11.  public virtual void bulkBeforeUpdate(Map<Id,SObject> oldSos, Map<Id,SObject> sos)
12.  . . .
13.  /** insert, update, and delete the processed records */
14.  public virtual void andFinally() {
15.  Utils.preCondition(isValid());
16. 
17.  insert toInsert;
18.  . . .
// Base classes make handler writing a snap
Conclusion
1.  Avoid them if possible!
2.  Only do DML when absolutely necessary
3.  Test thoroughly
4.  Use a good trigger framework
Writing bulletproof trigger code is challenging
Summary
Sample frameworks and additional guidance
Additional Resources
•  Trigger Frameworks and Apex Trigger Best Practices – Kevin O’Hara
•  An architecture framework to handle triggers in the Force.com platform – Hari Krishnan
•  Trigger Pattern for Tidy, Streamlined, Bulkified Triggers – Tony Scott
•  Advanced Apex Programming – Dan Appleman
Share Your Feedback, and Win a GoPro!
3
Earn a GoPro prize entry for
each completed survey
Tap the bell to take a
survey2Enroll in a session1
Questions?

More Related Content

What's hot

Secure Development on the Salesforce Platform - Part I
Secure Development on the Salesforce Platform - Part ISecure Development on the Salesforce Platform - Part I
Secure Development on the Salesforce Platform - Part ISalesforce Developers
 
Lightning components performance best practices
Lightning components performance best practicesLightning components performance best practices
Lightning components performance best practicesSalesforce Developers
 
Building apps faster with lightning and winter '17
Building apps faster with lightning and winter '17Building apps faster with lightning and winter '17
Building apps faster with lightning and winter '17Salesforce Developers
 
Salesforce API Series: Integrating Applications with Force.com Webinar
Salesforce API Series: Integrating Applications with Force.com WebinarSalesforce API Series: Integrating Applications with Force.com Webinar
Salesforce API Series: Integrating Applications with Force.com WebinarSalesforce Developers
 
Salesforce.com API Series: Service Cloud Console Deep Dive
Salesforce.com API Series: Service Cloud Console Deep DiveSalesforce.com API Series: Service Cloud Console Deep Dive
Salesforce.com API Series: Service Cloud Console Deep DiveSalesforce Developers
 
Coding Apps in the Cloud with Force.com - Part 2
Coding Apps in the Cloud with Force.com - Part 2Coding Apps in the Cloud with Force.com - Part 2
Coding Apps in the Cloud with Force.com - Part 2Salesforce Developers
 
Modeling and Querying Data and Relationships in Salesforce
Modeling and Querying Data and Relationships in SalesforceModeling and Querying Data and Relationships in Salesforce
Modeling and Querying Data and Relationships in SalesforceSalesforce Developers
 
Lightning web components episode 2- work with salesforce data
Lightning web components   episode 2- work with salesforce dataLightning web components   episode 2- work with salesforce data
Lightning web components episode 2- work with salesforce dataSalesforce Developers
 
Summer '15 Release Preview: Platform Feature Highlights
Summer '15 Release Preview: Platform Feature Highlights Summer '15 Release Preview: Platform Feature Highlights
Summer '15 Release Preview: Platform Feature Highlights Salesforce Developers
 
Elevate workshop programmatic_2014
Elevate workshop programmatic_2014Elevate workshop programmatic_2014
Elevate workshop programmatic_2014David Scruggs
 
Build, Manage, and Deploy Mobile Apps Faster with App Cloud Mobile
Build, Manage, and Deploy Mobile Apps Faster with App Cloud MobileBuild, Manage, and Deploy Mobile Apps Faster with App Cloud Mobile
Build, Manage, and Deploy Mobile Apps Faster with App Cloud MobileSalesforce Developers
 
Coding Apps in the Cloud with Force.com - Part I
Coding Apps in the Cloud with Force.com - Part ICoding Apps in the Cloud with Force.com - Part I
Coding Apps in the Cloud with Force.com - Part ISalesforce Developers
 
Integrating with salesforce
Integrating with salesforceIntegrating with salesforce
Integrating with salesforceMark Adcock
 
Lightning Design System and Components for Visualforce Developers
Lightning Design System and Components for Visualforce DevelopersLightning Design System and Components for Visualforce Developers
Lightning Design System and Components for Visualforce DevelopersSalesforce Developers
 
Unlocking the Hidden Potential of Salesforce Live Agent
Unlocking the Hidden Potential of Salesforce Live AgentUnlocking the Hidden Potential of Salesforce Live Agent
Unlocking the Hidden Potential of Salesforce Live AgentSalesforce Developers
 
Process Automation on Lightning Platform Workshop
Process Automation on Lightning Platform WorkshopProcess Automation on Lightning Platform Workshop
Process Automation on Lightning Platform WorkshopSalesforce Developers
 
UX and Design: Top Tips for Salesforce Apps on Mobile and Wearables
UX and Design: Top Tips for Salesforce Apps on Mobile and WearablesUX and Design: Top Tips for Salesforce Apps on Mobile and Wearables
UX and Design: Top Tips for Salesforce Apps on Mobile and WearablesSalesforce Developers
 
Mastering Force.com: Advanced Visualforce
Mastering Force.com: Advanced VisualforceMastering Force.com: Advanced Visualforce
Mastering Force.com: Advanced VisualforceSalesforce Developers
 

What's hot (20)

Secure Development on the Salesforce Platform - Part I
Secure Development on the Salesforce Platform - Part ISecure Development on the Salesforce Platform - Part I
Secure Development on the Salesforce Platform - Part I
 
Lightning components performance best practices
Lightning components performance best practicesLightning components performance best practices
Lightning components performance best practices
 
Using Apex for REST Integration
Using Apex for REST IntegrationUsing Apex for REST Integration
Using Apex for REST Integration
 
Building apps faster with lightning and winter '17
Building apps faster with lightning and winter '17Building apps faster with lightning and winter '17
Building apps faster with lightning and winter '17
 
Salesforce API Series: Integrating Applications with Force.com Webinar
Salesforce API Series: Integrating Applications with Force.com WebinarSalesforce API Series: Integrating Applications with Force.com Webinar
Salesforce API Series: Integrating Applications with Force.com Webinar
 
Salesforce.com API Series: Service Cloud Console Deep Dive
Salesforce.com API Series: Service Cloud Console Deep DiveSalesforce.com API Series: Service Cloud Console Deep Dive
Salesforce.com API Series: Service Cloud Console Deep Dive
 
Coding Apps in the Cloud with Force.com - Part 2
Coding Apps in the Cloud with Force.com - Part 2Coding Apps in the Cloud with Force.com - Part 2
Coding Apps in the Cloud with Force.com - Part 2
 
Modeling and Querying Data and Relationships in Salesforce
Modeling and Querying Data and Relationships in SalesforceModeling and Querying Data and Relationships in Salesforce
Modeling and Querying Data and Relationships in Salesforce
 
Exploring the Salesforce REST API
Exploring the Salesforce REST APIExploring the Salesforce REST API
Exploring the Salesforce REST API
 
Lightning web components episode 2- work with salesforce data
Lightning web components   episode 2- work with salesforce dataLightning web components   episode 2- work with salesforce data
Lightning web components episode 2- work with salesforce data
 
Summer '15 Release Preview: Platform Feature Highlights
Summer '15 Release Preview: Platform Feature Highlights Summer '15 Release Preview: Platform Feature Highlights
Summer '15 Release Preview: Platform Feature Highlights
 
Elevate workshop programmatic_2014
Elevate workshop programmatic_2014Elevate workshop programmatic_2014
Elevate workshop programmatic_2014
 
Build, Manage, and Deploy Mobile Apps Faster with App Cloud Mobile
Build, Manage, and Deploy Mobile Apps Faster with App Cloud MobileBuild, Manage, and Deploy Mobile Apps Faster with App Cloud Mobile
Build, Manage, and Deploy Mobile Apps Faster with App Cloud Mobile
 
Coding Apps in the Cloud with Force.com - Part I
Coding Apps in the Cloud with Force.com - Part ICoding Apps in the Cloud with Force.com - Part I
Coding Apps in the Cloud with Force.com - Part I
 
Integrating with salesforce
Integrating with salesforceIntegrating with salesforce
Integrating with salesforce
 
Lightning Design System and Components for Visualforce Developers
Lightning Design System and Components for Visualforce DevelopersLightning Design System and Components for Visualforce Developers
Lightning Design System and Components for Visualforce Developers
 
Unlocking the Hidden Potential of Salesforce Live Agent
Unlocking the Hidden Potential of Salesforce Live AgentUnlocking the Hidden Potential of Salesforce Live Agent
Unlocking the Hidden Potential of Salesforce Live Agent
 
Process Automation on Lightning Platform Workshop
Process Automation on Lightning Platform WorkshopProcess Automation on Lightning Platform Workshop
Process Automation on Lightning Platform Workshop
 
UX and Design: Top Tips for Salesforce Apps on Mobile and Wearables
UX and Design: Top Tips for Salesforce Apps on Mobile and WearablesUX and Design: Top Tips for Salesforce Apps on Mobile and Wearables
UX and Design: Top Tips for Salesforce Apps on Mobile and Wearables
 
Mastering Force.com: Advanced Visualforce
Mastering Force.com: Advanced VisualforceMastering Force.com: Advanced Visualforce
Mastering Force.com: Advanced Visualforce
 

Viewers also liked

Angular js best practice
Angular js best practiceAngular js best practice
Angular js best practiceMatteo Scandolo
 
Git/Github & Salesforce
Git/Github & Salesforce Git/Github & Salesforce
Git/Github & Salesforce Gordon Bockus
 
Multiorg Collaboration Using Salesforce S2S
Multiorg Collaboration Using Salesforce S2SMultiorg Collaboration Using Salesforce S2S
Multiorg Collaboration Using Salesforce S2SMayur Shintre
 
Getting Started with Angular JS
Getting Started with Angular JSGetting Started with Angular JS
Getting Started with Angular JSAkshay Mathur
 
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ Teerasej Jiraphatchandej
 
Salesforce Development Best Practices
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best PracticesVivek Chawla
 
50 Ways to Become More Professionally Excellent
50 Ways to Become More Professionally Excellent50 Ways to Become More Professionally Excellent
50 Ways to Become More Professionally ExcellentLeslie Bradshaw
 

Viewers also liked (7)

Angular js best practice
Angular js best practiceAngular js best practice
Angular js best practice
 
Git/Github & Salesforce
Git/Github & Salesforce Git/Github & Salesforce
Git/Github & Salesforce
 
Multiorg Collaboration Using Salesforce S2S
Multiorg Collaboration Using Salesforce S2SMultiorg Collaboration Using Salesforce S2S
Multiorg Collaboration Using Salesforce S2S
 
Getting Started with Angular JS
Getting Started with Angular JSGetting Started with Angular JS
Getting Started with Angular JS
 
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ
Angular JS: First look เริ่มต้น Angular JS กันแบบสบายๆ
 
Salesforce Development Best Practices
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best Practices
 
50 Ways to Become More Professionally Excellent
50 Ways to Become More Professionally Excellent50 Ways to Become More Professionally Excellent
50 Ways to Become More Professionally Excellent
 

Similar to Write bulletproof trigger code

Get ready for your platform developer i certification webinar
Get ready for your platform developer i certification   webinarGet ready for your platform developer i certification   webinar
Get ready for your platform developer i certification webinarJackGuo20
 
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Mary Scotton
 
Webinar Coding for Salesforce Admins
Webinar Coding for Salesforce AdminsWebinar Coding for Salesforce Admins
Webinar Coding for Salesforce AdminsSalesforce Admins
 
Performance Tuning for Visualforce and Apex
Performance Tuning for Visualforce and ApexPerformance Tuning for Visualforce and Apex
Performance Tuning for Visualforce and ApexSalesforce Developers
 
Visualforce Hack for Junction Objects
Visualforce Hack for Junction ObjectsVisualforce Hack for Junction Objects
Visualforce Hack for Junction ObjectsRitesh Aswaney
 
S-Controls for Dummies
S-Controls for DummiesS-Controls for Dummies
S-Controls for Dummiesdreamforce2006
 
S-Controls for Dummies
S-Controls for DummiesS-Controls for Dummies
S-Controls for Dummiesdreamforce2006
 
10 Principles of Apex Testing
10 Principles of Apex Testing10 Principles of Apex Testing
10 Principles of Apex TestingKevin Poorman
 
Atl elevate programmatic developer slides
Atl elevate programmatic developer slidesAtl elevate programmatic developer slides
Atl elevate programmatic developer slidesDavid Scruggs
 
Process Automation Showdown Session 1
Process Automation Showdown Session 1Process Automation Showdown Session 1
Process Automation Showdown Session 1Michael Gill
 
The Business of Flow - Point and Click Workflow Applications
The Business of Flow - Point and Click Workflow ApplicationsThe Business of Flow - Point and Click Workflow Applications
The Business of Flow - Point and Click Workflow ApplicationsDreamforce
 
Making External Web Pages Interact With Visualforce
Making External Web Pages Interact With VisualforceMaking External Web Pages Interact With Visualforce
Making External Web Pages Interact With VisualforceSalesforce Developers
 
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013Salesforce Developers
 
Building Visualforce Custom Events Handlers
Building Visualforce Custom Events HandlersBuilding Visualforce Custom Events Handlers
Building Visualforce Custom Events HandlersSalesforce Developers
 

Similar to Write bulletproof trigger code (20)

Get ready for your platform developer i certification webinar
Get ready for your platform developer i certification   webinarGet ready for your platform developer i certification   webinar
Get ready for your platform developer i certification webinar
 
Apex Design Patterns
Apex Design PatternsApex Design Patterns
Apex Design Patterns
 
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
 
Introduction to Apex for Developers
Introduction to Apex for DevelopersIntroduction to Apex for Developers
Introduction to Apex for Developers
 
Webinar Coding for Salesforce Admins
Webinar Coding for Salesforce AdminsWebinar Coding for Salesforce Admins
Webinar Coding for Salesforce Admins
 
Performance Tuning for Visualforce and Apex
Performance Tuning for Visualforce and ApexPerformance Tuning for Visualforce and Apex
Performance Tuning for Visualforce and Apex
 
Visualforce Hack for Junction Objects
Visualforce Hack for Junction ObjectsVisualforce Hack for Junction Objects
Visualforce Hack for Junction Objects
 
S-Controls for Dummies
S-Controls for DummiesS-Controls for Dummies
S-Controls for Dummies
 
S-Controls for Dummies
S-Controls for DummiesS-Controls for Dummies
S-Controls for Dummies
 
10 Principles of Apex Testing
10 Principles of Apex Testing10 Principles of Apex Testing
10 Principles of Apex Testing
 
10 Principles of Apex Testing
10 Principles of Apex Testing10 Principles of Apex Testing
10 Principles of Apex Testing
 
Atl elevate programmatic developer slides
Atl elevate programmatic developer slidesAtl elevate programmatic developer slides
Atl elevate programmatic developer slides
 
Process Automation Showdown Session 1
Process Automation Showdown Session 1Process Automation Showdown Session 1
Process Automation Showdown Session 1
 
Force.com Friday : Intro to Apex
Force.com Friday : Intro to Apex Force.com Friday : Intro to Apex
Force.com Friday : Intro to Apex
 
Df12 Performance Tuning
Df12 Performance TuningDf12 Performance Tuning
Df12 Performance Tuning
 
The Business of Flow - Point and Click Workflow Applications
The Business of Flow - Point and Click Workflow ApplicationsThe Business of Flow - Point and Click Workflow Applications
The Business of Flow - Point and Click Workflow Applications
 
Introduction to Apex Triggers
Introduction to Apex TriggersIntroduction to Apex Triggers
Introduction to Apex Triggers
 
Making External Web Pages Interact With Visualforce
Making External Web Pages Interact With VisualforceMaking External Web Pages Interact With Visualforce
Making External Web Pages Interact With Visualforce
 
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013
Salesforce1 Platform ELEVATE LA workshop Dec 18, 2013
 
Building Visualforce Custom Events Handlers
Building Visualforce Custom Events HandlersBuilding Visualforce Custom Events Handlers
Building Visualforce Custom Events Handlers
 

More from Salesforce Developers

Sample Gallery: Reference Code and Best Practices for Salesforce Developers
Sample Gallery: Reference Code and Best Practices for Salesforce DevelopersSample Gallery: Reference Code and Best Practices for Salesforce Developers
Sample Gallery: Reference Code and Best Practices for Salesforce DevelopersSalesforce Developers
 
Maximizing Salesforce Lightning Experience and Lightning Component Performance
Maximizing Salesforce Lightning Experience and Lightning Component PerformanceMaximizing Salesforce Lightning Experience and Lightning Component Performance
Maximizing Salesforce Lightning Experience and Lightning Component PerformanceSalesforce Developers
 
Local development with Open Source Base Components
Local development with Open Source Base ComponentsLocal development with Open Source Base Components
Local development with Open Source Base ComponentsSalesforce Developers
 
TrailheaDX India : Developer Highlights
TrailheaDX India : Developer HighlightsTrailheaDX India : Developer Highlights
TrailheaDX India : Developer HighlightsSalesforce Developers
 
Why developers shouldn’t miss TrailheaDX India
Why developers shouldn’t miss TrailheaDX IndiaWhy developers shouldn’t miss TrailheaDX India
Why developers shouldn’t miss TrailheaDX IndiaSalesforce Developers
 
CodeLive: Build Lightning Web Components faster with Local Development
CodeLive: Build Lightning Web Components faster with Local DevelopmentCodeLive: Build Lightning Web Components faster with Local Development
CodeLive: Build Lightning Web Components faster with Local DevelopmentSalesforce Developers
 
CodeLive: Converting Aura Components to Lightning Web Components
CodeLive: Converting Aura Components to Lightning Web ComponentsCodeLive: Converting Aura Components to Lightning Web Components
CodeLive: Converting Aura Components to Lightning Web ComponentsSalesforce Developers
 
Enterprise-grade UI with open source Lightning Web Components
Enterprise-grade UI with open source Lightning Web ComponentsEnterprise-grade UI with open source Lightning Web Components
Enterprise-grade UI with open source Lightning Web ComponentsSalesforce Developers
 
TrailheaDX and Summer '19: Developer Highlights
TrailheaDX and Summer '19: Developer HighlightsTrailheaDX and Summer '19: Developer Highlights
TrailheaDX and Summer '19: Developer HighlightsSalesforce Developers
 
Lightning web components - Episode 4 : Security and Testing
Lightning web components  - Episode 4 : Security and TestingLightning web components  - Episode 4 : Security and Testing
Lightning web components - Episode 4 : Security and TestingSalesforce Developers
 
LWC Episode 3- Component Communication and Aura Interoperability
LWC Episode 3- Component Communication and Aura InteroperabilityLWC Episode 3- Component Communication and Aura Interoperability
LWC Episode 3- Component Communication and Aura InteroperabilitySalesforce Developers
 
Lightning web components - Episode 1 - An Introduction
Lightning web components - Episode 1 - An IntroductionLightning web components - Episode 1 - An Introduction
Lightning web components - Episode 1 - An IntroductionSalesforce Developers
 
Migrating CPQ to Advanced Calculator and JSQCP
Migrating CPQ to Advanced Calculator and JSQCPMigrating CPQ to Advanced Calculator and JSQCP
Migrating CPQ to Advanced Calculator and JSQCPSalesforce Developers
 
Scale with Large Data Volumes and Big Objects in Salesforce
Scale with Large Data Volumes and Big Objects in SalesforceScale with Large Data Volumes and Big Objects in Salesforce
Scale with Large Data Volumes and Big Objects in SalesforceSalesforce Developers
 
Replicate Salesforce Data in Real Time with Change Data Capture
Replicate Salesforce Data in Real Time with Change Data CaptureReplicate Salesforce Data in Real Time with Change Data Capture
Replicate Salesforce Data in Real Time with Change Data CaptureSalesforce Developers
 
Modern Development with Salesforce DX
Modern Development with Salesforce DXModern Development with Salesforce DX
Modern Development with Salesforce DXSalesforce Developers
 
Integrate CMS Content Into Lightning Communities with CMS Connect
Integrate CMS Content Into Lightning Communities with CMS ConnectIntegrate CMS Content Into Lightning Communities with CMS Connect
Integrate CMS Content Into Lightning Communities with CMS ConnectSalesforce Developers
 

More from Salesforce Developers (20)

Sample Gallery: Reference Code and Best Practices for Salesforce Developers
Sample Gallery: Reference Code and Best Practices for Salesforce DevelopersSample Gallery: Reference Code and Best Practices for Salesforce Developers
Sample Gallery: Reference Code and Best Practices for Salesforce Developers
 
Maximizing Salesforce Lightning Experience and Lightning Component Performance
Maximizing Salesforce Lightning Experience and Lightning Component PerformanceMaximizing Salesforce Lightning Experience and Lightning Component Performance
Maximizing Salesforce Lightning Experience and Lightning Component Performance
 
Local development with Open Source Base Components
Local development with Open Source Base ComponentsLocal development with Open Source Base Components
Local development with Open Source Base Components
 
TrailheaDX India : Developer Highlights
TrailheaDX India : Developer HighlightsTrailheaDX India : Developer Highlights
TrailheaDX India : Developer Highlights
 
Why developers shouldn’t miss TrailheaDX India
Why developers shouldn’t miss TrailheaDX IndiaWhy developers shouldn’t miss TrailheaDX India
Why developers shouldn’t miss TrailheaDX India
 
CodeLive: Build Lightning Web Components faster with Local Development
CodeLive: Build Lightning Web Components faster with Local DevelopmentCodeLive: Build Lightning Web Components faster with Local Development
CodeLive: Build Lightning Web Components faster with Local Development
 
CodeLive: Converting Aura Components to Lightning Web Components
CodeLive: Converting Aura Components to Lightning Web ComponentsCodeLive: Converting Aura Components to Lightning Web Components
CodeLive: Converting Aura Components to Lightning Web Components
 
Enterprise-grade UI with open source Lightning Web Components
Enterprise-grade UI with open source Lightning Web ComponentsEnterprise-grade UI with open source Lightning Web Components
Enterprise-grade UI with open source Lightning Web Components
 
TrailheaDX and Summer '19: Developer Highlights
TrailheaDX and Summer '19: Developer HighlightsTrailheaDX and Summer '19: Developer Highlights
TrailheaDX and Summer '19: Developer Highlights
 
Live coding with LWC
Live coding with LWCLive coding with LWC
Live coding with LWC
 
Lightning web components - Episode 4 : Security and Testing
Lightning web components  - Episode 4 : Security and TestingLightning web components  - Episode 4 : Security and Testing
Lightning web components - Episode 4 : Security and Testing
 
LWC Episode 3- Component Communication and Aura Interoperability
LWC Episode 3- Component Communication and Aura InteroperabilityLWC Episode 3- Component Communication and Aura Interoperability
LWC Episode 3- Component Communication and Aura Interoperability
 
Lightning web components - Episode 1 - An Introduction
Lightning web components - Episode 1 - An IntroductionLightning web components - Episode 1 - An Introduction
Lightning web components - Episode 1 - An Introduction
 
Migrating CPQ to Advanced Calculator and JSQCP
Migrating CPQ to Advanced Calculator and JSQCPMigrating CPQ to Advanced Calculator and JSQCP
Migrating CPQ to Advanced Calculator and JSQCP
 
Scale with Large Data Volumes and Big Objects in Salesforce
Scale with Large Data Volumes and Big Objects in SalesforceScale with Large Data Volumes and Big Objects in Salesforce
Scale with Large Data Volumes and Big Objects in Salesforce
 
Replicate Salesforce Data in Real Time with Change Data Capture
Replicate Salesforce Data in Real Time with Change Data CaptureReplicate Salesforce Data in Real Time with Change Data Capture
Replicate Salesforce Data in Real Time with Change Data Capture
 
Modern Development with Salesforce DX
Modern Development with Salesforce DXModern Development with Salesforce DX
Modern Development with Salesforce DX
 
Get Into Lightning Flow Development
Get Into Lightning Flow DevelopmentGet Into Lightning Flow Development
Get Into Lightning Flow Development
 
Integrate CMS Content Into Lightning Communities with CMS Connect
Integrate CMS Content Into Lightning Communities with CMS ConnectIntegrate CMS Content Into Lightning Communities with CMS Connect
Integrate CMS Content Into Lightning Communities with CMS Connect
 
Introduction to MuleSoft
Introduction to MuleSoftIntroduction to MuleSoft
Introduction to MuleSoft
 

Write bulletproof trigger code

  • 1. new String[] {"hip”, "hip”}; (hip hip array!) Q: What’s the object-oriented way to become wealthy? A: Inheritance To understand what recursion is, you must first understand recursion. Q: Why don't jokes work in octal? A: Because 7 10 11 Session: Write Bulletproof Trigger Code
  • 2. Write Bulletproof Trigger Code ​ Steve Cox ​ Senior Software Developer ​ steve.cox@exponentpartners.com ​ @SteveCox67 ​  Writing Apex triggers can be simple, safe, and fun!
  • 3. ​ Safe harbor statement under the Private Securities Litigation Reform Act of 1995: ​ This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the forward-looking statements we make. All statements other than statements of historical fact could be deemed forward- looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of our services. ​ The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of any litigation, risks associated with completed and any possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com, inc. is included in our annual report on Form 10-K for the most recent fiscal year and in our quarterly report on Form 10-Q for the most recent fiscal quarter. These documents and others containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site. ​ Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements. Safe Harbor
  • 4. Steve Cox Senior Software Developer, Exponent Partners
  • 5. 1.  What are the “bullets”? a)  Pop Quiz b)  What makes trigger writing challenging? 2.  What’s our defense?! a)  Don’t write a trigger b)  Avoid DML c)  Write great unit tests d)  Use a good trigger framework Agenda
  • 6. 1.  // When an account changes, set the description on all contacts associated with that account 2.  trigger MyAccountTrigger on Account(before insert, before update) { 3.  Account a = Trigger.new[0]; 4.  Contact[] contacts = [SELECT Salutation, FirstName FROM Contact WHERE AccountId = :a.Id]; 5.  for (Contact c : contacts) { 6.  c.Description = a.Name + ': ' + c.Salutation + ' ' + c.FirstName; 7.  update c; 8.  } 9.  } How many “bullets” is this trigger code littered with? “Bullets” Quiz
  • 7. 1.  A variable number of records may be passed to your trigger 2.  Your trigger may be called multiple times due to workflow, other triggers, process builder, or flows 3.  The order of execution of multiple triggers on the same object is not defined 4.  Performance may be an issue if the object contains many records 5.  Limits! 6.  Testing triggers can be very involved 7.  Writing great Apex code is difficult Several factors make trigger writing challenging “Bullets”
  • 8. Dealing with “Bullets” Defense #1: Don’t Write a Trigger - Configure
  • 9. •  Formula fields •  Validation rules •  Workflow •  Roll-up summary fields •  Approval processes •  Visual flow •  Process builder… When possible, use point and click solutions rather than custom code Don’t Write a Trigger - Configure . . . Use ANYTHING BUT a trigger
  • 11. •  Just because a trigger is called doesn’t mean there’s anything that needs to be done •  Don’t update records that don’t need to be updated •  Don’t execute code that doesn’t need to be executed Avoiding DML protects us from several “bullets” •  Limits •  Multiple calls and trigger “loops” •  Performance issues P.S. Be careful relying on static variables to determine multiple executions Avoid DML
  • 12. 1.  public class AccountHandler extends BaseHandler { 2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) { 3.  Account[] accountsToUpdate = new Account[]{}; 4.  for (Account a : accounts) { 5.  accountsToUpdate.add(new Account(Id = a.Id, Description = a.Name + ': ' + a.AccountNumber); 6.  } 7.  update accountsToUpdate; 8.  } 9.  } What changes could be made to the code below to postpone or isolate DML? Avoid DML – But How?
  • 13. 1.  public class AccountHandler extends BaseHandler { 2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) { 3.  Account[] accountsToUpdate = new Account[]{}; 4.  for (Account a : accounts) { 5.  Account oldAccount = (Account)oldAccounts.get(a.Id); 6.  if ((a.Name != oldAccount.Name) || (a.AccountNumber != oldAccount.AccountNumber)) { 7.  accountsToUpdate.add(new Account(Id = a.Id, Description = a.Name + ': ' + a.AccountNumber)); 8.  } 9.  } 10.  update accountsToUpdate; 11.  } 12. } Method 1: has a relevant field changed? Avoid DML – But How? Advantages / Disadvantages?
  • 14. 1.  public class AccountHandler extends BaseHandler { 2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) { 3.  Account[] accountsToUpdate = new Account[]{}; 4.  for (Account a : (Account[])accounts.values()) { 5.  String newDescription = a.Name + ': ' + a.AccountNumber; 6.  if (a.Description != newDescription) { 7.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription); 8.  } 9.  } 10.  update accountsToUpdate; 11.  } 12.  } Method 2: are we making a change? Avoid DML – But How? Advantages / Disadvantages?
  • 15. 1.  public class AccountHandler extends BaseHandler { 2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) { 3.  Map<Id,SObject> changedAccounts = accounts.clone(true); 4.  Account[] accountsToUpdate = new Account[]{}; 5.  for (Account a : changedAccounts) { 6.  // logic / update a.field1 7.  // logic / update a.field2 8.  // ... 9.  // logic / update a.fieldN 10.  if (!accountsAreEqual(a, accounts.get(a.Id))) { 11.  accountsToUpdate.add(a); 12.  } 13.  } 14.  update accountsToUpdate; 15.  } 16.  } Method 3: are we making a change (complex logic)? Avoid DML – But How? Advantages / Disadvantages?
  • 16. Dealing with “Bullets” Defense #3: Write Great Unit Tests
  • 17. 1.  Design for testability! 2.  Use asserts 3.  Always test in bulk 4.  Test the happy path 5.  Don’t test if the trigger handler is disabled 6.  (?) Test error and exception conditions 7.  (?) Cover all logical branches Write Great Unit Tests
  • 18. 1.  trigger MyAccountTrigger on Account(before insert, before update) { 2.  Account[] accountsToUpdate = new Account[]{}; 3.  for (Account a : (Account[])Trigger.new) { 4.  String newDescription = a.Name + ': ' + a.AccountNumber; 5.  if (a.Description != newDescription) { 6.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription); 7.  } 8.  } 9.  update accountsToUpdate; 10. } Design: why is this code difficult to test? Write Great Unit Tests Suggestions?
  • 19. 1.  public class AccountHandler extends BaseHandler { 2.  public override void bulkAfterUpdate(Map<Id,SObject> oldAccounts, Map<Id,SObject> accounts) { 3.  update Accounts.setDescriptions((Account[])accounts.values()); 4.  } 5.  } 6.  public class Accounts extends Domain { // enterprise patterns 7.  public static Account[] setDescriptions(Account[] accounts) { // reusable! 8.  Utils.preCondition(null != accounts, 'a list of accounts is required'); 9.  Account[] accountsToUpdate = new Account[]{}; 10.  for (Account a : accounts) { 11.  String newDescription = a.Name + ': ' + a.AccountNumber; 12.  if (a.Description != newDescription) { 13.  accountsToUpdate.add(new Account(Id = a.Id, Description = newDescription); 14.  } 15.  } 16.  return accountsToUpdate; // no DML! 17.  } Design: fixes Write Great Unit Tests Advantages / Disadvantages?
  • 20. 1.  @IsTest class TestAccounts extends BaseTest { // base does setup, creates myAdminUser, etc. 2.  static testMethod void testSetDescriptions() { 3.  System.runAs(myAdminUser) { 4.  Account[] accounts = newAccounts('name'); // no DML! 5.  Test.startTest(); 6.  // no accounts 7.  System.assert(Accounts.setDescriptions(new Account[]{}).isEmpty()); 8.  // bulk accounts with changed descriptions 9.  Account[] results = Accounts.setDescriptions(accounts); 10.  Test.stopTest(); 11.  for (Integer i = 0; i < accounts.size(); ++i) { 12.  //TODO assert description was correctly set 13.  } 14.  } 15.  . . . Create a base class; always test in bulk Write Great Unit Tests Advantages / Disadvantages?
  • 21. 1.  @IsTest class TestAccounts extends BaseTest { 2.  static testMethod void testSetDescriptions() { 3.  System.runAs(myAdminUser) { 4.  Test.startTest(); 5.  //TODO test with null values for Name, AccountNumber 6.  //TODO test with blank values for Name, AccountNumber 7.  //TODO test with long values for Name, AccountNumber 8.  //TODO test case where description doesn’t change 9.  //TODO test Accounts.setDescriptions(Accounts.setDescriptions()) 10.  Test.stopTest(); 11.  } 12.  } 13.  } Cover all logic Write Great Unit Tests
  • 22. 1.  @IsTest class TestAccounts extends BaseTest { 2.  static testMethod void testTriggerHandlerAssertions() { 3.  System.runAs(myAdminUser) { 4.  Test.startTest(); 5.  try { 6.  Accounts.setDescriptions(null); 7.  System.assert(false, ‘records are required'); 8.  } catch (PreConditionException ex) {} 9.  Test.stopTest(); 10.  } 11.  } 12.  } Test error conditions & exceptions Write Great Unit Tests
  • 23. 1.  @IsTest class TestAccounts extends BaseTest { 2.  static testMethod void testTriggerHandler() { 3.  if (Utils.triggerIsEnabled('MyAccountTrigger', 'SetAccountDescriptions')) { 4.  System.runAs(myAdminUser) { 5.  Account[] accounts = newAccounts('name'); 6.  insert accounts; 7.  //TODO assert descriptions are NOT set 8.  Test.startTest(); 9.  update accounts; 10.  Test.stopTest(); 11.  //TODO assert descriptions ARE set 12.  } 13.  . . . Test the trigger Write Great Unit Tests
  • 24. Dealing with “Bullets” Defense #4: Use a Good Trigger Framework
  • 25. A framework can provide consistency and save loads of time •  Allows you to specify the processing order •  Makes enabling/disabling individual handlers a snap •  Provides centralized diagnostics and error handling •  Decreases development and ramp-up time Use a Good Trigger Framework
  • 26. Framework Features Use a Good Trigger Framework 1.  trigger MyObj on MyObj__c(after delete, after insert, after undelete, 2.  after update, before delete, before insert, before update) { 3.  new TriggerFactory().handle(MyObj__c.SObjectType); // non-testable / non-reusable code 4.  } // Trigger body is reduced to a single line 1.  public static Handler__c[] handlers { 2.  get { 3.  if (null == handlers) { 4.  handlers = [SELECT ... FROM TriggerSettings WHERE Active__c = true ORDER BY Type__c, Order__c]; 5.  //categorize as desired – SObjectType, subsystem, feature, etc. 6.  } 7.  return handlers; 8.  } 9.  private set; 10. } // Handlers are declared in custom settings, where they can be disabled or the order set
  • 27. Framework Features Use a Good Trigger Framework 1.  public void handle(SObjectType o) { // centralized trigger handler 2.  final String type = String.valueOf(o); 3.  if (!Boolean.valueOf(TriggerSettings.getInstance().EnableTriggers__c)) return; 4.  try { 5.  if (!Boolean.valueOf(TriggerSettings.getInstance().get(‘Trigger_’ + type)) return; 6.  } catch (Exception ex) {} // if no setting exists, handlers are enabled by default 7.  8.  for (Handler__c h : handlers) { 9.  if (h.Active__c && h.Type__c.equalsIgnoreCase(type)) { 10.  final String handlerName = h.Name.trim(); 11.  try { 12.  ITrigger t = (ITrigger)Type.forName(handlerName).newInstance(); 13.  if (null != t) { 14.  execute(t); 15.  } 16.  } catch (NullPointerException ex) {} 17.  } 18.  } 19. } // Centralized dispatcher enforces disabling
  • 28. Framework Features Use a Good Trigger Framework 1.  private void execute(ITrigger handler) { // centralized trigger handler 2.  try { 3.  if (Trigger.isBefore) { 4.  if (Trigger.isInsert) { 5.  handler.bulkBeforeInsert(Trigger.newMap); 6.  for (SObject so : Trigger.new) { 7.  handler.beforeInsert(so); 8.  } 9.  } else if (Trigger.isUpdate) { 10.  handler.bulkBeforeUpdate(Trigger.oldMap, Trigger.newMap); 11.  for (SObject so : Trigger.old) { 12.  handler.beforeUpdate(so, Trigger.newMap.get(so.Id)); 13.  } 14.  } else if (Trigger.isDelete) { 15.  handler.bulkBeforeDelete(Trigger.oldMap); 16.  for (SObject so : Trigger.old) { 17.  handler.beforeDelete(so); 18.  } 19.  } 20.  } else if (Trigger.isAfter) { 21.  . . . // A common, logical ‘execute’ pattern provides consistent routing
  • 29. Framework Features Use a Good Trigger Framework 1.  public virtual class BaseHandler extends BaseClass implements ITrigger { 2.  /** the records that will be inserted when "andFinally" is called */ 3.  protected SObject[] toInsert = new SObject[]{}; 4.  . . . 5.  6.  /** handle bulk "before" processing of insert */ 7.  public virtual void bulkBeforeInsert(Map<Id,SObject> sos) 8.  { Utils.preCondition(Test.isRunningTest() || (Trigger.isBefore && Trigger.isInsert)); } 9.  10.  /** handle bulk "before" processing of update */ 11.  public virtual void bulkBeforeUpdate(Map<Id,SObject> oldSos, Map<Id,SObject> sos) 12.  . . . 13.  /** insert, update, and delete the processed records */ 14.  public virtual void andFinally() { 15.  Utils.preCondition(isValid()); 16.  17.  insert toInsert; 18.  . . . // Base classes make handler writing a snap
  • 31. 1.  Avoid them if possible! 2.  Only do DML when absolutely necessary 3.  Test thoroughly 4.  Use a good trigger framework Writing bulletproof trigger code is challenging Summary
  • 32. Sample frameworks and additional guidance Additional Resources •  Trigger Frameworks and Apex Trigger Best Practices – Kevin O’Hara •  An architecture framework to handle triggers in the Force.com platform – Hari Krishnan •  Trigger Pattern for Tidy, Streamlined, Bulkified Triggers – Tony Scott •  Advanced Apex Programming – Dan Appleman
  • 33. Share Your Feedback, and Win a GoPro! 3 Earn a GoPro prize entry for each completed survey Tap the bell to take a survey2Enroll in a session1