Saturday 11 March 2017

One Trigger to Rule Them All? It Depends.

One Trigger to Rule Them All? It Depends

Trg

Introduction

Anyone involved in Salesforce development will be familiar with triggers and the religious wars around how they should be architected. My view is that, like many things in life, there is no right answer - it all depends on the context. The options pretty much boil down to one of the two following options:

One Trigger per Object

This approach mandates a single trigger file that handles all possible actions, declared along the lines of:

trigger AccountTrigger on Account (
  before insert, before update, before delete,
  after insert, after update, after delete, after undelete) {

  // trigger body
}

One Trigger per Object and Action

This approach takes the view that each trigger should handle a single action on an objectf:

trigger AccountTrigger_bu on Account (before update) {

  // trigger body
}
...
trigger AccountTrigger_au on Account (after update) {

  // trigger body
}

I’ve read many blog posts stating flat out that the first way is best practice. No nuances, it’s just the right way and should be used everywhere, always. Typically the reason for this is that is how the author does it, therefore everyone should. I strongly disagree with this view. Not that one trigger per object shouldn’t be used, but that it shouldn’t be used without applying some thought.

Note: one trigger per object and action is the maximum granularity - never have two triggers for the same action on the same object as then you have no control over the order of execution and this will inevitably bite you. Plus you’ve striped business logic across multiple locations and made life harder for everyone.

Consulting versus Product Development

The reason I have this view is that I work across consultancy and product development at BrightGen. Most of the work I do nowadays is related to our business accelerators, such as BrightMEDIA, but I still have Technical Architect responsibility across a number of consulting engagements, which are often implementations for companies that don’t have a lot of internal Salesforce expertise, and what they have isn’t the developer skill set.

One message I’m always repeating to our consultants is to have some empathy with our customers and think about those that come after us. Thus we use clicks not code and try to take the simplest approach that will work, so that we don’t leave the customer with a system that requires them to come back to us every time they need to change anything. Obviously we like to take our customers into service management after a consultancy engagement, but we want them to choose to do that based on the good job that we have done, rather than because we have locked them in by making things complex.

Sample Scenario

So here’s a hypothetical example - as part of a solution I need to take some action when a User is updated, but not for any other trigger events. At some point later a customer administrator wants to know if there is any automated processing that takes place after a user is inserted to triage a potential issue.

If I’ve gone with the one trigger per object and action combination, they can simply go to the setup page for the object in question and look at the triggers. The naming convention makes it clear that the only trigger in place is to handle the case when a user is updated, so they can stop this particular line of enquiry (assuming my Evil Co-Worker hasn’t chosen an inaccurate name just to cause trouble).

Screen Shot 2017 03 11 at 07 29 35

If I’ve gone with one trigger per object, the administrator is none the wiser. There is a single trigger, but nothing to indicate what it does. The administrator then has to look into the trigger code to figure out if there is any after insert processing. What they will then find is one of two things:

  • A load of wavy if statements checking the type of action - before vs after, insert vs update etc - and then calling out to external code. Most developers try to make sure that an external method is called only once, so you often end up with a wall of if statements for the administrator to enjoy
  • Delegation to a trigger handler, leaving the admin to look at another source file to try to figure out what is happening.

Now I don’t know about you, but if administrators are having to look at my source code, and even worse trying to understand Apex code to figure out something as basic as this, I’d feel like I’d done a pretty poor job.

Enter the Salesforce Optimizer

The Spring 17 Release introduced the Salesforce Optimiser - an external tool that analyses your implementation and sends you the results - here’s what it has to say about my triggers:

Screen Shot 2017 03 11 at 07 54 33

And there’s the dogma writ large again - a big red warning alert saying I should have one trigger per object, just because. Don’t get me wrong, I think the Salesforce Optimizer is a great idea and has the potential to be a real time saver, and that the intention is to help, but it’s a really blunt instrument that presents opinion as fact.

The chances are at some point my customers will run this and ask me why I’ve gone against the recommended approach, even though in their case it is absolutely the appropriate approach. I find I have no problem explaining this to customers, but I do have to take the time to do that. Thanks for throwing me under the bus Salesforce!

In Conclusion

What you shouldn’t take away from the above is that one trigger per object is the wrong approach - in many situations it’s absolutely the right approach and it’s the one I use in some of my product development. In other situations it isn’t the right approach and I don’t use it. What you should take away is that it’s important to think about how to use triggers for every project you undertake - going in with a dogmatic view that there is one true way to do things and everything will be brute-forced into that may make you feel like a l33t developer but is unlikely to be helpful in the long term. it may also mark you out as a Rogue High Performer, and you really don’t want that.

 

Sunday 5 March 2017

Salesforce DX Week 1

SalesforceDX Week 1

(NOTE: This post is based on the SalesforceDX pilot which, like all pilots, may never make it to GA. I bet it does though!)


Scratch

Introduction

The SalesforceDX pilot started a week or two ago and BrightGen were lucky enough to be selected to participate (thanks to the sterling efforts of my colleague Kieran Maguire who didn’t screw up his signup, unlike me!). This week I’ve managed to spend a reasonable amount of time reading the docs and trying out the basics and it’s clear already that this is going to be a game changer. 

There will be bugs!

While this isn’t the first  pilot that I’ve been involved in, but it’s by far the largest in terms of new functionality - a new version of the IDE, a new CLI with a ton of commands and a new type of org. A pilot is a two way street - you get to play with the new feature long before it becomes (if it ever does!) GA, but the flip side is that this won’t be tested to destruction like a GA feature. With the best will in the world there’s no way that Wade Wagner and co could test out every possible scenario, so some stuff will break, and that’s okay. When things break (or work in a non-intuitive way) you sometimes get a chance to influence how the fix works, which is pretty cool. Be a grown up though - report potential issues in a measured way with as much detail as you can gather - it’s always embarrassing when you have to climb down from a high horse when you realise that you made the mistake, not the tool!

Scratch Orgs

Scratch orgs are probably the feature I’ve been most excited about in SalesforceDX. I run the BrightMEDIA team at BrightGen and setting up a developer org for a new member of our team takes around half a day. After the initial setup, every release needs to be executed on each dev org as well as the target customer or demo org(s), which consumes a fair amount of time with a weekly release cadence. There’s also the problem of experimentation - often devs will try something out, realise it’s not the best way to do it, but not tear down everything they built. Over time the dev org picks up baggage which the dev has to be careful doesn’t make its way into version control.

Scratch orgs mitigate the first problem and solve the second. A scratch org is ephemeral - it is created quickly from configuration and should only last for the duration of the development task you are carrying out. When we setup a developer edition we have to contact Salesforce support to get the apex character limit increased and multi-currency enabled. Scratch orgs already have an increased character limit and features can be defined in the configuration. Here’s the scratch org configuration file for one of my projects:

{
  "Company": "KAB DEV",
  "Country": "GB",
  "LastName": "kbowden",
  "Email": "keir.bowden@googlemail.com",
  "Edition": "Developer",
  "Features": "Communities;MultiCurrency",
  "OrgPreferences" : {
    "ChatterEnabled": true,
    "S1DesktopEnabled" : true,
    "NetworksEnabled": true,
    "Translation" : true,
        "PathAssistantsEnabled" : true
  }
}

The features attribute : 

"Features": "Communities;MultiCurrency"

enables communities and multi-currency when my org is created, saving me a couple of hours raising a case and waiting for a response right off the bat.

Creating a Scratch Org

Is a single command utilising the new CLI:

> sfdx force:org:create --definitionfile config/workspace-scratch-def.json

 and it’s fast. I’ve just created an org for the purposes of this blog and I’d be surprised if it took more than a minute, although the DNS propagation of the new org name can take a few more minutes. You don’t have to worry about passwords with scratch orgs, it’s all handled by the CLI. To “login” I just execute:

> sfdx force:org:open

and a browser window opens and I’m good to go. Accessing the Manage Currencies setup node shows that multi-currency has indeed been enabled.

Screen Shot 2017 03 04 at 15 46 04

There’s a bit more to it than this in our case - a few packages have to be installed for example - but so far it looks like I can script all of this, which means a new developer just runs a single command to get an org they can start work in. Note that there’s just the standard developer edition data in here - I haven’t found time to play with the data export/import side of the CLI yet so that will have to wait for another day.

Managing Code

If you are familiar with the git paradigm of pulling and pushing changes from/to a remote location, the SalesforceDX source management is simple to pick up. You don’t get version control, but you do get automatic detection of what has changed and where. The docs state that this functionality is only available for scratch orgs and we still have to use the metadata API to push to sandbox/production orgs, which seems fair enough a pilot to me.

Detecting Differences

In my scratch org I create a simple lightning component in the developer console:

<aura:component >
	<h1>I'm a simple Lightning Component</h1>
</aura:component>

In current development process I have a script to extract the Lightning metadata and copy it into my source directory. With scratch orgs it’s a fair bit easier.

I can figure out what has changed by running the status subcommand:

> sfdx force:source:status

State Full Name Type Workspace Path
────────── ───────── ──────────────
Remote Add Simple AuraDefinitionBundle

Pulling Code from the Scratch Org

 to extract the new code from the org to my workspace in local filesystem:

> sfdx force:source:pull


State Full Name Type Workspace Path
─────── ───────── ──────────────────── ───────────────────────────────────────────────────────────
Changed Simple AuraDefinitionBundle /Users/kbowden/SFDX/Blog/force-app/main/default/aura/Simple

I can then list the contents of my workspace and there is my new component:

> ls force-app/main/default/aura/

Simple

Pushing Code to the Scratch Org

If I edit the component locally, the status subcommand picks that up too:

> sfdx force:source:status

State Full Name Type Workspace Path
───────────── ───────────────── ──────────────────── ──────────────────────────────────────────────────────
Local Changed Simple/Simple.cmp AuraDefinitionBundle force-app/main/default/aura/Simple/Simple.cmp-meta.xml
Local Changed Simple/Simple.cmp AuraDefinitionBundle force-app/main/default/aura/Simple/Simple.cmp

and I can publish these changes to the scratch org via the push subcommand:

> sfdx force:source:push

State Full Name Type Workspace Path
─────── ───────── ──────────────────── ──────────────────────────────────
Changed Simple AuraDefinitionBundle force-app/main/default/aura/Simple
 

Screen Shot 2017 03 05 at 07 49 03

Scratch Orgs are Temporary

Unlike developer orgs, scratch orgs are not intended to persist. In fact I’ve seen docs that state they may be deleted at any point in time. Although I’d imagine in reality it will be based on lack of use, it doesn’t matter as if your scratch org disappears, you can just spin up a new one with the same setup, push your local code and you are back where you were. This does mean you need to treat your local filesystem as the source of the truth, but that’s pretty much how I work anyway.

This way scratch orgs don’t accumulate any baggage, and you don’t have to worry about destroying anything. If you don’t put it into version control, it won’t be there in the future.

One Org Shape to Rule them All

The configuration, data and code that make up your scratch org can be considered a template, especially if the setup is all scripted. This means that my team and I just need to update a single org shape “template" with changes that need to be applied to every development environment. Then we just spin up new scratch orgs and we can be sure that we are all in step with each other, which will save us time on many levels. 

Related Posts