2026 context: Workflow Rules and Process Builder are fully deprecated. Record-Triggered Flows are now the primary automation tool. These best practices apply to all Flow types but focus on Record-Triggered Flows.
1. Before-Save vs After-Save
This is the single most impactful decision in Record-Triggered Flow design.
USE BEFORE-SAVE WHEN:
✓ Updating fields on the triggering record
✓ You want the update in the same transaction without extra DML
✓ Field validation or defaulting logic
USE AFTER-SAVE WHEN:
✓ Creating or updating related records
✓ Sending emails or notifications
✓ Making callouts (via Apex action)
✓ Querying the saved record's Id (new inserts)
✓ Publishing platform events
Before-save flows are significantly more efficient — they don't consume a DML statement for the field update because the record hasn't been written yet. Use before-save whenever you're only modifying the triggering record.
2. Build Bulk-Safe Flows
Flows process records one at a time by default in record-triggered context, but they share the same transaction governor limits as all Apex. A flow with a Get Records inside a loop will hit the 100 SOQL limit on the first data load over 100 records.
Anti-pattern: A Loop element that contains a Get Records or Create Records element. Every iteration burns a SOQL query or DML statement.
Bulk-safe patterns to use instead:
- Use Get Records outside loops to query all related records at once into a collection variable.
- Use Assignment elements inside loops to build up a record collection variable.
- Use a single Create Records or Update Records element after the loop to perform DML once on the whole collection.
- Never put DML actions (Create/Update/Delete Records) or Get Records inside a Loop element.
3. Always Configure Fault Paths
Every element that can fail — Create Records, Update Records, Delete Records, Apex Actions, Send Email — should have a fault path configured. Without one, an unhandled fault rolls back the entire transaction and shows a generic error to the user.
Action Element (Create Records / Apex Action / etc.)
│
├─ SUCCESS path → continue flow
│
└─ FAULT path →
├─ Assignment: capture {!$Flow.FaultMessage}
└─ Choice:
├─ Screen Flow: Display error screen to user
└─ Auto-launched Flow: Send email to admin / log to object
4. One Flow Per Object Per Trigger Type
Just like triggers, multiple record-triggered flows on the same object fire in an indeterminate order. Consolidate logic into one flow and use Decision elements to branch for different scenarios. This avoids hard-to-debug ordering issues and keeps your automation inventory manageable.
5. Use Subflows for Reusability
If the same logic appears in multiple flows, extract it into an autolaunched flow and invoke it as a Subflow. Common candidates: validation logic, notification patterns, record creation templates.
Main Flow (Record-Triggered)
└─ Decision: Which type of change?
├─ New High-Value Opportunity → Subflow: NotifyTeam
├─ Stage Changed to Closed Won → Subflow: CreateOnboardingTask
└─ Account Changed → Subflow: SyncRelatedContacts
Subflow: NotifyTeam (Autolaunched)
└─ Input: {recordId}, {opportunityName}, {ownerEmail}
└─ Send Email action
└─ Create Task action
6. Avoid Hard-Coding IDs and Values
Record IDs are org-specific. A flow with a hard-coded Profile ID, Record Type ID, or User ID will break in every sandbox and every production org it's deployed to. Use alternatives:
- Use Get Records to look up the ID by Name or DeveloperName at runtime.
- Use Custom Labels for configurable string values.
- Use Custom Metadata for multi-value configuration tables that can be deployed without code.
7. Flow vs Apex — When to Use Which
USE FLOW WHEN:
✓ Field updates on triggering record or immediate children
✓ Sending standard email alerts
✓ Creating tasks, chatter posts, or simple child records
✓ Simple conditional branching
✓ Screen flows for guided user input
USE APEX WHEN:
✗ Complex multi-object DML with conditional relationships
✗ Callouts to external APIs with custom auth/headers
✗ Batch processing of 50,000+ records (use Batch Apex)
✗ Complex string/data manipulation beyond Flow formulas
✗ Recursion control that Flow can't handle
✗ Custom exception handling with try/catch/finally
Debug tip: Use Flow Builder's built-in debug run tool (the bug icon) to step through your flow with custom input values and see exactly which path each element took and what values were set.