SfSkills Domains Docs GitHub

Home / apex / trigger-and-flow-coexistence

trigger-and-flow-coexistence

Governance patterns for orgs where Apex triggers and record-triggered Flows both run on the same object: field-write conflict prevention, single-entry-point consolidation, recursion guards across automation types, and automation inventory. 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 save order of execution — use apex/order-of-execution-deep-dive. NOT for Flow-only patterns — use flow/record-triggered-flow-patterns.

Open the skill folder on GitHub


Trigger And Flow Coexistence

This skill activates when an org has both Apex triggers and record-triggered Flows on the same object and needs to prevent silent conflicts, field-overwrite bugs, or recursion loops. It provides governance patterns for inventorying mixed automation, detecting field-write collisions, and establishing guard clauses that work across automation types.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

Before-Save Timing Is Determinate

Before-save record-triggered Flows execute at step 3 of the order of execution. Apex before triggers execute at step 4. These are separate, consecutively numbered steps in the Apex Developer Guide, so the relative order is fixed and documented: the Flow runs first, the trigger runs second.

The practical consequence for coexistence: if a before-save Flow and a before trigger both write the same field, the before trigger's value is the one that persists — on every transaction, in every org, regardless of deployment order. The symptom "the Flow's value keeps disappearing" is fully explained by step order; it is not intermittent and not environment-dependent.

This still makes overlapping field writes the most common source of coexistence bugs — but the bug is a silent, predictable overwrite, not a race. Diagnose it by reading the step map, and fix it by giving the field one owner (or by making the later writer conditional). Do not add guards, retries, or ordering hacks intended to defeat nondeterminism that does not exist.

After-Save Flow Placement

After-save Flows run at step 14 in the order of execution, after workflow rules (step 11), escalation rules (step 12), and Process Builder / workflow-launched Flows (step 13) — including any re-invocation of before update and after update triggers caused by a workflow field update. This means an after-save Flow sees field values that may have been modified by a workflow field update that re-fired triggers. Conversely, DML performed inside an after-save Flow will re-enter the order of execution and fire triggers again, creating a recursion path that is invisible to trigger-only recursion guards.

Single Entry-Point Principle

The Salesforce Well-Architected framework recommends a single automation entry point per object per timing slot. In practice this means either triggers or Flows own a given timing slot on a given object, not both. Coexistence is a tolerated legacy state, not a recommended architecture. The goal of this skill is to make coexistence safe while planning consolidation.

Field Ownership Model

Each writeable field on an object should have at most one automation that sets its value during a given timing slot. When multiple automations write the same field at the same timing, you have a field-write collision. The field ownership model is a simple matrix: rows are fields, columns are automations, cells indicate read (R) or write (W). Any column with two or more W entries at the same timing is a conflict.


Common Patterns

Pattern 1: Field Ownership Registry

When to use: Any object with two or more automations that write fields during the same timing slot.

How it works:

  1. Create a spreadsheet or metadata document listing every field on the object that is written by any automation.
  2. For each field, record which automation writes it and at which timing (before-save, after-save).
  3. Flag any field with more than one writer at the same timing as a conflict.
  4. Resolve conflicts by moving one writer to a different timing, consolidating both writes into a single automation, or using a guard clause to ensure only one writer executes.

Why not the alternative: Without an explicit registry, developers add automations that silently overwrite each other. Nothing in the platform reports the collision — the later step simply wins and no error is raised. The registry is what makes an otherwise invisible conflict visible.

Pattern 2: Cross-Automation Recursion Guard

When to use: An after trigger performs DML that re-enters the save and fires Flows, or an after-save Flow performs DML that fires triggers, causing a recursion loop.

How it works:

public class AutomationControl {
    // Static flag visible to triggers; Flow can read via Invocable
    public static Boolean flowOriginatedSave = false;
    public static Boolean triggerOriginatedSave = false;

    @InvocableMethod(label='Check If Trigger Originated Save')
    public static List<Boolean> isTriggerOriginated(List<String> unused) {
        return new List<Boolean>{ triggerOriginatedSave };
    }
}

In the trigger handler, set AutomationControl.triggerOriginatedSave = true before performing DML. In the Flow, use a Decision element to call the Invocable method and skip logic when the flag is true. This prevents the Flow from re-processing records that the trigger already handled.

Why not the alternative: Static variables in Apex are invisible to Flows by default. Without an Invocable bridge, the Flow has no way to know it was triggered by an Apex DML and will always execute, causing infinite recursion.

Pattern 3: Consolidation Into Single Trigger Handler

When to use: Long-term refactoring of a mixed-automation object toward a single entry point.

How it works:

  1. Inventory all before-save Flows on the object and document their logic.
  2. Rewrite each Flow's logic as a method in the trigger handler class.
  3. Deactivate the before-save Flows one at a time, testing after each deactivation.
  4. Repeat for after-save Flows if the team prefers trigger-only automation.
  5. Document the migration in the automation inventory so future developers do not recreate Flows on the object.

Why not the alternative: Consolidation into Flows is equally valid. The key is choosing one entry point per timing slot, not necessarily choosing triggers over Flows. The decision depends on the team's skill set and change management process.


Decision Guidance

SituationRecommended ApproachReason
Before trigger and before-save Flow both write the same fieldGive the field a single owner, or make the trigger's write conditionalStep 4 (trigger) always runs after step 3 (Flow), so the trigger silently wins every time
After trigger DML re-fires Flows that re-fire triggersImplement cross-automation recursion guard with InvocableMethod bridgeStatic variables alone do not cross the Apex-to-Flow boundary
New Flow being added to an object with existing triggersBuild automation inventory first; confirm no field-write collisionsPrevents introducing a silent overwrite bug in production
Org has process builder, workflow rules, triggers, and Flows on the same objectPrioritize consolidation; use Flow Trigger Explorer to sequence FlowsFour automation types on one object is unmaintainable at scale
Team is split between Flow-first and code-first developersEstablish a per-object ownership agreement and document it in the automation inventoryMixed patterns are acceptable if governed; ungoverned mixing causes outages

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Build the automation inventory. For the target object, list every trigger (before/after, insert/update/delete), every record-triggered Flow (before-save, after-save), every workflow rule with field updates, and every active Process Builder. Record the fields each automation reads and writes.
  2. Identify field-write collisions. Using the inventory, flag any field that is written by more than one automation at the same timing slot. These are active or latent coexistence bugs.
  3. Check for cross-automation recursion paths. Trace DML operations in after triggers and after-save Flows. If a trigger performs DML that re-enters the save, check whether Flows on the target object will fire and vice versa.
  4. Implement guards or resolve conflicts. For field-write collisions, choose one owner per field per timing. For recursion paths, implement the cross-automation recursion guard pattern with an InvocableMethod bridge. For legacy automations (workflow rules, Process Builder), plan migration to either triggers or Flows.
  5. Document the ownership model. Update the automation inventory with the resolved ownership. Store this document in the repo or wiki so future changes go through the same review.
  6. Test with the Flow Trigger Explorer. In a sandbox, use Setup > Flow Trigger Explorer to visualize the execution sequence. Verify that field values match expectations after save.
  7. Validate in a full sandbox with data volume. Run bulk DML (200+ records) to confirm that recursion guards hold under bulk and that governor limits are not hit by the combined automation stack.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. The before trigger always overwrites the before-save Flow, silently -- The Apex Developer Guide lists before-save Flows at step 3 and before triggers at step 4. The order is documented, not indeterminate. If a Flow and a trigger both set Status__c, the trigger's value is what saves, every time. The gotcha is that nothing surfaces the conflict: no error, no warning, no debug-log flag. Beware older guidance (including AI-generated guidance) that describes this as an unpredictable race — acting on that belief leads people to add guards instead of fixing field ownership.
  2. Workflow field updates re-fire before and after triggers but not record-triggered Flows -- When a workflow rule performs a field update at step 11, the platform updates the record again, re-runs system validations, and executes before update and after update triggers "one more time (and only one more time)." Record-triggered Flows, custom validation rules, duplicate rules, and escalation rules are not re-run. This asymmetry means a trigger can see a value set by a workflow field update, but a before-save Flow cannot.
  3. After-save Flow DML is invisible to trigger recursion guards -- A typical trigger recursion guard uses a static Boolean like hasRun. This prevents the trigger from running twice in the same transaction. However, DML performed by an after-save Flow enters a fresh save cycle that resets the trigger context. The static variable is still true, so the trigger skips -- but the Flow may not skip, causing a one-sided infinite loop.

Output Artifacts

ArtifactDescription
Automation Inventory MatrixObject-level document listing every automation, its type, timing, fields read, fields written, and ownership
Conflict Analysis ReportIdentification of field-write collisions and recursion paths with severity ratings
Refactoring PlanStep-by-step plan to consolidate or guard coexisting automations

Package files