GTM Engineering
We Deactivated 123 Flows and Nothing Broke
An org running 282 automations dropped to 82, and record saves got faster instead of breaking. Here is the five-rung build order that made a two-thirds cut safe: inventory, dedupe, merge, retire, govern.
· 14 min read
282 automations. We turned off 123 flows and 89 validation rules and landed at 82. Record saves got faster. No support tickets, no silent data corruption, no angry Slack from a rep whose deal would not advance. The org had been carrying two thirds of its automation as pure weight.
For a decade the reflex has been the same: a field is wrong, a handoff is missed, a stage skips a step, so you ship a flow. Then someone ships a flow that does almost the same thing a year later because they could not find the first one, and a validation rule goes on top to catch the case where the two disagree. Nobody deletes. Everybody adds. That reflex is the debt, and it accrues interest as save latency, order-of-execution bugs, and the slow death of anyone who tries to trace why a field changed. The way out is not another flow. It is a build order you run in reverse: you climb down the pile in a fixed sequence, and the sequence is what keeps a two-thirds cut from becoming an incident.
- R5Govern so the pile cannot re-accrue82 kept
One flow per object per trigger phase, a naming convention, and a rule that new automation must show what it replaces. Without this rung the 82 grows back to 282 in three years, one convenient flow at a time.
- R4Retire dormant and redundant in dev, then promote123 off
Deactivate whole clusters in a sandbox, re-run the full save path, diff against a control record, and promote the exact set you tested. Deactivate first because it is a one-click undo; delete only what stayed dark a full quarter.
- R3Merge fighting clusters into one ordered flow33 flows
Where two correct automations wrote the same field in an unchosen order, fold the logic into a single flow with the order chosen on purpose. The "random ownership flip" bug was two flows in a race; one ordered flow ends the race.
- R2Dedupe by what each one writes6 → 1
Group every automation by the object and field it mutates. Six flows writing Owner on Account is a fighting cluster; four writing the same date field is redundancy. The map is ugly and the map is the deliverable.
- R1Inventory every automation on the object282
Pull the full active list from the Tooling API. One row per flow, process builder, workflow rule, and validation rule. You cannot cut a pile you have never counted, and on this org the count was 282 where leadership assumed maybe 40.
That ladder is the whole method, bottom to top. Skip a rung and the cut gets dangerous: retire before you dedupe and you turn off a flow whose job a survivor silently depended on; merge before you inventory and you consolidate two flows while a third you never found keeps writing the same field. Read the rest of this piece as the detail behind each rung, and the numbers behind why the org got faster instead of breaking.
R2: most of it is redundant, dormant, or fighting itself
When I read the 282, they sorted into three piles, and that sort is the entire diagnosis because each pile maps to a different rung of the fix. Name the pile before you touch anything.
Redundant automations do the same job in triplicate. Three separate flows all stamping a “last activity” style date, each triggered on a slightly different condition, each convinced it was the source of truth. Two of them were wrong most of the time and you could not tell which.
Dormant automations have not fired in a year because the entry condition references a stage or record type that no longer exists. Dead code that still costs you, because every save evaluates the entry criteria before deciding not to run.
Fighting automations are the expensive pile. Flow A sets Owner based on territory. Flow B sets Owner based on account segment. They run in the same save, in an order nobody chose on purpose, and the last write wins. The bug reps reported was that ownership flipped at random. It was not random. It was two correct automations both doing their job in a race.
| Pile | What it looks like | What it costs | Which rung fixes it |
|---|---|---|---|
| Redundant | 3-6 flows writing the same field | Save latency, no clear source of truth | R4: keep one, retire the rest |
| Dormant | Entry criteria references a dead stage or record type | Wasted criteria evaluation on every save | R4: deactivate outright |
| Fighting | Two flows writing the same field in an unchosen order | Intermittent “random” data bugs | R3: consolidate into one ordered flow |
Here is roughly how the 282 split. The fighting pile was the smallest by count and the largest by bug volume, which is why R3 sits below R4 in the ladder: you resolve the races before you retire the dead weight, because a race you retire wrong becomes a new race.
View as table
| Item | Value |
|---|---|
| Redundant | 96 |
| Dormant | 71 |
| Fighting | 33 |
| Kept | 82 |
The order-of-execution trap that makes R4 scary
Before the retire rung, the thing you have to hold in your head. Salesforce runs the automations that survive a save in an order you do not fully control, and when you remove one, it reorders what is left. A flow that used to run third now runs second, and it may read a field before another flow has written it. The bug you introduce by deleting a flow is rarely in the deleted flow. It is in the survivor whose timing you just changed.
This is the reason the ladder puts dedupe (R2) and merge (R3) below retire (R4). If you have already collapsed a fighting cluster into one ordered flow, there is no race left to disturb when you retire the dormant rows around it. Cut in the wrong order and every deactivation is a coin flip on the survivors’ timing.
R1 and R2: build the map before you guess
You need three columns before you touch anything. Build them in a spreadsheet, one row per automation, and the retire list writes itself.
Last-modified date tells you who is even maintaining a thing. Anything untouched for two-plus years is a candidate for the “why does this exist” list, not automatic deletion, but a candidate.
Run frequency is the column people skip because Salesforce does not hand it to you cleanly. Pull it from the automation’s own logging if you built any, or infer it: for a flow gated on StageName = 'Closed Won', count how many opps hit that stage last quarter. Zero means dormant. You rank by “does this run at all,” not by “does this look important.”
-- Infer run frequency: how many records could a Closed Won flow have fired on?
SELECT COUNT(Id) potential_fires
FROM Opportunity
WHERE StageName = 'Closed Won'
AND LastModifiedDate = LAST_N_DAYS:90
Overlap is manual and it is where the real savings hide. Group every automation by the object and the fields it writes. Six flows all writing Owner on Account is your fighting cluster. Four writing the same date field is redundancy. Pull the raw list of active flows from the Tooling API to seed the map:
sf data query --use-tooling-api -o vinaydev \
--query "SELECT DeveloperName, TriggerType, Status, LastModifiedDate FROM FlowDefinitionView WHERE Status = 'Active' ORDER BY LastModifiedDate"
Draw the field-write overlap on one page. The map is ugly and the map is the whole point.
R4: deactivate in dev first, watch, then promote
Here is the method that let me turn off 123 things and sleep. Follow it in order. The order is what makes it safe.
- 1
Build the three-column map
Last-modified, inferred run frequency, and field-write overlap for every automation on the target object. Rank by "does this run" and group by "what does it write." The fighting clusters and dormant rows fall out on their own.
- 2
Snapshot a control record in an untouched org
Before you change anything, capture the exact field values a clean save produces in a sandbox you are not touching. This is your diff target. Without it you are testing against your memory of what "correct" looks like.
- 3
Deactivate a whole cluster in dev, never one flow at a time
The fighting automations only reveal themselves as a group. Turn off the six Owner-writers together, not one per day, or you will chase timing ghosts for a week.
- 4
Re-run the full save path, not the single record
Create an opp, advance it through every stage, change owner, close won, close lost. Diff the resulting record against the control. You are testing whether the survivors still land correct values after the reorder, not whether the deleted flow still does its job.
- 5
Promote the exact cluster you tested
If you tested six flows off together, promote six, not four. Promoting a subset ships a configuration you never verified, and the reorder math changes again.
The danger is entirely order of execution. When you remove a flow from a save, you are not just removing its effect; you are changing the order the survivors run in. So the thing you must test is whether turning it off changed the timing for everything downstream, not whether the deactivated flow’s own job still happens. That is why you deactivate in clusters and re-run the full path, not the single record the flow used to touch.
This is also why deactivate beats delete as your first move. A deactivated flow is a reversible change: if the full-path diff shows a survivor now reading a field early, you flip it back on in seconds and the timing is restored while you rethink. A deleted flow is gone, and rebuilding it from a change set or from memory under pressure is exactly how a “cleanup” turns into an incident. The org that went 282 to 82 deactivated everything and deleted almost nothing for the first month, so that any surprise was a one-click undo rather than a rebuild.
| The naive approach | The method that worked | |
|---|---|---|
| Unit of change | One flow at a time | A whole field-write cluster at once |
| What you test | Does the deleted flow still work | Did the survivors change timing |
| Test scope | The single record it touched | The full create-advance-close path |
| Environment | Production, "it is just a deactivation" | Dev sandbox, diffed against a control |
| Promotion | Hand-picked subset that "looks safe" | The exact cluster you verified |
A worked example that reconciles to the count
Take the Account object, the worst offender on this org, and walk it down the ladder. The map showed 41 active automations touching Account, and the field-write grouping put them in four buckets. Watch the count fall rung by rung and land on the number in the stat tiles.
| Rung | Action on Account | Automations before | Automations after |
|---|---|---|---|
| R1 Inventory | Count everything active on Account | 41 | 41 |
| R2 Dedupe | Group by field written, flag overlaps | 41 | 41 (mapped) |
| R3 Merge | Fold 6 Owner-writers + 4 date-stampers into 2 ordered flows | 41 | 31 |
| R4 Retire | Deactivate 19 dormant, delete none yet | 31 | 12 |
| R5 Govern | One-per-trigger-phase rule holds the 12 | 12 | 12 |
Account went from 41 to 12, a 71 percent cut on the object, matching the org-wide 71 percent that was redundant, dead, or self-canceling. Run the same pass across every heavy object and the org total lands at 282 to 82. The two thirds you remove is not a guess or a target you reverse-engineered. It is what the overlap map exposes once you group by the field each automation writes, and the fighting clusters that caused the “random” bugs collapse into single ordered flows on R3 before a single dormant row is retired on R4.
The org got faster for a mechanical reason: save time scales with how much work every save does, and two thirds of that work was redundant, dead, or self-canceling. The 82 that remained were legible. A new admin could open the ownership logic and find one flow, not six in a knife fight.
R5: govern so it cannot re-accrue
The last rung is the one everybody skips, and it is why most cleanups are temporary. Cut to 82, ship no governance, and you are back to 282 in three years because the reflex that built the pile is still the default. Governance is three cheap rules: one flow per object per trigger phase (before-save, after-save), a naming convention so the next person finds the existing flow instead of building a twin, and a standing requirement that any new automation names what it replaces. None of the three is a tool purchase. They are the difference between a one-time heroics project and an org that stays legible.
The number that matters
We went from 282 to 82 and the org got faster, and the reason it was safe is that we climbed the ladder in order rather than reaching into the pile and pulling. Inventory before dedupe, dedupe before merge, merge before retire, retire before govern. Each rung makes the next one safe: you cannot dedupe what you never counted, and you cannot retire safely until the fighting clusters are already one ordered flow instead of a race.
If you want the survivors to stay legible, the same discipline that made this safe to cut makes it safe to keep. Automations should be idempotent so a re-run cannot corrupt a record, and a governed org keeps tool sprawl from re-accumulating the moment you look away.
The loan was taken out one convenient flow at a time. You pay it down the same way, one rung at a time, with a control record and a full-path test between each. The interest was save latency and a class of “random” bugs nobody could reproduce. Both go away when the map is legible, and the map is an afternoon of work you can start today.
Keep reading
One email. Every week.
One email a week: a system I built or broke, with the config, the numbers, and what I would change. No roundups, no theory, unsubscribe whenever it stops being useful.
The newsletter opens soon.
Connect a provider in src/config.ts