Business logic written inside a trigger

ID

apex.maintainability.avoid_logic_trigger

Severity

high

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Api Design

Language

Apex

Description

Reports control flow, DML or a query written directly in a .trigger file instead of in a handler class the trigger delegates to. The context router - an if on a Trigger context variable whose branches call a handler - is the recommended shape and is not reported.

Rationale

A trigger body is the one piece of Apex that cannot be unit-tested in isolation, cannot be reused, and cannot be ordered against the other triggers on the same object - the platform decides that. Logic that lives there is also invisible to the class-level tooling a team already uses: it has no interface, no constructor to inject a mock into, and no way to be called from a batch that needs the same behaviour. Once two triggers on one object both carry logic, the execution order between them is undefined.

The following code illustrates the pattern detected by this rule:

trigger AccountTrigger on Account (before insert, before update) {
    if (Trigger.isBefore) {
        AccountTriggerHandler.handleBefore(Trigger.new, Trigger.oldMap);
    } else {
        AccountTriggerHandler.handleAfter(Trigger.new, Trigger.oldMap);
    }

    // FLAGGED: Business logic written inside a trigger
    if (Trigger.new.size() > 200) {
        AccountTriggerHandler.warnOnBulk();
    }

    // FLAGGED: Business logic written inside a trigger
    for (Account a : Trigger.new) {
        a.Name = a.Name.trim();
    }

    // FLAGGED: Business logic written inside a trigger
    insert new Task(Subject = 'follow up');

    if (Trigger.isBefore && Trigger.isInsert) {
        AccountTriggerHandler.handleBeforeInsert(Trigger.new);
    } else if (Trigger.isAfter && Trigger.isUpdate) {
        AccountTriggerHandler.handleAfterUpdate(Trigger.new, Trigger.oldMap);
    }
}

Remediation

Move the body into a handler class and leave one delegation call in the trigger, e.g. AccountTriggerHandler.run(Trigger.operationType, Trigger.new, Trigger.oldMap);. The handler is then a normal class: testable, reusable from a batch, and explicit about the order in which each step runs.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved