With Record-Triggered Flows now mature, the boundary has shifted. The decision pivots on complexity and performance.
Choose Flow when:
- Logic is straightforward field updates, related-record creates/updates.
- Logic is conditional but expressible in Decision elements.
- The team doesn't have a developer to maintain Apex.
- Future modifications likely come from admins.
- Performance demands are typical.
Choose Apex Trigger when:
- Performance-critical — hot-path triggers on millions of records.
- Complex bulk patterns — flows that loop and modify collections get complicated.
- Custom error handling — partial-success logic.
- Recursion-aware logic — Apex's static state pattern is cleaner.
- Cross-object atomic transactions — Apex's transaction model is more predictable.
- Apex Managed Sharing — flows can't write to
__Sharetables. - Complex callouts — multiple HTTP calls with conditional branching.
Mixed pattern (often best): Flow at the entry point (orchestration, simple decisions), Apex Invocable Method called from Flow for heavy lifting. Admins can tweak orchestration; developers own the logic.
Anti-patterns: Apex Trigger AND a Record-Triggered Flow on the same object firing the same logic; Flow with hundreds of elements; Apex doing what one Flow Decision could do.
