Skip to main content

Flow Structural Conventions

Per-Flow Design

  1. Flows should have one easily identifiable Triggering Element.
    This relates to the Naming Conventions.
    For Record-Triggered Flows, this is easy - it is the Record that triggers the DML.
    For Event-based Flows, this should be a single event, as simple as possible.
    For Screen Flows, this should be either a single recordId, a single sObject variable, or a single sObject list variable. In all cases, the Flow that is being called should query what it needs by itself, and output whatever is needed in its context.
    For Subflows, the rule can vary - it can be useful to pass multiple collections to a Subflow in order to avoid recurring queries on the same object. However, passing multiple single-record variables, or single text variables, to a Subflow generally indicates a design that is overly coupled with the main flow and should be more abstracted.

    A good example of Triggering Elements naming
  2. Fill in the bloody descriptions. 
    You'll thank yourself when you have to maintain it in two years.
    Descriptions Likeshould seriouslynot be technical, but functional. A Consultant should be able to read your Flow and know what it takesdoes twotechnically. seconds,The stopDescriptions arguingshould andtherefore justexplain dowhat it.function the Flow provides within the given Domain (if applicable) of the configuration.

    Descriptions shouldn’t be too technical.
  3. All Pink (DML or Query) elements should have Error handling. 
    Screen Flow? Throw a Screen, and display what situation could lead to this. 
    Record-triggered Flow? Throw an email to the APEX Email Exception recipients. Hell, better yet throw that logic into a Subflow and call it from wherever. 
    Or send a Notification I don't care. CATCH. ERRORS.Notification.
    (Note that if you are in a sandbox with email deliverability set to System Only, regular flow emails and email alerts will not get sent.)

    If the DMLs fail, the user gets presented with an error message written in the screen.
  4. Don't do DMLs or Queries in Loops. Simpler: No pink squares in loops. 
    Salesforce now actually warns you when you're doing this, but it still bears saying. 
    To avoid doing this, ASSIGN a single record that you want to process to a collection variable in your loop, then do the DML outside of the loop at the end of your Flow. *1

    Don’t do this
  5. If you have the same logic happening in various places, this is where you should berepackage consideringthis tologic useinto Subflows.a Sub-flow.
    This will avoid you having to modify the same thing in 6 places. It also makes the Flows easier to read. *2

  6. Don't exit loops based on decision checks. 
    The Flow engine doesn't support that well and you will have weird and confusing issues if you ever go back to the main loop.

    Don’t do this either - always finish the loop
  7. Don't

    Do not design Flows that will have long Wait elements and will be called by thousands of records.
    You'll exceed your Paused Interview limits. This kind of use non-officialcase componentsit withoutshould checkingbe theira limits.Scheduled Yesflow UnofficialSFanyway.

  8. Do not rely on implicit references.
    This is great,when you query a record, then fetch parent information via {MyRecord.ParentRecord__c.SomeField__c}. While this is useful, it’s also very prone to errors (specifically with fields like RecordType and itmakes alsofor containswonky componentserror thatmessages.

    are not bulkified or contain bugs.

Cross-Flow Design

  1. Try to pass only one Record variable or one Record collection to a single Subflow.Subflow.
    See Per-Flow design item 1.
    Initializing a lot of Record variables on run often points to you being able to split that subflow into different functions. Passing Records as the Triggering Element, and configuration information as variables is fine within reason.

    Example - the Pricebook2Id variable should be taken from the Order variable.
  2. Try to make Subflows are reusable as possible.
    A Subflow that does a lot of configurationdifferent informationactions aswill variables is fine, though.

  3. Once at the point of designing subflows, try to make those reusable as much as possible - you mightprobably be ablesingle-use, to reuse a subflow from another main flow laterand if you designneed thema well.subpart of it in another logic, you will probably build it again, which may lead to higher technical debt.
    If at all possible, each Subflow should execute a single function, within a single Domain.

  4. Each Flow should be tied to a single Domain, and communication between Domains should be handled by Events.
    See Domain-Driven Design.
    In short, if a Flow starts in Sales (actions that are taken when an Opportunity closes for example) and finishes in Invoicing (creates an invoice and notifies the people responsible for those invoices), this should be two separate Flows, each tied to a single Domain.

    In this example, the Technician On Site flow signals that an Intervention has started, which triggers a Work Order related flow.
  5. Communication between Flows should ideally be handled via Events to avoid heavy coupling.
    In the example above, a Flow that starts in Sales should fire an “Opportunity Closed” event, which will be listened to by the “Start Invoicing” flow to trigger the actions tied to Invoicing.
    See above example

  6. Avoid cascading Subflows wherein one calls another one that call another one. 
    Unless the secondary subflows are basically fully abstract methods handling inputs from any possible Flow (like one that returns a collection from a multipicklist), you're adding complexity in maintenance which will be costly.

Domain-Driven Design

  1. Identify the Domain of your Flow when you create it.
    One flow should have exclusively one attributable Domain.

  2. If you need to modify elements from another Domain in your Flow, emit an Event matching the current status instead, and use that to start another flow which has said attributed Domain.

  3. Domains are definable as Stand-alone groupings of function which have a clear Responsible Persona.

High-level communication between Domains

 

Record-Triggered Flow Design

  1. OneCreate Triggeronly Perone Flow per Object is... "subject to considerations" now. SFXD as of this writing has no formal best practice on the number of flowsand per Object.Operation type The- currentmeaning recommendationsone belowBEFORE mayCreate, changeone dependingBEFORE onUpdate, newone releasesAFTER orCreate, communicationsetc.
    In all cases apart from Salesforce.

    BEFORE
    1. Prioritize BEFORE-save operations whenever possible. This is more efficient in every way.
    2. You can't really leverage Subflows yet (at least not for Before-save), but it will come later, so try to design in a wayflows, you can leverage Subflows laterto ifensure atorder allof possible.
    3. execution
    4. Recordand triggeredease Flowsof maintenance.
      For BEFORE Flows, for the moment it should still be 1best perpractice Objectto andhave Operationa (sosingle `RTFL01_ACC_BeforeUpdate`,flow, `RTFL02_ACC_AfterUpdate`of )which excludingthe Emailfunctions Flows.are split via Decision nodes.

      1. In the case of BeforeUpdate, the "decisions" should be done in a vertical tree reminiscent of Process Builder, and the actual actions and logic to the right side. This is done to familiarize Admins with Flows if they open them, and to ease migrating to Subflows when possible.

      2. In the case of AfterUpdate, the "decisions" should be done in a vertical tree reminiscent of Process Builder, and the logic should be stored in Subflows whenever possible, and called on the right side of the decision.

    5. Prioritize BEFORE-save operations whenever possible.
      This is more efficient in every way for the database, and avoids recurring SAVE operations.

    6. Try to leverage Sub-Flows in all designs, including Record-Triggered Flows, for the reasons outlined above.

    7. Flows that exclusively are Email handlers should be their own record-triggered flows. 
      This is done because the conditions for evaluating Flows can impact how Email sending is handled. Another advantage is that you can turn off your “email handler” flows while making changes or testing your other flows.

    8.  

      You may want to start out in Autolayout Mode (beta feature) which makes it easier for beginners and keeps things nice and neat, and then turn it off as needed since it doesn’t support everything you might want to do. You can turn it on and off as needed so there’s no commitment involved in that decision.

    On Delayed Actions

    Flows allows you to do complex queries and loops as well as schedules. As such, there is virtually no reason to use wait elements or delayed actions, unless said waits are for a platform event, or the delayed actions are relatively short.

    Any action that is scheduled for a month in the future for example should instead set a flag on the record, and let a Scheduled Flow evaluate the records daily to see if they fit criteria for processing. If they do in fact fit criteria, then execute the action.

    A great example of this is Birthday emails - instead of triggering an action that waits for a year, do a Scheduled flow running daily on contacts who's birthday it is. This makes it a lot easier to debug and see what’s going on.

    *1  This is easy to work around for DML, but sometimes less so for Get Records. There are apex actions you can install to work around this issue, with the caveats mentioned above about installed actions. If you are building a screen flow, this is less of an issue.

    *2 (Note that as of Spring 21, record-edit flows do not yet support calling subflows. You may consider waiting to use them until they do.)