Discover proven Apex best practices. Build scalable and agile codebases for enhanced Salesforce performance.
In this article, we will be offering practical advice on how to structure your Apex code for scalability and maintainability. Readers can anticipate learning about foundational principles such as code clarity, standardization, reusability, and the importance of each class having a single responsibility, as well as how to effectively use tools and processes like Visual Studio Code and a git-based development process.
Here are our 5 key takeaways:
Apex is obviously one of the most powerful features of Salesforce. It’s also one of the easiest ways to rapidly create technical debt within your Salesforce org. After more than a decade of work as a Salesforce engineer and architect, I’ve gotten the chance to dive into Apex codebases at dozens of different organizations at various levels of maturity. And more often than not what I find upon first inspection is a complete mess:
Some of these may seem like fairly innocuous issues, but they’re not. These issues add up over time, leading to code bloat and code smell (more code smell examples). Eventually this leads to user-facing impacts like bugs, data loss, difficulty in deploying new features and even mini “revolts” where whole teams give up and revert to doing certain tasks in spreadsheets.
The good news is that there are a multitude of easy-to-follow best practices and fast-to-implement design patterns which can help save your team time and make it easier to manage your entire Salesforce implementation. What follows is a collection of pro-tips, examples and code snippets which should help you lay down a solid foundation for your Apex development work.
Adopting these practices can help a small team get more done and help keep a big team from getting stuck in the weeds.
Are you tracking your Apex code in a source-control management system (like GitHub or SFDC-specific source control tools like Blue Canvas)? If you aren’t tracking your Apex in a git-based repository of some sort yet, stop reading this article and fix that problem first, then come back.
Source control is the first and most important tool in your toolbox. If you aren’t using it today then you are flying blind. Without the ability to do code reviews on each change coming into production you will struggle to implement even the most basic of best practices. And without detailed change histories on your codebase, retrospective debugging is largely impossible.
Have your source control on point? Great, let’s move on.
All of the recommendations in this article are grounded in a set of basic guiding principles:
It all starts with the tools we use to do our jobs and the way we approach our work.
This can be a tricky one for some teams. Many Salesforce developers have become very accustomed to developing using Salesforce’s built in developer console. Perhaps you even count yourself among them. Other developers started using Sublime Text with MavensMate back in the day and have tried to stick with that tool despite its retirement.
Do yourself a favor and make the switch to Visual Studio Code with the official Salesforce extensions. And make sure everyone who writes code for your org does the same. Getting things setup can be a little fussy (pro-tip: choose a JVM that isn’t from Oracle) but you only have to do it once and it’s worth it! Just follow the instructions here or earn this badge on Trailhead.
Why does this matter?
While you’re at it, you should use the features of VS Code to enforce a couple of key standards:
Note: There are good use cases for using the developer console, just don’t use it to write production code. Running one off scripts using the Apex code executer in the dev console is perfectly fine (though tools like SoqlX do a better job). The test execution/coverage UI in the dev console is also very handy.
As outlined in the introduction, the beating heart of any scalable software development process is your source control system. Tracking your Apex code in a source-control management system (like GitHub or Blue Canvas) is the first step. But in order to get the operational benefits of a more organized system you’ll need to put your git repository at the center of your team’s workflow.
These Salesforce-development-focused articles from the folks at Blue Canvas are a great starting point:
After that you can search around for other “git workflows” (start with the GitHub flow documentation) and figure out what approach works best for your team. Some of the key questions you’ll need to address are:
Every developer is going to write and format code slightly differently and that’s fine. But some level of coding consistency will help you speed up development and prevent bugs. The easiest way to achieve this is by adopting a code style guide and making sure everyone on your team agrees to follow it.
You could build a style guide yourself if you want, but you’ll save a lot of time if you use an existing style guide as a starting point. The best Apex-specific style guide I’ve found is an open source project started by the team at NimbleAMS. The style guide itself is here: NimbleUser Apex Style Guide. It was adapted from Google's Java Style Guide. For more detail check out Nimble's original 2017 launch post .
Every Apex file in your org should have a single, well-defined responsibility. Your goal when designing a Salesforce code architecture, then, is to build a menu of different Apex file types with clearly defined roles and well thought out design patterns connecting them. Then when it comes time to build a new feature, there’s no new architecture design work to be done. It’s just a matter of picking the right patterns from the menu and filling them in with actual business logic.
There are tons of different options out there when it comes to Salesforce patterns (just google “apex design patterns” or “apex enterprise patterns”). What follows in this section is an outline of some of the most useful patterns I’ve come across. Every org is a bit different, though, so it’s important to think through your particular use case to figure out which patterns are right for your situation.
As mentioned above there are many many design patterns to choose from when building out an Apex implementation. This section focuses on a few patterns which I’ve found to be indispensable when trying to maintain a scalable codebase.
There are many different schools of thought around how to structure Apex triggers, but most folks agree on a few things.
Having multiple triggers makes it impossible to control the order of execution, makes it difficult to follow the execution path when debugging, and leads to overall inefficiency. You can read more here.
If you put actual business logic directly into your triggers it makes it impossible to create individual unit tests for each piece of code. This leads to messy, difficult to maintain code. Logic-filled triggers also violate the single responsibility principle and encourage the creation of non-reusable code. This is all solved by the logic-less trigger pattern.
Technically there are many ways you could implement logic-less triggers, but the most popular is to use the trigger handler pattern. This pattern involves turning each trigger into a boilerplate chunk of code which invokes a trigger handler class which, in turn, handles the record filtering logic. The handler class still doesn’t modify any actual database state itself, it just filters records and calls out to helper classes.
Here’s a super simple version of what this looks like in actual code:
This pattern is extremely helpful for writing clean, easy to understand triggers. Many people cite atomic testability as a key advantage of this pattern, which is certainly true. But even more helpful is the clarity that comes by organizing your trigger filtering logic around specific trigger events (before insert, after delete, etc). Having a separate method for each trigger event makes your code way easier to understand and debug.
Some more reading on this topic:
The Service Layer Pattern is a very powerful tool that can help a team create a strong bias towards reusable code. If this concept is new to your team, the Service Layer Badge on Trailhead is a great place to start. The underlying pattern is old and has a long background in many programming languages (including Java).
Once you’re bought into the idea, the implementation is actually pretty simple. The recommended approach is to create a single service class for each Salesforce object for which you have code (“AccountService.cls”, “CampaignMemberService.cls”, etc). Within that class you create static methods which implement different bits of business logic. Your main design constraint is to try and make the service methods themselves as reusable as possible. Keep them generic!
If you find yourself curious about the distinction between a service class and a utility/helper class, read on to the next section.
The Helper/Utility Class Pattern is considered by some to be an anti-pattern. But it is none-the-less a common pattern worth mentioning. And for what it’s worth, I think it can be utilized without causing code smell.
From what I’ve observed within the Salesforce development community, the big difference between a Service Class and a Helper/Utility Class is that a Service Class is focused on a single standard or custom object, while a Helper or a Utility is focused on a particular functional area which may be useful to code across many different objects. Some examples are “DateTimeHelpers” for common date and time manipulation methods, “TestUtilities” for reusable test method logic and “ApiHelpers” for methods that help parse, serialize and deserialize API requests & responses.
Of course, the distinction between a Service Class and a Helper/Utility Class is largely semantic and I’ve seen plenty of counter examples which defy simple categorization.
Whatever standard you settle on, the takeaway I would suggest is that it is helpful to distinguish between classes which contain reusable logic for particular Salesforce objects (“Service Classes”) and classes which focus on other non-SObject-based abstractions (“Helper Classes” or “Utility Classes”).
If you are going to have Helper/Utility classes, here are a few tips:
The last pattern I’ll mention is one that I haven’t seen anyone develop a catchy name for, so I’ll call it the Unified Org Settings Pattern. This pattern has to do with how we manage constant values in Apex code.
When we write Apex, we frequently need to use values which, though constant for a time, may need to be changed later. Usually these are non-sensitive things like record type ids, default field values, admin email addresses, user/queue ids, API urls, batch process configuration settings, etc. (These can also be sensitive values like API keys, more on that later.)
Here’s a breakdown of the most common solutions to storing and managing Apex constants, from worst to best:
Some common problems with using Salesforce Custom Settings:
The Unified Org Settings Pattern
The ideal solution utilizes custom settings but streamlines the developer experience and makes it easier for administrators to manage the settings centrally. This solution also ensures that each Apex file self-documents which custom settings it is using. Here’s how it works:
Here’s some example code to get you started:
The Unified Org Settings pattern reduces the friction needed for developers to implement custom settings in their code. It also makes their code more readable by incorporating a self-documenting “Settings” section at the top of each Apex file. Finally, it streamlines the admin experience by putting all of the various Apex settings into a single unified list.
Salesforce’s Apex is an incredibly powerful tool for enterprise software development. But with great power comes great responsibility. Unscalable Apex code is a real danger, but the patterns and practices above are a great place to start when architecting your Apex codebase for scale and maintainability.
One last piece of advice: The Salesforce development community is wonderful and extremely supportive, but it can also be a bit insular at times. As you work to develop your own set of best practices, don’t be afraid to pull inspiration from other communities and technology stacks. Some of the best Salesforce advice I’ve received has come from experienced web application engineers who’ve never written a line of Apex. Great architecture is universal.
About the author
Hank Holiday is a senior Salesforce developer with over 10 years of experience writing Salesforce code at organizations large and small. You can get in touch and share feedback with him @hankish on Twitter.
As you get more and more familiar with Apex development best practices, learn how our Salesforce Deployment Tools can help you speed up your deployment cycle too. Blue Canvas offers powerful source control and continuous integration tools specifically designed for Salesforce development. With Blue Canvas, you can track changes, collaborate effectively, and ensure the integrity of your codebase throughout the development process and across orgs. Get started with Blue Canvas today and take your Salesforce operations to the next level!
Why is it important to structure and manage the Apex codebase for scale and agility?
What are some common challenges in managing an Apex codebase?
How can I optimize the clarity and readability of my Apex code?
Is there a recommended development tool for Apex coding?
How can source control management benefit Apex development?
What is the Unified Org Settings pattern?
How can Blue Canvas help in managing the Apex codebase?
Can I implement these best practices in an existing Apex codebase?
Are these best practices applicable only to large-scale Salesforce implementations?
How Sysco's team of 40+ developers and admins support a complex Salesforce release flow with Blue Canvas.
Our latest feature offers proactive suggestions so you can avoid dependency errors and better understand relationships between your Salesforce objects.
From your sandbox to a git repository in less than a minute
Why Salesforce DevOps is more than just hooking up a git repo to a Salesforce org
Diving into what it takes to smoothly merge work across Salesforce orgs