Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Diagram illustrating the structure of a well-organized Salesforce Trigger Class handler pattern in Apex.
Apex

Salesforce Trigger Class: Why and How to Use Handlers

Putting all your logic inside a trigger is how you end up with technical debt nobody wants to touch. Here is how a Salesforce trigger class makes that Apex reusable and much easier to test.

The short answer

A Salesforce trigger handler class moves business logic out of the trigger definition and into a dedicated Apex class. The trigger file stays short, the logic can be reused from batch jobs and APIs, and unit tests get much simpler.

Key takeaways Move business logic out of the trigger file into a dedicated Apex handler class so batch jobs and APIs can reuse it. Keep one trigger per object, or the order of execution stops being predictable. Bulkify everything: process record collections, and keep SOQL queries and DML statements outside loops. Use a static boolean variable in your handler class to stop trigger recursion and the stack depth errors that follow. Declare trigger handler classes with the with sharing keyword so record-level security is enforced explicitly.

Why your project needs a Salesforce trigger class

If you've spent any time in messy Apex, you already know why a Salesforce trigger class helps. It keeps your logic organized so you don't end up with a thousand-line trigger file that nobody wants to touch. In my experience, skipping this step is the fastest way to create technical debt that'll haunt you for years.

A Salesforce trigger class, often called a handler, is just a regular Apex class where you put your business rules. Instead of writing code directly inside the trigger, you call methods on this class. It sounds like an extra step. It stops being one the moment the logic gets complex.

Why we stop putting logic in triggers

I've seen teams try to "keep it simple" by putting everything in the trigger. Then they need that logic in a Batch job or a REST API and they're stuck. A handler class makes the code reusable: you can call calculateTax() from the batch job, the API, or the trigger.

Then there's testing. Triggers are a pain to test because you have to insert records every single time to see whether your logic works. With a class you can often test specific methods in isolation, which makes your unit tests run faster and feel a lot less clunky.

A slim Salesforce trigger next to the Apex handler class it delegates to.

A slim trigger on the left, the handler class it delegates to on the right.

How to build a basic Salesforce trigger class

The most common pattern is what I call the "Slim Trigger." The trigger itself does no heavy lifting. It checks the context (whether we're in a "before insert" or an "after update" state) and hands the work off to the Salesforce trigger class:

trigger AccountTrigger on Account (before insert, before update) {
    AccountTriggerHandler handler = new AccountTriggerHandler();
    
    if (Trigger.isBefore) {
        if (Trigger.isInsert) {
            handler.handleBeforeInsert(Trigger.new);
        }
        if (Trigger.isUpdate) {
            handler.handleBeforeUpdate(Trigger.new, Trigger.oldMap);
        }
    }
}

The handler class is where the real work sits, and it reads far better once the logic is separated out. If you're still debating when to use code over automation, this level of organization is a strong argument for Apex.

public with sharing class AccountTriggerHandler {
    public void handleBeforeInsert(List<Account> newAccounts) {
        for (Account acc : newAccounts) {
            // Simple logic: default the industry if it's blank
            if (String.isBlank(acc.Industry)) {
                acc.Industry = 'Technology';
            }
        }
    }

    public void handleBeforeUpdate(List<Account> newAccounts, Map<Id, Account> oldMap) {
        for (Account acc : newAccounts) {
            Account oldAcc = oldMap.get(acc.Id);
            // Only do something if the revenue changed
            if (acc.AnnualRevenue != oldAcc.AnnualRevenue) {
                // Logic goes here
            }
        }
    }
}

One thing that trips people up

Recursion is the one that catches people. If your trigger updates a record, which then fires the same trigger again, you'll hit a limit faster than you can blink. Most developers use a simple static boolean variable in their handler class to prevent this. It's a quick fix that saves you from a lot of "Maximum stack depth reached" errors.

Pro tip: Never put a SOQL query or a DML statement inside a loop in your handler. It's the number one way to break your org during a bulk data load. Collect your IDs first and run the query once.

Best practices for your Salesforce trigger class

Everyone has a favorite framework, but the basics stay the same. Follow these rules and you'll be ahead of 90% of the developers out there.

  • One trigger per object. Don't create five different triggers on the Account object. It makes the order of execution impossible to predict.
  • Keep the trigger slim. The trigger file should only be a few lines long. If there's a "for loop" in your trigger file, you're doing it wrong.
  • Bulkify everything. Always assume you're processing 200 records at once. Even if you think it'll only ever be one record, someone will eventually use the Data Loader and break your code.
  • Use "with sharing" and be intentional about security. A class lets you explicitly define whether you want to respect the user's permissions or run in system mode.

If you're prepping for a job search, you'll definitely see this come up in Apex trigger interview questions. Interviewers love to ask why we use handlers instead of putting code in the trigger, so be ready to talk about maintainability and testing.

Key takeaways

  • A Salesforce trigger class separates the "when" (the trigger) from the "what" (the logic).
  • Handlers make your code easier to test and much easier to reuse in other parts of the system.
  • Bulkification is mandatory. Never put queries or DML inside loops.
  • Recursion control is essential to prevent your triggers from firing in an infinite loop.

Using a Salesforce trigger class is about making your life easier in six months. When a bug pops up or a client wants a new feature, you won't have to dig through a messy, unorganized trigger file to find where anything happens.

Frequently asked questions

Why should you use a trigger handler class in Salesforce?

A trigger handler class keeps the execution context separate from the business logic, so the trigger file never grows into something unmaintainable. Developers can reuse the methods from batch jobs or APIs, and test one logic method in isolation without inserting test records every time.

How do you prevent trigger recursion in Salesforce?

Declare a static boolean variable in your handler class and use it to control execution. That stops the trigger running its logic again and again and exceeding the maximum stack depth limit when an update operation re-fires it.

Why should you only have one trigger per object in Salesforce?

Multiple triggers on one object make the order of execution impossible to predict. One trigger per object keeps the operations running in a consistent, controlled sequence.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment