Salesforce

Salesforce Order of Execution: The Real Sequence

The Salesforce order of execution is the fixed sequence of events that runs every time a record gets saved, whether that save comes from a user clicking a button, an API call, or a batch job. It decides whether your validation rule fires before or after your flow, whether your trigger sees the old value or the new one, and why a single save can sometimes run your before-trigger logic twice. Miss this sequence and you will spend hours debugging a bug that was never in your code.

Most admins learn Salesforce backwards. They build a flow, then a validation rule, then a trigger, and assume everything runs in the order they built it. It doesn't. Salesforce runs a rigid, documented sequence regardless of build order, and that sequence is the same whether you're saving one record or fifty thousand through a data load.

The Full Sequence, Step by Step

Here's what actually happens between the moment a record is saved and the moment it lands in the database, for an update to an existing record.

StepWhat Runs
1Original record loaded from database, new values applied in memory
2System validation (required fields, field formats, field-level security)
3Before-save record-triggered flows
4Before triggers (old and new values available)
5Custom validation rules
6Duplicate rules
7Record saved to database, not yet committed
8After triggers
9After-save record-triggered flows
10Assignment rules, auto-response rules
11Workflow rules (if a field update fires, steps 2 through 10 run again)
12Processes and flows launched by that workflow field update
13Entitlement rules, roll-up summary recalculation on parent records
14Post-commit: outbound messages, email alerts, future methods, queueable jobs

Notice step 11. That line is the reason your trigger fires twice, and it's the single most misunderstood part of the whole sequence.

Why Your Trigger Fires Twice

A workflow rule with a field update does not just update a field quietly in the background. It forces the record through validation and before/after triggers a second time, in the same transaction. If your before-trigger logic isn't written to handle a second pass, you'll get duplicate task creation, doubled email sends, or a recursion error that crashes the save entirely.

This isn't a bug. It's documented behavior that's existed since workflow rules shipped. The fix is boring but effective: check whether the triggering field update actually happened before running expensive logic, and use a static boolean flag in a class to block a trigger from re-entering itself when it doesn't need to.

Record-triggered flows with field updates on the same object cause an identical problem, though with one safety net Salesforce added later: flow has a hard limit of recursion depth (typically capped before it throws a runtime exception), so infinite loops get stopped automatically. Workflow rules have no such ceiling. An old workflow rule updating its own triggering field can loop until it hits governor limits and fails loudly.

Before-Save Flows vs Before Triggers

Salesforce added before-save record-triggered flows specifically to compete with before triggers on performance, and the order matters. Before-save flows run first, then before triggers run second, both operating on the same in-memory record before it touches the database.

This creates a real design choice. If a flow sets a field value and a trigger also sets that same field, the trigger's value wins, because it runs later in the sequence and overwrites whatever the flow did. Teams that don't know this spend days debugging why a flow-calculated field keeps reverting to a trigger's hardcoded default.

The practical rule: pick one tool per field. If a trigger owns a field's logic, don't also let a flow touch it on the same object and same save event. Splitting ownership across two automation tools on the same field is how technical debt gets built one well-intentioned change at a time.

Where Validation Rules Actually Sit

Validation rules run after before triggers, not before them. That means a before trigger can modify a field value, and the validation rule will check the modified value, not the one the user originally entered. This is useful: a trigger can clean up bad data before the validation rule ever sees it.

It also means validation rules can't stop a before trigger from running its logic first. If your before trigger does something destructive before a validation rule blocks the save, that destructive action already happened in memory, even though the record never commits to the database. Most of the time this doesn't matter because nothing persisted. But if that trigger logic calls an external system via a synchronous callout, or queues a future job, that side effect already fired, independent of whether the save ultimately succeeds or fails.

After-Triggers and the Post-Commit Zone

After triggers run once the record is written to the database but before the transaction commits. They can query the record's own ID and related records that were just inserted, which before triggers cannot do reliably for new records. This is why anything involving parent-child relationship updates, roll-up logic the platform doesn't handle natively, or related record creation almost always belongs in an after trigger.

Future methods, queueable jobs, and outbound messages wait until after the entire transaction commits successfully. If the transaction rolls back for any reason, including a validation failure three steps earlier, none of that asynchronous work ever fires. This is actually a feature: it stops you from sending a confirmation email for a record that technically never got saved.

Building Automation That Respects the Sequence

Three habits prevent 90% of order-of-execution bugs. First, pick one automation tool per object per responsibility, and document which tool owns which field. Second, avoid workflow rules with field updates entirely on new builds; record-triggered flows with before-save updates do the same job without the double-fire risk on recursion. Third, when you inherit an org with workflow rules, triggers, and flows all touching the same object, map the actual execution order before changing anything, because the bug you're chasing is probably a sequencing collision, not broken logic.

Salesforce publishes this order of execution in its own documentation and it hasn't changed in any way that breaks existing automation in years. The platform is predictable. The chaos comes from builders who stack five automation tools on one object without ever checking which one runs first.

Frequently Asked Questions

Why does my Salesforce trigger run twice on one save?

This almost always happens because a workflow rule's field update forces the record back through validation rules and triggers a second time in the same transaction. Record-triggered flows with field updates can cause the same effect, though Salesforce caps flow recursion depth automatically. The fix is to check whether a relevant field actually changed before running expensive logic again, or to block re-entry with a static flag in Apex.

Do record-triggered flows run before or after Apex triggers?

Before-save record-triggered flows run before before-triggers on the same save event. After-save record-triggered flows run after after-triggers have finished. If both a flow and a trigger write to the same field during a before-save event, the trigger's value wins because it executes later in the sequence.

Where do validation rules fit in the order of execution?

Validation rules run after before-triggers execute but before the record is written to the database. That means a before-trigger can modify field values, and the validation rule evaluates those modified values rather than the original user input. Validation rules also run a second time if a workflow field update triggers a re-save cycle.

What is the difference between before triggers and after triggers?

Before triggers run while the record still exists only in memory, before it's written to the database, so they're used for setting or correcting field values on the record itself. After triggers run once the record has a database ID, which makes them the right place for updating related or parent records and for any logic that needs the record's own ID.

How do you stop infinite recursion in a Salesforce trigger?

Use a static Boolean variable in an Apex class to track whether the trigger has already run in the current transaction, and skip the recursive logic on the second pass. Also check whether the specific field your logic depends on actually changed, using Trigger.old versus Trigger.new, instead of re-running the full logic block on every invocation regardless of what changed.