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.
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.
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.
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 |
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.
View as table
| Stage | Value |
|---|---|
| Process builders | 41 |
| Workflow rules | 118 |
| Record-triggered flows | 96 |
| 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()
)
- 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. 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. 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. 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. 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.
Keep reading
All guides →Object & data model
How the standard objects, record types, and custom fields fit together, plus the grain rules and relationship choices that decide whether reporting ever ties out.
ArchitectCPQ & quote-to-cash
How quotes, price books, approvals, and billing connect into one revenue chain, plus the config choices that decide whether a signed deal actually invoices correctly.
ArchitectIntegrations & data flow
How systems sync without corrupting each other, plus the ownership, idempotency, and error-handling rules that keep a two-way integration from overwriting the truth every fifteen minutes.