ESC

AI-powered search across all blog posts and tools

Architecture Β· November 11, 2025

Salesforce Order of Execution - The Complete Visual Guide

A deep dive into all 17 steps Salesforce executes when you save a record, with visual diagrams and real-world scenarios.

☕ 10 min read 📅 November 11, 2025
  • System validations and before-save flows fire before your Apex triggers
  • Validation rules run AFTER before triggers, meaning triggers can manipulate data before validation
  • After triggers fire after the record is saved to the database but before the transaction commits

When I first started building on Salesforce, I had a nasty bug that I could not explain. A before trigger was updating a field, but a validation rule kept rejecting the record. I spent three hours debugging before a senior architect pulled me aside and said: β€œDo you know the order of execution?” I did not. That conversation changed how I design everything on the platform.

Understanding the order of execution is not just academic knowledge β€” it is the difference between a system that works predictably and one that produces mysterious failures. Let me walk you through all 17 steps Salesforce executes every time a record is saved, with real examples of where things go wrong.

The Problem

A before trigger sets a field value to normalize data. A validation rule that checks that same field keeps rejecting the record. Both the trigger developer and the validation rule developer are confident their logic is correct. Yet every save fails β€” and nobody can explain why.

The Solution

Knowing the order of execution immediately reveals the answer: before triggers run at step 5, validation rules run at step 6. The trigger’s output is what validation sees. If the trigger sets the field to a value the validation rule rejects, the two automations are in conflict β€” and the fix requires coordinating them intentionally rather than treating them as independent components.

The Animated Flowchart

Salesforce Order of Execution (Record Save)
Salesforce Order of Execution (Record Save)1. Load old record from database (or init if new)2. Overwrite old record with new field values3. System validations (required fields, field formats, field-level security)4. Before-save Flow (Record-Triggered, Before Save)5. Before Apex Triggers6. Custom Validation Rules7. Duplicate rules8. RECORD SAVED TO DATABASE (not committed)9. After Apex Triggers10. Assignment Rules11. Auto-response Rules12. Workflow Rules (legacy)13. Escalation Rules14. After-save Flows (Record-Triggered, After Save)15. Criteria-based sharing rules evaluation16. Roll-up summary recalculations; grand-parent recalculations17. COMMIT to database β€” post-commit logic (emails, async jobs)Apex / FlowValidation / RulesDatabase operationsPost-save

Why the Order Matters More Than You Think

Before Triggers Run Before Validation Rules

This is the fact that trips up almost every new Salesforce developer. Step 5 (before triggers) runs before step 6 (validation rules). This means you can use a before trigger to normalize data β€” and validation rules will see the normalized value.

πŸ’‘ Data Normalization Pattern

Use a before trigger to clean/format data, then let validation rules check the cleaned value. Example: a before trigger reformats phone numbers, and the validation rule ensures the formatted result matches the expected pattern.

trigger ContactBeforeTrigger on Contact (before insert, before update) {
    for (Contact c : Trigger.new) {
        if (c.Phone != null) {
            // Strip everything except digits
            String digits = c.Phone.replaceAll('[^0-9]', '');
            if (digits.length() == 10) {
                c.Phone = '(' + digits.substring(0,3) + ') '
                        + digits.substring(3,6) + '-'
                        + digits.substring(6,10);
            }
        }
    }
}

Now your validation rule NOT REGEX(Phone, "\\(\\d{3}\\) \\d{3}-\\d{4}") will pass because the trigger already cleaned the data.

The Database Save Happens at Step 8 β€” Not Step 17

⚠️ Common Misconception

Many developers assume that β€œsaving” means the transaction commits at step 8. It does not. The record is written to the database at step 8, but the transaction is not committed until step 17. Everything between steps 8 and 17 still has the ability to throw an exception and roll back the entire operation.

This is why you can safely query the record inside an after trigger (step 9) β€” it is already in the database β€” but if your after trigger throws an unhandled exception, the entire record save is rolled back.

After Triggers See the Committed ID

One practical consequence of step 8 happening before step 9: your after trigger can read Trigger.new[0].Id and it will be populated, even for insert operations. Before triggers do not have this luxury for new records.

trigger OpportunityAfterTrigger on Opportunity (after insert) {
    for (Opportunity opp : Trigger.new) {
        // opp.Id IS populated here β€” record exists in DB at this point
        System.debug('New Opportunity ID: ' + opp.Id);
    }
}

The Before-Save Flow vs. Before Trigger Question

Since Winter β€˜20, Salesforce Record-Triggered Flows can run before a record is saved. Where do they fall in the order? Step 4 β€” before your Apex triggers at step 5. This has a critical implication:

ℹ️ Info

If your before-save flow sets a field value, your before trigger will see that updated value in Trigger.new. This is a clean pattern β€” flows handle simple field assignments, triggers handle complex logic β€” as long as you document it.

Workflow Rules and the Second Pass

🚨 The Double-Fire Trap

When a workflow rule fires a field update (step 12), Salesforce re-runs the trigger and most of the order of execution for that field update. This means your before and after triggers can fire twice on a single user action.

The classic sign that you have hit this problem: a counter field increments by 2 instead of 1, or an email is sent twice. The fix is to track whether the trigger has already processed the record using a static variable:

public class TriggerState {
    public static Set<Id> processedIds = new Set<Id>();
}

trigger AccountTrigger on Account (after update) {
    List<Account> toProcess = new List<Account>();
    for (Account acc : Trigger.new) {
        if (!TriggerState.processedIds.contains(acc.Id)) {
            TriggerState.processedIds.add(acc.Id);
            toProcess.add(acc);
        }
    }
    if (!toProcess.isEmpty()) {
        AccountService.handleUpdate(toProcess, Trigger.oldMap);
    }
}
πŸ’‘ Pro Tip

After-save Flows (step 14) do NOT cause a second trigger pass for field updates the way workflow rules do. This is one reason Salesforce recommends migrating away from workflow rules to record-triggered flows. Use a static Set<Id> variable in a separate class (not the trigger itself) to track processed record IDs across re-entrant trigger executions.

Rollup Summary Fields and Step 16

Roll-up summary fields on parent records are recalculated at step 16. This means that inside an after trigger (step 9) on a child record, the parent’s roll-up field has not yet been updated. If your trigger queries the parent’s roll-up value expecting to see the new total, you will get the old value.

trigger OrderItemAfterTrigger on OrderItem (after insert) {
    // BAD: querying the Order's TotalAmount here (step 9) will return
    // the PRE-update value because roll-ups recalculate at step 16
    Order parentOrder = [SELECT TotalAmount FROM Order WHERE Id = :Trigger.new[0].OrderId];
    // parentOrder.TotalAmount is STALE at this point
}

The workaround is to use a platform event or a queueable job to defer the query until after the transaction fully commits.

Validation Rules Can Reject Work Done by a Before Trigger

Here is the corollary to the β€œbefore trigger runs before validation” point: if your before trigger sets a field to a value that violates a validation rule, the save will fail. The trigger runs, modifies the record in memory, then validation checks the modified record and rejects it.

Real-World Example: Trigger vs Validation Conflict

I once saw a trigger that set Opportunity.CloseDate to 90 days in the future as a default. The validation rule said close date must not be more than 60 days out. The trigger and validation rule were written by two different developers on two different days. Every new Opportunity save failed. Nobody could figure out why until we traced the order of execution.

The Complete 17 Steps (Reference Table)

Full 17-Step Reference Table
StepOperationCan Throw Exception?
1Load original record from DBNo
2Overwrite with new valuesNo
3System validationsYes β€” rolls back
4Before-save FlowsYes β€” rolls back
5Before TriggersYes β€” rolls back
6Custom Validation RulesYes β€” rolls back
7Duplicate RulesYes β€” rolls back
8Save to DB (not committed)No
9After TriggersYes β€” rolls back
10Assignment RulesNo
11Auto-response RulesNo
12Workflow Rules (legacy)No
13Escalation RulesNo
14After-save FlowsYes β€” rolls back
15Sharing rule evaluationNo
16Roll-up summary recalculationNo
17Commit; post-commit (emails, async)N/A (committed)

Practical Debugging Checklist

When you have a record-save issue you cannot explain, I go through this checklist in order:

  1. Is there a before-save Flow that modifies the same fields as my trigger? (Step 4 vs Step 5)
  2. Does my validation rule depend on a field that a before trigger sets? (Intentional or conflict?)
  3. Is a workflow field update causing my trigger to fire twice? (Check the static variable pattern)
  4. Does my after trigger depend on a roll-up summary value that has not recalculated yet? (Step 9 vs Step 16)
  5. Is there a second-level trigger caused by a record update inside an after trigger?
ℹ️ Info

Nested triggers β€” triggers that fire because your trigger updates another record β€” follow the same 17-step process recursively. Salesforce allows up to 16 trigger depth levels before throwing System.LimitException.

Wrapping Up

The order of execution is one of those foundational topics that separates architects who guess from architects who know. Once you internalize these 17 steps, mysterious save failures become logical outcomes of a predictable sequence.

The most common real-world scenarios to watch are: before triggers + validation rules interacting (steps 5 and 6), workflow field updates causing a second trigger pass (steps 5, 6, and 12), and roll-up summary stale reads in after triggers (steps 9 and 16).

Have you ever encountered an unexpected order-of-execution issue in a production org? What was the scenario, and how did you diagnose it? I’d love to hear your war stories in the comments.


Knowledge Check

A before trigger sets a field to a value that a validation rule rejects. What happens?
In an after insert trigger on OrderItem, you query the parent Order's roll-up summary field TotalAmount. What value do you get?

How did this article make you feel?

Comments

Salesforce Tip

🎉

You finished this article!

What to read next

Contents