The Salesforce Apex Order of Execution
Every save in Salesforce travels a deterministic path — from JavaScript validation in the browser to post-commit asynchronous Apex. Here is the complete sequence, the traps that catch admins and developers, and the modern positions of flows after the Workflow Rules retirement.
Why the order of execution matters
When a record is saved with an insert, update, or upsert statement, Salesforce performs a fixed sequence of events. The sequence is the same whether the save originates from the UI, the REST API, an Apex DML statement, or a flow. That determinism is the contract: if you understand where each tool runs, you can predict — and debug — any save.
The order of execution sits underneath every customization decision. A validation rule that fires before a trigger has a chance to fix the data. A workflow field update that re-fires triggers but skips custom validation. A roll-up summary that updates a parent record, which then runs its own triggers and flows. None of this is documented inside the rule, trigger, or flow itself — the behavior is dictated by the position the tool occupies in the save sequence.
The order of execution is not a guideline. It is a deterministic contract. Every save runs the same steps, in the same order, every time — whether triggered by a click, an integration, a flow, or another trigger.
The browser phase: JavaScript validation
Before anything reaches Salesforce servers, the browser runs JavaScript validation on dependent picklist fields. This is the only client-side validation the platform performs, and it limits each dependent picklist to the values controlled by its parent. No other validation — no required-field check, no formula, no validation rule — runs in the browser.
This step never appears for API-initiated saves, Apex DML, or flow DML. It exists exclusively for users editing records in the standard UI.
The complete server-side sequence
Once the request reaches the server, Salesforce executes the following events in order. The list reflects the current Spring ’26 documented behavior.
- Load the original record from the database, or initialize a new record for an upsert/insert.
- Overwrite old field values with new values from the request.
- System validation — for UI saves: page-layout-specific rules, required fields at layout level, field formats, and maximum field length. For API, SOAP, and Apex saves: foreign keys, field formats, maximum field lengths, and restricted picklists; before executing a trigger, Salesforce verifies that any custom foreign keys do not refer to the object itself. Custom validation rules also run here for multiline items such as quote line items and opportunity line items, and for User object saves from a standard UI edit page.
- Execute before-save record-triggered flows.
- Execute all
beforetriggers. - Run most system validation again (required fields, formats) plus custom validation rules. Layout-specific rules from step 3 are not re-run.
- Execute duplicate rules. If a rule with the block action identifies the record as a duplicate, the save halts here — no after triggers, no workflow rules, nothing further runs.
- Save the record to the database — but do not commit yet.
- Execute all
aftertriggers. - Execute assignment rules.
- Execute auto-response rules.
- Execute workflow rules.
- Apply workflow field updates — if any exist, the record is updated again.
- Re-fire
before updateandafter updatetriggers exactly one more time, plus standard validations. Custom validation rules, duplicate rules, and escalation rules are not re-run. - Execute escalation rules.
- Execute Salesforce Flow automations — processes built with Process Builder and flows launched by workflow rules (flow trigger workflow actions pilot). These run in no guaranteed order. To control ordering, use record-triggered flows.
- Execute after-save record-triggered flows.
- Execute entitlement rules.
- Recalculate roll-up summaries on the parent if the record is part of a master-detail or cross-object workflow. The parent record then enters its own save procedure.
- Recalculate roll-up summaries on the grandparent if applicable. The grandparent then enters its own save procedure.
- Evaluate criteria-based sharing rules.
- Commit all DML operations to the database.
- Execute post-commit logic — outbound emails, asynchronous Apex (queueable jobs and future methods), asynchronous paths in record-triggered flows, and “publish after commit” platform events.
The sequence above can be grouped into four phases that map cleanly to how the platform actually behaves:
The distinction between save (step 8) and commit (step 22) is the single most tested concept in this topic. A record can have an Id, exist in the database, and still be rolled back if anything later in the sequence fails.
Before triggers vs. after triggers
The two trigger contexts are not interchangeable. Each one runs at a specific point in the save sequence, has access to different fields, and supports different operations.
Before triggers
- Run at step 5, before the record is saved to the database
- Modify field values directly on
Trigger.new— no DML required - Record does not yet have an Id on insert
- Cannot reference related records that depend on the new record’s Id
- Use for: data cleanup, defaulting fields, cross-field validation
After triggers
- Run at step 9, after the record is saved (but before commit)
- Cannot modify
Trigger.newdirectly — fields are read-only - Record has an Id on insert
- Can create, update, or delete related records via DML
- Use for: cross-object updates, roll-up logic, integrations
Use before triggers for changes to the same record. Use after triggers for changes to related records. Mixing the two contexts in a single trigger framework is fine — the framework dispatches to handler methods that match each context’s capabilities.
Multiple triggers on the same object
When two or more triggers exist for the same object and event, Salesforce does not guarantee the order in which they fire. This is a fundamental property of the platform and is unchanged across all releases. The accepted solution is the one-trigger-per-object pattern: a single trigger that delegates to a handler class, with the handler class controlling explicit sub-method ordering.
Where flows fit (before-save vs. after-save)
Record-triggered flows occupy two distinct positions in the save sequence depending on how they are configured.
| Flow type | Step | Best for |
|---|---|---|
| Before-save record-triggered flow | Step 4 — runs before all before triggers | Same-record field updates with no DML cost |
| After-save record-triggered flow | Step 17 — runs after escalation rules and Salesforce Flow automations | Cross-object updates, calling subflows, async paths |
| Schedule-triggered flow | Outside the save sequence — runs on a cron schedule | Batch operations against records meeting criteria |
| Platform-event-triggered flow | Runs in a separate transaction triggered by event delivery | Reacting to events from other transactions or external systems |
Before-save flows are the most efficient declarative option for same-record updates because they avoid the extra DML that an after-save flow or workflow field update would require. They run before before-triggers, which means a before-save flow can set a field that a before trigger then reads.
A before-save flow runs before any before trigger. If your trigger logic depends on a field set by a flow, this ordering works in your favor. If your flow logic depends on a value set by a before trigger, the flow will see the original value, not the trigger’s modification.
The workflow field-update re-fire trap
Workflow field updates introduce one of the most subtle behaviors in the entire save sequence. When a workflow field update modifies a record, before update and after update triggers fire one more time — and only one more time, regardless of how many field updates ran. Several things do not re-run during this second pass:
- Custom validation rules
- Flows (record-triggered flows of any kind)
- Duplicate rules
- Processes built with Process Builder
- Escalation rules
Standard system validations do re-run during this pass, but custom validation does not — so a custom validation rule cannot block a workflow-driven update.
This selective re-fire creates a category of bugs that look impossible. A field set by a workflow update will appear in the second trigger pass, but Trigger.old still reflects the value before the user’s edit, not the value before the workflow update. A trigger that compares Trigger.new to Trigger.old will see a “change” that happened during the workflow phase, not the user’s original change.
If you have an after update trigger that fires on a field, and a workflow field update changes that field, the trigger fires twice for a single user save. Idempotent trigger logic is not optional in this context — it is required.
Recursive saves and what gets skipped
A recursive save happens when a save triggers another save on the same record — most often through a trigger or flow that performs a DML update on the record being saved. To prevent runaway loops, Salesforce skips a large block of steps during a recursive save: assignment rules, auto-response rules, workflow rules, escalation rules, after-save flows, entitlement rules, and parent and grandparent roll-up summary recalculation.
What still runs in a recursive save: before-save flows, before triggers, validation, the save itself, and after triggers. The recursive save is a real save with full validation — it is the post-save automation block that gets bypassed.
| Step in recursive save | Runs? |
|---|---|
| Before-save flows, before triggers, validation, duplicate rules | Yes |
| Save to database | Yes |
| After triggers | Yes |
| Assignment, auto-response, workflow, escalation rules | Skipped |
| After-save flows, entitlement rules | Skipped |
| Roll-up summaries (parent and grandparent) | Skipped |
| Criteria-based sharing, commit, post-commit | Yes |
Post-commit: emails, async Apex, platform events
Step 23 is where work that does not affect the current transaction gets to run. By the time post-commit logic begins, the database is permanent and no further changes can roll back.
Outbound emails
Email alerts queued by workflow rules, processes, flows, approval processes, and the Messaging.sendEmail() method are sent here. If the transaction had failed before commit, none of these emails would have been sent.
Asynchronous Apex
Queueable jobs enqueued via System.enqueueJob(), future methods called via @future, and batch jobs started via Database.executeBatch() all begin in the post-commit phase. Each runs in its own transaction with its own governor limits — and its own pass through the order of execution for any DML it performs.
Asynchronous paths in record-triggered flows
After-save record-triggered flows can include asynchronous paths that run in a separate transaction after the originating save commits. These behave like queueable Apex from a transactional standpoint: the parent save is already permanent, and any DML in the async path triggers its own full pass through the order of execution.
Platform events
Platform events have two publish behaviors that determine where in the save sequence they fire. The Salesforce Architects guide describes them clearly: Publish Immediately publishes during the transaction before commit, with no order guarantee. Publish After Commit publishes only after a successful commit, with strict ordering preserved. Choose Publish After Commit when subscribers depend on data committed by the publisher; choose Publish Immediately for use cases like logging, where the event should fire even if the transaction rolls back.
If a piece of work cannot affect the current save and cannot block the user, move it to post-commit. Async Apex and platform events isolate slow or risky logic — callouts, complex calculations, third-party integrations — from the synchronous save. A faster, more reliable user experience is the result.
Workflow Rules and Process Builder after end of support
Salesforce ended support for Workflow Rules and Process Builder on December 31, 2025. New Workflow Rules and Process Builders cannot be created as of the Spring ’25 release. Existing automation continues to run — including the workflow field-update re-fire behavior at step 14 — but Salesforce no longer provides bug fixes or enhancements for these tools.
The order of execution diagram has not changed. Workflow rules still occupy step 12, workflow field updates still trigger the one-time re-fire at step 14, and processes still run alongside other after-save automation. The change is operational: legacy automation runs on borrowed time.
Workflow Rules & Process Builder
- End of support: December 31, 2025
- No new rules creatable since Spring ’25
- Existing rules continue to run
- No bug fixes or enhancements
- Migrate via the Migrate to Flow tool or rebuild in Flow
Flow Builder
- Active investment platform-wide
- Before-save flows execute earlier in the order of execution
- Supports loops, complex branching, subflows
- Async paths and scheduled paths available
- Single declarative tool covering all automation needs
Common pitfalls and best practices
Bulkify everything
The order of execution applies to whatever batch size invoked the save. Triggers fire once for the batch, not once per record. SOQL queries inside loops will exceed governor limits in production volumes regardless of how the save was initiated.
Validate after fixing, not before
Custom validation rules run at step 6 — after before triggers and after before-save flows. A trigger or flow that cleans up data does so before the validation runs against it. Validation rules should focus on business rules the user must satisfy, not on data hygiene that automation can handle.
Use one trigger per object
Multiple triggers on the same object have no guaranteed firing order. The one-trigger-per-object pattern, with a handler class that explicitly orders sub-methods, is the only way to control execution order across an organization.
Move slow work to async
Callouts, large data processing, and integrations belong in queueable, future, or batch Apex. They run after commit, do not block the save, and isolate failures from the user-facing transaction.
Track recursion explicitly
Static class variables are the standard pattern for preventing trigger recursion. The platform does not provide built-in recursion protection — every trigger framework needs its own guard.
If you are converting Workflow Rules to Flows, prefer before-save flows for same-record updates. They run earlier in the sequence than the original Workflow position and avoid the re-fire of triggers that workflow field updates cause. The converted automation will often perform better than the original.
High-yield exam tips
- Before-save flows run first. They execute at step 4, before any before trigger. This is the most efficient declarative path for same-record updates and a frequent multiple-choice distractor.
- System validation runs twice in UI saves. Once at step 3 (including layout-specific rules) and again at step 6 (most checks, but layout rules do not re-run).
- Custom validation runs after before-triggers. Triggers that clean up data succeed; triggers that try to bypass validation fail. Validation cannot be skipped from Apex except by re-writing the rule itself.
- Duplicate rules can halt the save. A duplicate rule with the block action at step 7 prevents all later steps — no after triggers, no workflow rules, no flows.
- Save is not commit. Step 8 saves; step 22 commits. Any failure between the two rolls back the entire transaction, including changes from after triggers and flows.
- Workflow field updates re-fire triggers exactly once. Before update and after update triggers fire one more time at step 14, plus standard validations. Custom validation, flows, duplicate rules, processes, and escalation rules do not re-run.
- Trigger.old does not see workflow updates. In the workflow re-fire pass,
Trigger.oldstill holds the value before the user’s edit, not the value before the workflow field update. - Multiple triggers, no guaranteed order. Two triggers on the same object and event fire in undefined order. The one-trigger-per-object pattern is the canonical fix.
- Recursive saves skip the post-save block. Assignment, auto-response, workflow, escalation, after-save flows, entitlements, and rollups are skipped. Validation and triggers still run.
- Post-commit fires async Apex. Queueable, future, batch, outbound emails, async paths in record-triggered flows, and platform events with Publish After Commit behavior all run at step 23 — after the database is permanent.
- Workflow Rules ended support December 31, 2025. Existing rules still run, but no new rules can be created and no fixes will be issued. Position in the save sequence is unchanged.
Frequently asked questions
Before-save record-triggered flows run before before-triggers, very early in the save process. After-save record-triggered flows run after workflow field updates and after escalation rules but before rollup summary recalculation. The split makes before-save flows the most efficient declarative option for same-record updates because they avoid an extra DML.
Yes. If a workflow field update modifies the record, before update and after update triggers fire one more time — and only one more time, regardless of how many field updates occurred. Custom validation rules, flows, duplicate rules, processes built with Process Builder, and escalation rules are not re-evaluated, but standard system validations are.
After the save step, the record is written to the database in a transient state. After all automation completes, DML operations are committed. Any failure between save and commit rolls back the entire transaction, including changes made by after triggers, workflows, and flows.
Outbound emails, asynchronous Apex jobs such as queueable, future, and batch, and platform events configured with Publish After Commit behavior. None of this logic can affect the data already committed in the current transaction.
No. When two or more triggers exist for the same object and event, Salesforce does not guarantee the firing order. Use a trigger framework or one-trigger-per-object pattern to make execution order explicit and controllable.
Existing Workflow Rules and Process Builders continue to run, but Salesforce ended support on December 31, 2025 and stopped allowing creation of new ones in Spring ’25. They still occupy their original positions in the save sequence, but new automation should be built in Flow Builder.
When a save triggers another save on the same record (a recursive save), Salesforce skips the steps from assignment rules through grandparent rollup summary recalculation. This prevents runaway loops while still honoring before-triggers, validation, after-triggers, and the eventual commit.
All information verified against the Salesforce Apex Developer Guide, Salesforce Architects decision guides, and Spring ’26 release documentation. Study smarter at CertifySF.com.
