[object Object]

Workflow Studio is not Flow Designer with a new coat of paint. The execution model, the state-handling, and the trigger plane are different enough that a lift-and-shift will produce flows that look identical and fail in ways the original never did. The migration is worth doing — eventually, for most flows — but only with eyes open.

Why migrate at all

Flow Designer still runs, still ships fixes, and is not deprecated. The pressure to move is not “the old thing is going away.” The pressure is that new platform capabilities — advanced triggers, richer parallelism, better observability, deeper Now Assist integration — land in Workflow Studio first and arrive in Flow Designer either late or never.

If your flows are mostly synchronous, single-trigger, and short-lived, you can stay on Flow Designer indefinitely with no penalty. If your flows do any of the following, the migration starts earning its keep:

  • Long-running orchestrations with multiple wait points
  • Parallel branches that need to merge cleanly
  • Triggers from multiple sources into the same workflow
  • Heavy reuse via subflows that you wish behaved more like real functions

What does not survive a copy-paste

The siren song is “export from one, import into the other.” That path exists for some constructs and does not for others. The pieces that do not translate cleanly:

  1. Custom actions written against the old Action Designer — their step composition is different. The action shell ports; the internal step graph often does not.
  2. Subflows with output variables that callers read by position — Workflow Studio is stricter about output binding. Anything that worked through implicit ordering will break.
  3. Wait-for-condition steps — semantics around timeout vs. signal differ. Re-author these, do not migrate them.
  4. Flow-level error handlers — the error propagation model is different enough that a working error handler in Flow Designer can swallow errors silently in Workflow Studio.

For everything else — trigger setup, action calls, scripts, data lookups, decision logic — a careful manual rebuild is faster than fighting a half-broken import.

The parallel-run pattern

The single most important pattern: run both flows in parallel for a full business cycle, comparing outputs, before you cut over. Here is the wrapper:

// Script Include: DualFlowDispatcher
var DualFlowDispatcher = Class.create();
DualFlowDispatcher.prototype = {
    initialize: function() {},

    dispatch: function(flowName, inputs, recordSysId, recordTable) {
        var canary = gs.getProperty('workflow_studio.canary.' + flowName, 'false');
        var compare = gs.getProperty('workflow_studio.compare.' + flowName, 'false');

        if (canary === 'true') {
            // Cutover: new flow is authoritative
            sn_fd.FlowAPI.getRunner().flow('workflow_studio_' + flowName)
                .inForeground().withInputs(inputs).run();
            return;
        }

        // Old flow is authoritative
        var oldResult = sn_fd.FlowAPI.getRunner().flow('legacy_' + flowName)
            .inForeground().withInputs(inputs).run();

        if (compare === 'true') {
            // Shadow run: new flow runs but does not affect record
            var shadowInputs = this._cloneInputs(inputs);
            shadowInputs.shadow_mode = true;
            sn_fd.FlowAPI.getRunner().flow('workflow_studio_' + flowName)
                .inBackground().withInputs(shadowInputs).run();
        }
    },

    _cloneInputs: function(inputs) {
        return JSON.parse(JSON.stringify(inputs));
    },

    type: 'DualFlowDispatcher'
};

The new flow respects a shadow_mode input and writes its decisions to a comparison table instead of touching the record. After two weeks, you query the comparison table for divergences. Divergence count near zero means you can flip the canary property and let the new flow take over.

The comparison table

Table: u_flow_compare_log
  u_flow_name          (String)
  u_record_table       (String)
  u_record_sys_id      (Reference)
  u_legacy_outcome     (String)
  u_new_outcome        (String)
  u_legacy_duration_ms (Integer)
  u_new_duration_ms    (Integer)
  u_diverged           (Boolean)
  u_diff_summary       (String)

The u_diff_summary field is the one engineers will actually read. Keep it short — three or four lines describing what differed. Anything longer and nobody reads it and the divergence sits unresolved for weeks.

Triggers: the silent breakage

The trigger model is where most migrations quietly go wrong. In Flow Designer, a record-update trigger fires on the first save that matches the condition. In Workflow Studio, the trigger model lets you specify more granular conditions — including “fires once per record state transition” semantics — and the defaults are different.

The result: if you rebuild a flow without re-examining its trigger, you may end up either firing twice where you used to fire once, or vice versa. The fix is mechanical but tedious: for every migrated flow, write out in plain English what should cause it to fire, then build the trigger from that statement. Do not pattern-match from the old trigger config.

Subflow boundaries get sharper

In Flow Designer, calling a subflow felt like calling a function with loose typing. Workflow Studio tightens this. Inputs are validated more strictly, outputs are bound by name not position, and the lifecycle of subflow execution is more explicit. This is good — it catches real bugs — but it means subflows that worked through happy-path luck will start surfacing errors.

The migration discipline: every subflow gets its input and output contract written down (in a comment block at the top of the flow description) before you build it. If you cannot write the contract, the subflow is not coherent enough to migrate yet.

Observability: use what you finally have

Workflow Studio’s execution logs are richer. Use them. Build a saved list filtered to flows that took longer than a threshold or hit a non-success terminal state, and review it daily during the migration window. Pin it to the platform ops dashboard. Most teams discover during this review that the legacy flow had been failing for months in cases nobody noticed because Flow Designer’s error visibility was thinner.

For the broader pattern of flow performance discipline, our piece on GlideAggregate vs GlideRecord count benchmarks covers the query-side of the same problem.

Cutover discipline

When you flip canary to true:

  • Do it during business hours, not at 2am. You want engineers awake.
  • Keep the legacy flow active but disabled, not deleted. Rollback is a sys_property flip.
  • Watch the comparison table for the first 48 hours — divergence rate should stay at the baseline you established during shadow mode.
  • Have a written rollback decision criterion: “If we see more than N divergences per hour, we flip back.” Write the number down before cutover, not during the incident.

What not to migrate

Some flows should die instead of being migrated. Run the migration list through these filters:

  • Flows that fire less than once a month — delete or consolidate
  • Flows whose business owner cannot articulate what they do — investigate before migrating
  • Flows that exist to work around a bug that has since been fixed — delete
  • Flows duplicated across scopes — consolidate to one

A migration is the cheapest moment you will ever have to clean house. Spend the morning auditing before you spend the month rebuilding.

UI considerations during the migration

If your flows surface status in a record-producer or service-portal UI (“Request being processed…”), keep the status field schema unchanged across the cutover. Users do not care which engine is running; they care that the status badge looks the same. A migration that breaks visual consistency for end users will get reported as an outage even when the workflow itself succeeded.

Update set hygiene during migration

A migration produces two parallel update sets — one for the new flow, one for the old flow’s deprecation. Keep them separate. Merging deprecation into the build update set means rolling back one rolls back both, which is rarely what you want.

Naming convention that survives the half-migrated state:

  • migration_<flow_name>_new — the rebuilt flow, the wrapper changes, the comparison logging.
  • migration_<flow_name>_decom — committed only after cutover; deactivates the legacy flow.
  • migration_<flow_name>_rollback — pre-built, never committed unless you need it. Reactivates legacy, deactivates new.

The pre-built rollback set is the discipline. Building it during an incident is what makes incidents into outages. Building it during the calm before cutover is cheap and pays for itself the moment you need it.

What to migrate first

You will be tempted to migrate the simplest flow first to learn the tool. Resist. Pick a medium-complexity flow that exercises the migration friction points (multiple triggers, at least one subflow, at least one wait step) and migrate that first. The learning is more valuable than the win.

After the medium flow, migrate the highest-volume flows. The performance and observability benefits compound, and these are the flows the platform team feels every day. Save the rarely-used flows for last; they may not be worth migrating at all.

Tradeoffs to name out loud

The parallel-run pattern doubles execution cost for the comparison window. For low-volume flows, this is invisible. For high-volume flows — anything firing more than a few thousand times a day — the shadow runs consume real capacity. Time-box the comparison window. Two weeks is usually enough for weekly business cycles; four if you have month-end patterns.

The other honest tradeoff: Workflow Studio is newer. You will find rough edges. The platform team’s response time on Workflow Studio issues is fast, but if you cannot tolerate occasional friction on platform tooling, postpone the migration by a release.

Key takeaways

  • Migrate when new platform capability lands only in Workflow Studio; otherwise Flow Designer is fine to keep running.
  • Lift-and-shift fails on custom actions, positional subflow outputs, wait conditions, and error handlers. Re-author those four constructs by hand.
  • Run both flows in parallel with a shadow-mode pattern and a comparison table before any cutover.
  • Triggers are the silent breakage. Write each trigger’s intent in plain English before rebuilding it.
  • Use the migration as a forcing function to delete dead flows. The cheapest cleanup is the one you are already paying for.
[object Object]
Share