Why Subflows Exist

When two flows need the same 10 steps, the temptation is to copy-paste. Six months later, a bug fix lands in one copy and not the other, and you have a production mystery.

Subflows solve this. Extract the shared logic into an Autolaunched Flow, mark it “available as a subflow,” and call it from wherever. One copy, one behavior, one place to fix bugs.

The tradeoff is indirection. Done well, subflows are a clear contract. Done badly, they’re a hall of mirrors where nobody can find the actual logic.

Pattern 1: The Parameterized Calculator

Subflow that takes a few inputs, runs logic, returns a value. No DML, no side effects.

Example: Calculate_Case_Priority takes an Account tier, a Case type, and a customer SLA; returns a priority level.

Properties:

  • Pure function: same inputs, same outputs.
  • No DML, no callouts, no record updates.
  • Fast; safe to call inside loops.

This is the safest, most reusable kind of subflow. Aim for as many of these as you can.

Pattern 2: The Entity Updater

Subflow that takes a record Id and performs a well-defined update.

Example: Escalate_Case takes a Case Id, sets Status to Escalated, assigns to the escalation queue, and creates a note.

Properties:

  • Performs DML.
  • Single responsibility (one logical operation).
  • Has a clear success/failure return.

Callers don’t need to know how a case is escalated — they just invoke it. When escalation rules change, you change one place.

Pattern 3: The Orchestrator

Subflow that coordinates a multi-step business process, potentially calling other subflows.

Example: Process_Renewal calls Validate_Contract, then Calculate_Discount, then Create_Renewal_Opportunity.

Properties:

  • Composed of other subflows.
  • Shallow — 3 to 6 steps, not 30.
  • Has its own fault path.

Orchestrators are powerful but brittle. Keep them shallow. If an orchestrator is calling an orchestrator calling an orchestrator, someone’s going to have a bad debugging session.

Pattern 4: The Guard

Subflow that returns a boolean after checking a set of conditions.

Example: Can_User_Approve_Discount takes a discount amount and a user; returns true if the user’s profile permits it.

Properties:

  • Returns a boolean output variable.
  • No DML.
  • Used in Decision elements before taking action.

This pattern extracts permission or eligibility logic into one place — crucial for auditability. When compliance asks “where do we enforce the 20% discount approval rule?” the answer is one subflow, not fourteen places.

Pattern 5: The Notifier

Subflow that formats and sends a communication — email, platform event, custom notification, or Chatter post.

Example: Notify_Case_Owner_Of_Update takes a Case Id and a message; sends the right channel based on user preferences.

Properties:

  • Single communication job.
  • Hides channel selection logic from callers.
  • Retries gracefully on transient failures.

When you switch from email to in-app notifications for a channel, you update the notifier subflow; no caller changes.

Input Design Rules

Every subflow has an input contract. Treat it like an API.

Name inputs unambiguously. caseId is clearer than id. targetAmount is clearer than amount.

Limit inputs to what’s needed. A subflow that takes a whole record collection when it only needs one field is lazy design.

Use types that constrain. Pass an Id, not a String. Pass a picklist value typed as an enum if you can.

Version via name, not via logic branches. If the contract changes, create Escalate_Case_V2. Keep V1 until all callers migrate. Don’t add flags to toggle old vs. new behavior in one flow.

Output Design Rules

Outputs are just as important.

Always return enough to reason about what happened. A notifier might return success: true/false, messageId: String, failureReason: String.

Never return massive collections. The caller rarely needs them, and passing large payloads through subflow boundaries kills performance.

Document outputs in the description. The description is read by everyone who considers calling your subflow. Tell them what they get back.

Bulkification: Subflow Is Not a Bulkification Strategy

A common misconception: “I’ll just call this subflow from a loop and it’ll be fine.”

It won’t. If the subflow contains DML, you’ll hit governor limits at scale. If it contains SOQL, same. Bulkification means the whole chain — including subflows — handles collections, not single records.

Practical rule: a subflow called inside a loop must be pure (no DML, no SOQL). Everything else needs bulk input/output patterns where the subflow processes a collection and returns a collection.

Versioning and Change Management

Flow versioning via activation is real but limited. The safer discipline:

  • Name subflows with semantic intent, not implementation details.
  • When the contract changes, create a V2 rather than editing in place.
  • Keep a usage inventory. Before deleting an old version, grep metadata for callers.
  • Test each caller against the new version before switching.

Debugging Chains

When a parent flow fails and the error trace points into a subflow, debugging gets harder because the debug log spans both. Two habits help.

Add a “Step” text variable. Pass a short string like “Escalate.Step1” into each subflow so the log shows the breadcrumb.

Use named fault paths. Every subflow should have a fault path that captures the error context and routes it back to the caller as a structured output, not as an unhandled exception.

When Not to Use a Subflow

Not every shared logic chunk belongs in a subflow. Alternatives:

  • One or two shared steps: Flow elements don’t need their own subflow. Just duplicate.
  • Complex conditional logic: sometimes Apex is clearer and easier to test.
  • Cross-object queries with performance sensitivity: SOQL in Apex, not in Flow.

Frequently Asked Questions

Can a subflow call itself?

Recursive subflows are technically possible, but Salesforce’s flow recursion limits (depth and steps) make them impractical. Use a batch pattern instead.

How deep should my subflow hierarchy go?

Keep it shallow — typically no more than 2 levels of nesting. Deep chains become unreadable.

Can I package a subflow?

Yes. Subflows deploy via change sets, unmanaged packages, and DX source control like any other flow metadata.

Are subflows slower than inline logic?

There is minor overhead for each subflow invocation. For high-throughput batch flows, this matters. For normal transactional use, it does not.

Share