Updated October 7, 2026
Most Flow problems are not exotic. A flow runs when it doesn’t need to, a Get Records element sits inside a loop, or a failure lands in someone’s inbox as a cryptic email with no context. Each one is avoidable, and the fixes are documented by Salesforce. This post collects the practices that matter most for performance and for keeping flows maintainable, with a link to the official source for each.
Know the Limits You Are Working Against
Flows run under the same per-transaction governor limits as Apex. Salesforce’s Per-Transaction Flow Limits page lists them: 100 SOQL queries, 150 DML statements, 50,000 records retrieved, 10,000 records processed by DML, and 10,000 milliseconds of CPU time. Every Get Records execution counts as a query, and so does an Update Records or Delete Records element that uses filter conditions.
Two details on that page change how you should design. First, if an element pushes the transaction over a limit, the entire transaction rolls back, even if the element has a fault path. Error handling cannot save you from a limit breach, so the only fix is not hitting it. Second, an autolaunched flow shares the limits of the transaction that launched it. Your flow is spending from the same budget as every trigger, flow, and Apex class that runs alongside it.
Use Before-Save Flows for Same-Record Updates
If a record-triggered flow only needs to change fields on the record that triggered it, build it as a before-save flow (the “Fast Field Updates” option). The Salesforce Architects Record-Triggered Automation decision guide calls a before-save record-triggered flow “the most performant declarative option” for updating the triggering record.
Save after-save flows (the “Actions and Related Records” option) for work that needs them: creating or updating other records, sending emails, calling invocable actions. The same guide notes that invocable actions are only available in the after-save context, so the choice of context is partly made for you by what the flow does.
Write Precise Entry Conditions
A flow that doesn’t run costs nothing. The decision guide lists precise entry conditions alongside before-save updates as the biggest performance gains available, because they keep a flow from running on changes that don’t affect it.
For update triggers, combine your conditions with the setting that runs the flow only when a record is updated to meet the condition requirements. When you need a formula, the guide recommends comparing $Record with $RecordPrior so the flow fires only when the field you care about actually changed. That same pattern prevents recursion, where a flow’s own update triggers it again.
Keep Queries and DML Out of Loops
This is where most of the old advice about “bulk processing” gets muddled, so it is worth being precise.
Record-triggered flows are bulkified by the platform. When a flow runs for many records at once, Salesforce creates one flow interview per record and groups similar operations when those interviews reach the same element. If a data load updates a batch of records and each interview reaches the same Update Records element, the platform issues one DML statement for the group instead of one per record. You don’t need to design the flow to process a collection of triggering records yourself.
What the platform does not do is fix your flow’s internal logic. The same page states that Salesforce doesn’t automatically consolidate operations within a single flow’s logic, and that a Create Records element inside a loop performs one DML operation per iteration.
Say an Opportunity flow loops over every related Contact and runs an Update Records element on each one inside the loop. Every iteration is a separate DML statement, and every interview in the batch repeats the pattern. Against a limit of 150 DML statements per transaction, that design has very little headroom. The fix is the one Salesforce gives in its Flow Builder best practices: avoid making edits in a loop path, use Assignment elements to build a collection inside the loop, and update everything at once after the loop. The same page recommends grouping all database changes together at the end of the flow.
The same logic applies to queries. If you need related data, get it once with a single Get Records element before the loop and work from the collection, rather than querying inside each iteration.
Split Flows by Purpose, and Set Their Order
It is tempting to put every automation for an object into one large flow so it’s “all in one place.” The decision guide calls this an anti-pattern and says consolidating into one flow versus splitting into multiple well-conditioned flows does not have a major impact on performance. Smaller flows with tight entry conditions are easier to read, test, and change.
If several flows on the same object need to run in a specific sequence, use Flow Trigger Explorer, which lets you assign an order value to each flow on an object.
Move Slow or Time-Based Work Off the Main Transaction
Not everything has to happen while the user waits. Record-triggered flows offer an asynchronous path for automations that require a separate transaction, and scheduled paths for time-based work like “3 days before close date.” Per the same guide, scheduled paths include automatic cancellation and rescheduling if the record’s data changes. Both are subject to daily limits, so they’re a tool for the right job, not a place to dump everything.
Reuse Logic with Subflows
When the same steps show up in several flows, build them once as their own flow and call it with the Subflow element. Salesforce’s Subflow element documentation recommends exactly this: create smaller flows that perform common tasks, then call them as needed. One caveat from that page: you can’t call flows that contain Wait elements.
Subflows also help with security. The best practices page describes using a subflow to mix user context and system context, so a flow can respect user permissions for some actions and run in system context for others.
Name and Describe Everything
Salesforce is blunt about this: labels, API names, and descriptions are the only way to document what your flow is doing. “Get Records 3” tells the next admin nothing. “Get Open Cases for Account” tells them what the element holds without opening it. Use the Description field on elements, resources, and flow versions to explain the business rule behind the logic, and where the rule came from if that matters.
The same page notes that the Toolbox panel can filter for resources missing descriptions, which makes cleaning up an older flow a lot faster. And while you’re in there, check for hardcoded record IDs. Salesforce advises never hard coding Salesforce IDs because they’re org-specific. Look them up with Get Records or pass them in as variables instead.
Add Fault Paths to Every Element That Can Fail
Any element that touches the database can fail: a validation rule blocks the update, a required field is missing, the running user lacks access. Salesforce’s guidance is to add fault paths to all elements that can fail. By default, a failed flow shows the user an error and emails the admin who last modified the flow, which is often not the person who can act on it.
A fault path lets you decide what happens instead: show the user a clear message on a screen, log the error to a record your team monitors, or notify the right group. Just remember the earlier point. A fault path handles element failures, not governor limit breaches, which roll back the whole transaction regardless.
Build Small and Test with the Debugger
Salesforce recommends building flows in a sandbox or Developer Edition org, starting with a few elements, getting them working, and adding functionality in small iterations so you know which change caused a problem.
The Flow Builder debugger shows step-by-step details of how a flow runs and the path it takes. Pay attention to its warning: if you debug without selecting “Run flow in rollback mode,” the flow performs its actions for real, including DML.
For performance work specifically, the best practices page points to the Apex debug log. The FLOW_ELEMENT_LIMIT_USAGE event shows CPU time consumption for each flow element, which tells you where a slow flow is actually spending its time.
If You Still Have Workflow Rules or Process Builder
Salesforce ended support for Workflow Rules and Process Builder on December 31, 2025. Existing automation keeps running, but Salesforce no longer provides customer support or bug fixes for either feature, and it recommends migrating to Flow using the Migrate to Flow tool. If your org still has active rules or processes, migration is a good moment to apply everything above: before-save where possible, tight entry conditions, no DML in loops, and real names on every element.
If you’d like a second set of eyes on an org’s automation before or after a migration, that’s the kind of review we do.
Sources
- Salesforce Help: Per-Transaction Flow Limits
- Salesforce Help: Flow Bulkification in Transactions
- Salesforce Help: Plan for Success with Flow Builder Best Practices
- Salesforce Architects: Record-Triggered Automation Decision Guide
- Salesforce Help: Subflow Element
- Salesforce Help: Customize What Happens When a Flow Fails
- Salesforce Help: Test or Troubleshoot Flows with the Flow Builder Debugger
- Salesforce Help: Workflow Rules & Process Builder End of Support