3 Apex Design Patterns for Your Salesforce Development Team
Apex is powerful, but with great power comes great responsibility. Because if you aren’t careful with Apex and custom code on your Salesforce environment, there’s a good chance you wind up creating technical debt. This is all the more important because Salesforce orgs are often monoliths that run for decades.
After over 10 years working as a Salesforce engineer and architect, I’ve seen my share of code bases, good and bad. A small investment in the structure of your Apex code upfront will save you a lot of time in the long run. I cover three Apex design patterns that can be easily adopted by your Salesforce developers that will have a big impact on the health of your Salesforce code base. It’s not just academic – it will lead to fewer bugs and more on-time releases.
Apex Codebases, Code Bloat and Code Smell
Unfortunately, more often than not I find that Apex codebases are a mess:
- I find multiple triggers for a single object with competing functionality
- I see boilerplate hunks of code that were copied and pasted between classes and methods throughout the repository
- I notice people hard code values all over their Apex (e.g. record type IDs, admin email addresses, API urls, etc.)
- I struggle to decipher file names like “qtrReportSumCont4.cls” and “CoTrig_v4_JohnApr19.trigger”
- I read decades old Apex that has lost all semblance of formatting
These issues add up and lead to code bloat and code smell which leads to bugs that your end-users will run into and your Salesforce developer and admin team will struggle to rollback and squash (not to mention teams abandoning processes and going back to doing things in spreadsheets).
However, it is possible to structure your Apex codebase for agility and scale. Now I will cover the three Apex design patterns that can be easily adopted by your Salesforce developers.
Prerequisites: Version Control and Code Style Guide
Before we can begin, I have to ask: are you using a source control system like GitHub or a Salesforce specific tool like Blue Canvas? If you’re not tracking Apex in a Git repo, do that first. You can use generic tools like GitHub, but Salesforce specific tools like Blue Canvas can get you up and running with DevOps and Git in a matter of minutes.
Source control is essential for implementing these design patterns because you need the ability to do code reviews to ensure the team follows best practices. Furthermore, without version control you can’t even visualize or see your code very well. Without that history and visibility you won’t have a good sense of what’s going on. So please, do this first. Once that’s done, you can implement the following patterns.
1. Apex Trigger Patterns
There are many different schools of thought around how to structure Apex triggers, but most folks agree on a few things:
- Only create one trigger per object – multiple triggers make controlling the order of execution difficult and leads to inefficiency
- Keep business logic out of triggers – this is key for atomic unit tests
Too many teams have too many Apex triggers. A simple rule: one trigger per object will go a long way to helping sort out your org.
Slightly more complex is keeping your actual business logic outside of your triggers. If you don’t do this it will make it difficult to atomitize your unit tests for each piece of code. Implementing so-called “logic-less” triggers can be done using the trigger handler pattern. Atomic testability is the key benefit of this pattern. It also helps clarify your codebase because you can organize your trigger filtering logic by specific trigger events (e.g. before delete, after insert, etc.). Ensuring a different method for every trigger event makes debugging and readability much easier.
2. Apex Service Classes
This pattern is actually pretty simple to implement, once you get your head around it. You can check out this Trailhead as an introduction: Service Layer Badge. Each Salesforce object gets its own singular service class (e.g. “LeadService.cls” or “AccountSerivice.cls”). In each class you can add static methods that implement different bits of business logic. The goal is to make the service methods as reusable as possible. Remember to keep them generic and DRY!
3. Helper and Utility Classes in Apex
While a service class focuses on a single standard or custom object, helper and utility classes focus on functional areas which may touch multiple objects. A “DateTimeHelper” for date and time manipulation is a good example. Or “ApiHelpers” which can help parse API requests and responses. The key distinction is to have reusable logic for Salesforce objects in service classes while abstractions that don’t depend on a single class can go into a helper or utility class.
A few key things to keep in mind:
- Each class should be specific rather than generic. Think: “APIHelpers” rather than “MiscHelpers”.
- Document your helpers in one place and keep it updated. Make “adding documentation” a part of your code review process or a manual step in your deployment request.
- Include your helper and utility classes in your training and onboarding for new developers. This will save a lot of headache down the line if they know up front where they can reuse code. This can also be reinforced during code reviews.
These three patterns will go a long way to helping keep your Apex code well structured and maintainable. They will actively reduce bugs and speed up the delivery of new features and functionality. But implementing them doesn’t happen overnight. It takes time to invest in process and tooling. These are just the first steps. For more information, I recommend Trailhead and how to establish an Apex code style guide and IDE tooling.
Interestingly, the trailhead referenced in the service layer actually says the opposite of what is written here. In the Trailhead, service classes should not be used for object specific functions.
I think salesforce should generate an Warning message when a user is creating a new trigger on the same object. For example if an Account Trigger is already created and a user is creating another trigger on Account then Salesforce should generate an Warning saying – “A trigger on this Object is already present Do you want to continue? “