← All guides

Automation architecture

How flows, triggers, and validation rules coexist without fighting, plus the order-of-execution and consolidation rules that keep automation from eating itself.

GTM Systems guide

The org I spent the longest untangling had 282 active automations on the Opportunity object: process builders, a wall of workflow rules, record-triggered flows built by four different admins across five years, and an Apex trigger nobody would touch because the author had left. A single owner change on a large account would fire a cascade that hit the Apex CPU governor limit and failed the save, so reps could not reassign accounts, and the error message pointed at none of the real culprits. The automations were each individually reasonable. Together they were a machine that had started eating itself. Automation architecture is not about what one flow does; it is about what all of them do at once, in an order most teams never stop to map.

The reason automation sprawls is that every new requirement looks like a new automation. It rarely is. Most of what a fifth flow wants to do, an existing flow already touches, and adding a sixth trigger to the same object is how you get order-of-execution bugs that only appear under load. The discipline is consolidation: one record-triggered flow per object per timing, a clear rule for what goes in a flow versus a validation rule versus Apex, and a shared understanding of the order Salesforce runs everything in.

1 per timing
Record-triggered flow per object, before and after
282 to 82
Automations after one consolidation pass
1 order
Of execution, and it is not the one you guess

Know the order of execution before you build anything

Salesforce runs a save in a fixed order, and almost every automation bug is really a misunderstanding of that order. When a record saves: the system loads the old and new values, before-save flows run, before-triggers (Apex) run, system validation and validation rules run, the record saves to the database (but is not committed), after-save flows run, after-triggers run, assignment and auto-response rules run, roll-up summaries recalculate, and only then does it commit. If any step throws, the whole thing rolls back.

This order is the difference between a flow that works and one that mysteriously does not. A before-save flow that sets a field is free (no extra DML) and its value is available to validation rules. An after-save flow that sets a field on the same record costs an extra save and can re-trigger the whole chain. Put same-record field updates in before-save. Put anything that needs the record’s Id or touches related records in after-save. Get this wrong and you either burn governor limits or reference a value that does not exist yet.

Save order of execution Where each automation type runs in a single save
Before-saveset fields, freeValidationgate the saveSave (uncommitted)Id now existsAfter-saverelated recordsRollups, commitor roll back allafter-save can re-trigger
Before-save flow and before-triggers set fields cheaply, then validation gates the save, then the record writes, then after-save flow and after-triggers fire (these can re-trigger the chain), then rollups recalc, then commit. Every automation bug lives somewhere on this line.

The right tool for each job

The modern platform gives you three declarative-and-code layers, and each has a job it does best. Validation rules stop bad data at the gate and should hold anything a formula can express. Flows handle the bulk of business logic: field updates, record creation, routing, notifications. Apex is for what flows genuinely cannot do well: complex bulk logic, callouts with sophisticated error handling, recursion control, and operations that need to be unit-tested to a fine grain. Reaching for Apex when a flow would do is how you build automation only one person can maintain; reaching for a flow when the logic is genuinely complex is how you build something that fails silently at 200 records.

Flow Apex trigger
Who maintains it Admins, declaratively Developers, with tests
Bulk behavior Handles collections, but easy to write non-bulk-safe Bulk-safe when written correctly, testable
Complex branching Gets unreadable past a few decisions Expresses cleanly in code
Callouts and error handling Limited, awkward for retries Full control, retry and logging
Best when Field updates, routing, notifications, most logic Heavy bulk, callouts, recursion, fine-grained tests
Default to flow for maintainability; escalate to Apex only when the logic exceeds what a flow expresses cleanly or bulk-safely.

The consolidation rule that matters most: one record-triggered flow per object per timing. All your before-save Opportunity logic lives in one before-save flow with clear decision branches, and all after-save logic in one after-save flow. This gives you a single, ordered place to reason about what happens on save, instead of a lottery among six flows whose relative order Salesforce does not guarantee.

Opportunity automations before and after consolidation
Illustrative from one real cleanup. Collapsing process builders and duplicate workflow rules into per-object per-timing flows cut 282 automations to 82 and eliminated the CPU-limit save failures on large-account owner changes.
View as table
StageValue
Process builders41
Workflow rules118
Record-triggered flows96
After consolidation (all)82

Recursion and bulkification are not optional

Two failure modes account for most automation that works in a demo and breaks in production. Recursion: an after-save flow updates a field, that update re-fires the flow, which updates the field again. Without a guard, this loops until it hits the governor limit and rolls back the save. Bulkification: a flow or trigger that queries or updates inside a loop works fine on one record and blows the query limit when a data load touches 200. Every automation you build has to survive a 200-record batch, because sooner or later an integration or a mass update will send one.

Here is the recursion guard I put on any before-save flow that could re-fire, expressed as the entry condition that stops the loop:

// Only run the update logic when the driving field actually changed
// AND the record was not already stamped by this same automation.
AND(
  ISCHANGED( StageName ),
  NOT( ISPICKVAL( PRIORVALUE( StageName ), "Closed Won" ) ),
  Automation_Last_Run__c <> TODAY()
)
Automation build order: five checks before you add anything
  1. 1

    1. Does an existing flow already touch this?

    Most new requirements belong in a branch of an existing per-object flow, not a new automation. Check before you build.

  2. 2

    2. Pick the layer honestly

    Validation rule if a formula can express it. Flow for most logic. Apex only when bulk, callouts, or recursion control exceed a flow.

  3. 3

    3. Choose before-save or after-save

    Same-record field updates go before-save (free). Anything needing the Id or related records goes after-save.

  4. 4

    4. Guard against recursion

    Entry conditions keyed to what actually changed, so an after-save update does not re-fire itself into the governor limit.

  5. 5

    5. Test at 200 records

    No queries or DML inside loops. If it cannot survive a bulk data load, it is not done, it just has not failed yet.

Automation architecture is the discipline of making many automations behave like one coherent system. Learn the order of execution, because every bug lives on that line. Consolidate to one record-triggered flow per object per timing so there is one ordered place to reason about a save. Pick the right layer instead of defaulting to whatever you last built. Guard recursion and test at bulk, because the record load that breaks you is a matter of when, not if. Do that and automation stays a machine you can reason about, instead of the 282-piece contraption that quietly starts eating itself the day the person who built it leaves.