← The GTM Operations Course

Module 5 · The Systems & Data Layer · Chapter 21 of 32

Automation Architecture and Order of Operations

Automation rarely breaks because one rule is wrong. It breaks because many well-meaning rules run in an order nobody designed, against a budget they all share. Here is how to build automation that stays debuggable and does not fight itself.

11 min read

The reassignment should have been a one-line update: change the owner on the Amazon.com account from the departing rep to the new one. It failed. Not with a warning, with a hard rollback and an error that named five automations throwing a limit exception on the same save: a flow cascading contact ownership, a flow updating new-business and expansion opportunities, an owner-change tracker, a confidentiality-clause flow, and a marketing-tier segmentation flow. Behind them, a managed-package trigger was firing once for every one of the account’s 8,364 contacts. The transaction ran past its CPU budget, and the platform did what it is supposed to do: it threw all of it away, atomically, so the database never held a half-finished reassignment.

The lesson was not “that account is too big.” It was that I had never designed the order those automations ran in, or budgeted the work they did together, so the first time a large enough account came through, they collided. The last chapter turned the Bowtie into real objects and fields with one data contract. This chapter is about the logic that fires when those records change, and the single idea that keeps it from becoming the failure above: automation runs in an order, whether or not you chose one.

282 → 82
Active Salesforce automations after a consolidation I ran: the count fell 71% while every real requirement stayed and 50 conflicts got fixed
14,149 → 7,730 ms
Account-save CPU before and after that consolidation. The old regime blew the 10,000 ms governor ceiling and failed; the new one clears it with headroom
~431 to 1,719
Contacts on an account where the owner change flips from saving to failing. Every automation on the save shares one CPU budget, so scale decides the outcome

Automation runs in an order, and the order is not optional

When a record saves, its automation does not fire all at once. It fires in a defined sequence, inside a single transaction, against a single resource budget, and it either commits as a whole or rolls back as a whole. Salesforce publishes this order of execution; every platform capable of automation has its own version of it. What an operator has to hold in their head is the shape, not the fine print. Cheap same-record work runs first. Then the record and its cross-object effects are written. Those cross-object writes can trigger more automation on the child records, which can trigger managed-package logic, and all of it draws down one CPU budget before the transaction commits.

Order of operations One save, one budget, a fixed order
Before-savesame-record defaults+ validationAfter-savecross-object writes(contacts, opps)Cascadechild saves re-firetheir own automationManaged triggersfire per child recordCommitall-or-nothingone transaction · one CPU budget (10,000 ms sync) · atomic rollback
A record save does not fire its automation all at once. It runs in a fixed sequence inside a single transaction that shares one CPU budget and commits or rolls back as a unit. Cheap same-record work runs first; the expensive cross-object cascades run later and can trigger yet more automation. Pile rules into those later lanes and a large enough parent record blows the shared budget, failing the entire save.

Two rules fall straight out of that shape, and most self-inflicted automation pain comes from ignoring one of them. The first: put every piece of logic in the cheapest phase that can do it. A field default, a formatted value, a validation on the record being saved belongs before-save, where it costs no extra database write. Cross-object writes and any logic that needs to compare a field to its prior value belong after-save, where the record and its history exist. During the consolidation I found before-save flows trying to read roll-up fields that are not populated yet at that point, and after-save logic doing same-record work that should never have caused a second write. Wrong phase is not a style error; it is wasted budget and, often, a bug.

The second rule: everything on the save shares one budget and commits atomically. There is no per-automation allowance. A cascade that runs comfortably on a 400-contact account fails on an 8,000-contact one because the budget is fixed while the work scales with the data. Nothing about the automations changed between the account that saved and the account that failed. The account got big enough to expose that they all draw on the same budget and run on the same transaction.

Consolidation over proliferation

The 282 automations I inherited were not one bad decision. They were 282 reasonable ones, each a new flow or rule bolted on to satisfy a new requirement, made by people who could not see each other’s work. The operating-model chapter framed that as a missing interface, which it was. One layer down, at build time, it is also an order-of-operations problem that compounds with every automation you add. Ten automations on one object are ten things whose relative order you now have to reason about, and most of that order is undefined, decided by internal tie-breakers you did not write and cannot see.

The design rule that fixes it is consolidation: one entry point per object per timing, with the individual requirements expressed as ordered steps inside it. Instead of eight after-save flows racing on the Account, you have one after-save flow whose steps run in the sequence you laid out. The requirements do not disappear; they stop competing. This is the same move as the classic one-trigger-per-object pattern, generalized to declarative automation, and it is why the 282 collapsed to 82 without dropping a single business rule.

Proliferation: one automation per requirement Consolidation: one entry point per object
How a new requirement lands A brand-new flow or rule bolts onto the object; the count climbs A new step inside the object's single automation; the count holds
Execution order Undefined across N automations racing on the same save Explicit: steps run in the sequence you laid out
Debugging a bad save Hunt across flows, rules, and triggers to find which one fired Open one automation and read its steps in order
CPU under load Each automation re-queries and re-writes; the budget burns fast Shared queries and one write pass; the same work costs less
Handing it to the next person Tribal knowledge of what fires when One place to look, named for the object it governs
The difference is not how many things your automation does. It is whether those things run in an order you chose and can find in one place. The right column is what makes the architecture debuggable, and it is the precondition for adding requirements without adding risk.

The CPU line in that table is not a side benefit. Eight flows that each query the account’s contacts run eight queries and eight write passes; one flow does it once. That is most of where the 14,149 milliseconds went and most of why the consolidated regime runs the same account save in roughly half the time. Consolidation buys you a debuggable architecture and a cheaper one at the same time, which is rare enough to take when it is offered.

Declarative or code: decide it on purpose

The instinct to reach for code first is usually wrong for GTM automation, and the instinct to reach for it never is wrong too. Declarative tools (Flows) should be the default, because the next admin can read and change them without a deployment, and because they live in the same place as every other rule on the object. Code earns its place when the constraint is something declarative genuinely cannot meet. Decide it with a reason, not a habit.

Reach for declarative (Flow) whenReach for code (Apex) when
The logic is field updates, record creation, or routing you can express as stepsYou need bulk-safe logic over thousands of related records in one pass
The next admin should be able to read and change it without a deployYou need a callout, precise recursion control, or explicit transaction control
You want it visible alongside every other rule on the objectYou are replacing a cascade a flow cannot make efficient at scale
Speed of change and debuggability matter more than raw throughputPerformance against the governor limit is the constraint, not a nicety

The trap is treating “declarative” as a synonym for “free.” A flow still consumes the same CPU budget as code, and a cascade of flows plus one managed-package Apex trigger can blow the governor limit exactly the way a badly written trigger would. The owner-change failure was almost entirely declarative. Declarative buys you readability and safe hand-off; it does not buy you a pass on the physics of the transaction.

Change the order, not always the metadata

Once you think in order of operations, the cheapest fix is often not “less logic” but “less work left for the logic to do by the time it runs.”

The general principle is worth keeping: when a transaction is too heavy to complete, you can shrink the logic, or you can change what state the record is already in before the heavy step runs. The second option is usually safer, because it does not touch shared metadata that a dozen other accounts depend on.

Make it debuggable, then govern it

Two disciplines keep a consolidated architecture from drifting back toward 282. The first is debuggability, and it starts with a naming convention boring enough that you never have to think about it: every automation named for the object it governs and the action it takes, so “Account | Cascade Contact Ownership” tells you what fires and where before you open it. Revenue Architecture borrows the safety researcher James Reason here, in its treatment of systems and human error: when something breaks, ask what caused it, not who did it. A single, named, ordered entry point per object is what lets you answer the “what” in one place instead of interrogating a dozen scattered rules and the invisible order between them.

The second discipline is governance. The Bowtie chapter established that a shared data model only survives if changes to it pass through review, because a silent redefinition rewrites every downstream metric. The same holds one layer down: a change to what fires on a save can change what the data means. Nothing I built reached production without being rebuilt and parity-tested in a sandbox that mirrored production first. For the consolidation, that meant standing up 142 record-triggered flows and 102 validation rules in a mirror org and diffing the new regime against the old on real records until it produced identical results, including at bulk, before a single change set moved. We formalize that review process in Data Governance and Quality; the deeper systems build, including trigger frameworks, orchestration order, and the admin patterns behind all of this, lives on the systems academy.

Handoff

You can now design automation that runs in an order you chose, consolidate it so a new requirement becomes a step rather than another racing rule, decide declarative versus code with a reason instead of a reflex, and respect the one budget every save shares. That is the logic layer sitting on top of the data model from the last chapter. What it quietly assumes, and does not address, is that the data model lives in more than one system. The owner on the account, the usage in the product, the invoice in the billing tool, and the health score in the success platform are the same customer scattered across a stack, and something has to keep them agreeing. Next, Integrations and the RevTech Stack picks a system of record and the sync patterns that keep the whole stack telling one story.