Skip to main content

SF-0342 · Concept · Medium

What are helper classes?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

A helper class is a plain Apex class that holds the reusable business logic your trigger handler calls into. The handler decides “which method to call when,” and the helper actually does the work — querying records, transforming data, building the next DML. Helpers are reusable: a handler, a Queueable, an LWC controller, and a batch job can all call the same helper method.

Where helpers fit in the layered model

trigger AccountTrigger          (1-2 lines: delegate to handler)

AccountTriggerHandler           (route by Trigger.operationType)

AccountHelper / AccountService  (reusable business logic)

AccountSelector                 (SOQL queries, kept in one place)

The handler is tied to trigger contexts. The helper is not — it’s pure logic. Anything else in the codebase can call the helper without dragging trigger context with it.

Concrete example

public with sharing class AccountHelper {

    // Reusable: handler calls it, batch job calls it, REST endpoint calls it
    public static void recalcHealthScores(List<Account> accounts) {
        for (Account a : accounts) {
            Decimal score = (a.AnnualRevenue == null ? 0 : a.AnnualRevenue) / 1000;
            if (a.Industry == 'Healthcare') score *= 1.1;
            a.Health_Score__c = score.setScale(2);
        }
    }

    public static Map<Id, Decimal> openCaseCounts(Set<Id> accountIds) {
        Map<Id, Decimal> result = new Map<Id, Decimal>();
        for (AggregateResult ar : [
            SELECT AccountId acct, COUNT(Id) c
            FROM Case WHERE AccountId IN :accountIds AND IsClosed = false
            GROUP BY AccountId
        ]) {
            result.put((Id) ar.get('acct'), (Decimal) ar.get('c'));
        }
        return result;
    }
}
public with sharing class AccountTriggerHandler {
    public void run() {
        if (Trigger.isBefore && Trigger.isInsert) {
            AccountHelper.recalcHealthScores(Trigger.new);
        }
    }
}

The handler is thin. The helper does the work. The helper has no dependency on Trigger.new or Trigger.isBefore — pass it a list, get a list back.

Helper vs Handler vs Service vs Selector

The terms aren’t standardized across orgs, but most teams converge on:

RoleKnows about trigger context?Issues SOQL?Issues DML?Reused across the codebase?
Trigger fileYes (one line of routing)NoNoNo
HandlerYes (dispatches per context)NoNoNo
Helper / ServiceNoSometimesYesYes
SelectorNoYes (only)NoYes

Salesforce’s enterprise pattern (fflib) names them more strictly: Domain, Service, Selector, Unit of Work. A helper sits between Service and a utility — same idea, less ceremony.

Why bother

  • Reusability — the same recalcHealthScores is called from a trigger, a Queueable nightly job, and an admin button.
  • Testability — a helper test passes records in directly, no need to construct a trigger context.
  • Single source of truth — the formula for Health Score lives in one method. When the formula changes, you change it once.

Common interview follow-ups

  • Can a helper class issue DML? — Yes, that’s normal. The handler is the thing that should not contain DML inline; the helper is fine.
  • Is helper the same as utility? — Close. “Utility” usually means generic, stateless helpers (date math, string manipulation). “Helper” usually means domain-specific (Account, Opportunity).
  • Why not put helper logic in the handler? — Then it isn’t reusable, and the handler grows into the 1,500-line monstrosity everyone has seen at least once.

Verified against: Apex Developer Guide — Best Practices for Triggers, Trailhead — Apex Triggers module. Last reviewed 2026-05-17.