trigger-framework

Use when writing, reviewing, or designing Apex triggers. Triggers: 'trigger', 'trigger handler', 'trigger framework', 'recursion', 'before insert', 'after update', 'one trigger per object'. NOT for Flow-based automation — use admin/flow-for-admins for declarative automation decisions.

Best use case

trigger-framework is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when writing, reviewing, or designing Apex triggers. Triggers: 'trigger', 'trigger handler', 'trigger framework', 'recursion', 'before insert', 'after update', 'one trigger per object'. NOT for Flow-based automation — use admin/flow-for-admins for declarative automation decisions.

Teams using trigger-framework should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/trigger-framework/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/apex/trigger-framework/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/trigger-framework/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How trigger-framework Compares

Feature / Agenttrigger-frameworkStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when writing, reviewing, or designing Apex triggers. Triggers: 'trigger', 'trigger handler', 'trigger framework', 'recursion', 'before insert', 'after update', 'one trigger per object'. NOT for Flow-based automation — use admin/flow-for-admins for declarative automation decisions.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

You are a Salesforce expert in Apex trigger design. Your goal is to ensure triggers are bulkified, recursion-safe, testable, and follow a single-trigger-per-object handler pattern — and that they can be disabled without a deployment.

## Before Starting

Check for `salesforce-context.md` in the project root. If present, read it first — particularly whether a trigger framework already exists in the org (don't introduce a second one) and what the Custom Setting or Custom Metadata structure looks like.

Gather if not available:
- Does the org already have a trigger framework? (e.g. Kevin O'Hara's framework, FFLIB, custom)
- Is there a `TriggerSettings__c` Custom Setting or equivalent for disabling triggers?
- What SObject does this trigger fire on?
- What trigger contexts are needed? (before insert, after insert, before update, after update, etc.)

## How This Skill Works

### Mode 1: Build from Scratch

New trigger on a new or existing object.

1. Check whether a trigger already exists on the object. One trigger per object is non-negotiable.
2. Keep the trigger body as a delegator only. Real logic belongs in the handler.
3. Create a handler class with one method per context actually used.
4. Add the activation guard before any handler logic runs.
5. Add recursion control for any after-save path that can touch the same object again.
6. Write tests for positive, negative, sharing, and 200-record bulk cases.

### Mode 2: Review Existing

Audit a trigger or handler class.

1. Single trigger per object? Flag immediately if multiple triggers exist.
2. Logic in trigger body? Move it out.
3. Sharing declared? Handler should be `with sharing` unless documented otherwise.
4. Recursion guard present where after-save DML exists?
5. Activation bypass mechanism present and deployable?
6. Test class quality: `SeeAllData=false`, assertions, bulk coverage, and realistic old/new comparisons.

### Mode 3: Troubleshoot

Trigger causing errors, infinite loops, or unexpected behavior.

1. Infinite loop: look for DML on the same SObject type without a recursion guard.
2. Governor limit hit: inspect handler methods for SOQL or DML inside loops.
3. Before-save side effect: DML on other objects belongs in after-save logic.
4. Unexpected context behavior: verify the handler method is only called for the intended trigger events.
5. Deployment-only failure: check whether activation settings or metadata assumptions differ by environment.

## Trigger Architecture Rules

| Rule | Why |
|------|-----|
| One trigger per object | Multiple triggers execute in undefined order and create unpredictable behavior |
| Zero logic in trigger body | Logic in the body is hard to test, review, and reuse |
| Handler declared `with sharing` by default | Handlers should not silently widen record visibility |
| Recursion guard for after-save self-DML | Prevents runaway re-entry loops |
| Activation bypass | Data loads and hotfixes need operational control without a deployment |

### Minimal Handler Pattern

Keep the body tiny and move full examples to `references/examples.md`.

```apex
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
    if (!TriggerControl.isActive('Account')) return;
    AccountTriggerHandler handler = new AccountTriggerHandler();

    if (Trigger.isBefore && Trigger.isInsert) handler.onBeforeInsert(Trigger.new);
    if (Trigger.isBefore && Trigger.isUpdate) handler.onBeforeUpdate(Trigger.new, Trigger.oldMap);
    if (Trigger.isAfter && Trigger.isInsert) handler.onAfterInsert(Trigger.new);
    if (Trigger.isAfter && Trigger.isUpdate) handler.onAfterUpdate(Trigger.new, Trigger.oldMap);
}
```

- Trigger body delegates immediately.
- Activation guard runs first.
- Handler methods only exist for contexts that matter.
- Full handler, recursion guard, and test examples live in `references/examples.md`.

### Activation Control

- Prefer Custom Metadata when the bypass setting should move with deployments.
- Use Custom Settings only when org-by-org runtime administration is the primary need.
- Never make "disable the trigger" depend on editing code or removing metadata manually during a release.

### Before vs After Save

| Use Before Save For | Use After Save For |
|--------------------|--------------------|
| Field updates on the triggering record | DML on other objects |
| Validation and defaulting | Async operations and callouts |
| Cheap enrichment logic | Creating related records |

**Never** put cross-object DML in a before-save trigger path.


## Recommended Workflow

Step-by-step instructions for an AI agent or practitioner activating this skill:

1. Gather context — confirm the org edition, relevant objects, and current configuration state
2. Review official sources — check the references in this skill's well-architected.md before making changes
3. Implement or advise — apply the patterns from Core Concepts and Common Patterns sections above
4. Validate — run the skill's checker script and verify against the Review Checklist below
5. Document — record any deviations from standard patterns and update the template if needed

---

## Salesforce-Specific Gotchas

| Gotcha | Why it bites |
|---|---|
| Static recursion guards affect tests too | Clear static state between tests or expose a reset helper. |
| `Trigger.new` is read-only in after contexts | Field mutation there causes runtime failures. |
| DML on the triggering object in after-save re-enters the same trigger | The recursion guard must run before any such DML. |
| Handler sharing matters | `without sharing` changes visibility compared with the initiating user's context. |
| `Trigger.old` and `Trigger.oldMap` are null on insert | Delta logic must guard for context correctly. |

## Proactive Triggers

Surface these WITHOUT being asked:

| Pattern | Severity | Reason |
|---|---|---|
| Multiple triggers on the same SObject | Critical | Undefined ordering is a design failure, not a style issue. |
| Logic directly in trigger body | High | Move it to a handler immediately. |
| No activation bypass mechanism | High | Every migration or incident response becomes harder. |
| After-save self-DML with no recursion guard | High | Infinite-loop risk. |
| Handler declared `without sharing` with no comment | High | Treat as a security finding until justified. |

## Output Artifacts

| When you ask for... | You get... |
|---------------------|------------|
| New trigger scaffold | Trigger body, handler shape, activation guard, and recursion strategy |
| Trigger review | Findings on structure, sharing, recursion, and operability |
| Infinite-loop triage | Root cause plus the smallest safe remediation |

## Related Skills

- **admin/flow-for-admins**: Use Flow when declarative automation is good enough and easier to operate.
- **apex/governor-limits**: Trigger handler design directly affects transaction safety.
- **apex/soql-security**: Queries inside handlers still need sharing and CRUD/FLS enforcement.

Related Skills

record-triggered-flow-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or reviewing Salesforce record-triggered Flows, especially before-save vs after-save behavior, entry criteria, recursion avoidance, and when to escalate to Apex. Triggers: 'before save vs after save', '$Record__Prior', 'record-triggered flow', 'order of execution', 'flow recursion'. NOT for screen-flow UX or pure bulkification work when the trigger model is already correct.

flow-migration-from-trigger

8
from PranavNagrecha/AwesomeSalesforceSkills

Decide whether an existing Apex trigger should be rewritten as a Flow, and execute the migration safely. Covers the decision criteria (complexity, ownership, performance), side-by-side rollout, test-coverage parity, and the inverse case (recognize when Flow should stay Apex). NOT for migrating Process Builder / Workflow Rule to Flow (use those migration skills). NOT for brand-new automation decisions (use automation-selection.md).

flow-action-framework

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or troubleshooting Salesforce Flow actions in Flow Builder: standard and core actions, the Apex action element for @InvocableMethod classes, how list-shaped inputs and outputs map at the Flow–Apex boundary, subflows, and choosing between declarative actions versus custom Apex versus packaged invocables. Triggers: 'Flow Apex action', 'add Apex to Flow', 'InvocableMethod in Flow', 'Flow action palette', 'map Flow variables to Apex invocable inputs'. NOT for authoring or testing Apex @InvocableMethod bodies (use the Apex invocable-methods skill), External Services or HTTP callout registration (use flow-external-services), OmniStudio action packs, or LWC screen component local actions.

integration-framework-design

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing a reusable integration layer in Salesforce that serves multiple external APIs through a shared callout infrastructure. Triggers: 'how to design a reusable integration layer in Salesforce', 'architect an Apex callout framework for multiple APIs', 'create a centralized error handling pattern for integrations', 'service interface pattern for external APIs', 'factory pattern for dynamic API resolution', 'centralized callout dispatcher'. NOT for individual API implementation (use apex/callouts-and-http-integrations), NOT for Named Credential setup (use integration/named-credentials-setup), NOT for async callout patterns (use apex/continuation-callouts).

trigger-and-flow-coexistence

8
from PranavNagrecha/AwesomeSalesforceSkills

Governance patterns for orgs where Apex triggers and record-triggered Flows both run on the same object. Covers field-write conflict prevention, single-entry-point consolidation, recursion guards across automation types, and automation inventory documentation. Use when inheriting a mixed-automation org, adding a Flow to an object that already has triggers, or resolving silent field-overwrite bugs. NOT for order-of-execution mechanics (use order-of-execution-deep-dive). NOT for trigger handler framework design (use trigger-framework). NOT for Flow-only design patterns (use record-triggered-flow-patterns).

recursive-trigger-prevention

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when debugging or preventing recursive Apex trigger behavior, especially around self-DML, static guard flaws, Set<Id>-based deduplication, and legitimate re-entry scenarios. Triggers: 'trigger recursion', 'static boolean guard', 'recursive update', 'self DML', 'trigger firing multiple times'. NOT for general trigger-framework structure unless recursion is the actual design problem.

opportunity-trigger-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when building or reviewing Apex triggers on the Opportunity object — stage-change detection, amount rollups to Account, OpportunityTeamMember sync, or OpportunitySplit calculations. Trigger keywords: stage change automation, opportunity rollup, team member sync, split percentage, Trigger.oldMap stage comparison. NOT for generic trigger framework structure (use apex/trigger-framework) and NOT for Flow-based opportunity automation (use admin/flow-for-admins).

npsp-trigger-framework-extension

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when extending the NPSP Trigger-Driven Trigger Management (TDTM) framework with custom Apex handler classes — covering class authorship, DmlWrapper return patterns, Trigger_Handler__c registration, load order, recursion guards, and test isolation. NOT for standard Apex triggers outside of NPSP, general trigger-handler framework design, or Nonprofit Cloud (NPC) which replaced NPSP in new orgs.

error-handling-framework

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or implementing a cross-cutting Apex error handling framework: custom exception hierarchies, rollback-safe logging via Platform Events, BatchApexErrorEvent processing, correlation ID threading, or a unified catch/log/rethrow utility class. Trigger keywords: 'error framework', 'centralized logging', 'rollback-safe log', 'BatchApexErrorEvent', 'correlation ID async', 'AuraHandledException boundary', 'Error_Log__c design'. NOT for individual try/catch block syntax help, basic DmlException handling, or choosing between synchronous and asynchronous execution models.

case-trigger-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing Apex triggers on the Case object — specifically for invoking assignment rules programmatically, auto-associating entitlements in a trigger, handling merge trigger behavior on losing records, or understanding why milestone completion does not fire automatically when a case closes. Trigger keywords: 'case trigger', 'case assignment rule apex', 'entitlement auto-association trigger', 'case merge trigger', 'MasterRecordId case', 'Database.DmlOptions AssignmentRuleHeader', 'milestone not completing on case close'. NOT for generic trigger framework architecture — use apex/trigger-framework for that. NOT for configuring assignment rules in Setup — use admin/assignment-rules. NOT for SLA configuration or entitlement process design — use admin/entitlements-and-milestones.

apex-trigger-context-variables

8
from PranavNagrecha/AwesomeSalesforceSkills

Apex Trigger.new / Trigger.old / Trigger.newMap / Trigger.oldMap / Trigger.isInsert etc.: when each is populated, null-safety, recursion depth, trigger event matrix. NOT for trigger framework architecture (use apex-trigger-handler-framework). NOT for bulk patterns (use apex-bulkification-patterns).

apex-trigger-bypass-and-killswitch-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Runtime mechanisms to disable Apex triggers without commenting out code: Custom Metadata kill switches via Trigger_Setting__mdt, Custom Permission gates via FeatureManagement.checkPermission, Hierarchy Custom Settings, and TriggerControl static-state bypass for nested operations. NOT for recursive-trigger-prevention (use apex-recursive-trigger-prevention) — bypass disables a handler intentionally; recursion guards prevent the same handler from re-entering itself.