XSCREENSAVER / 3D PIPES

[readonly] markdown buffer

Flow-Friendly Apex Is Becoming Its Own Design Discipline

May 22, 2026 · 9 min read

An invocable method is not finished when Flow can call it. It is finished when an admin can configure it without opening the Apex class.

Summer '26 moves the platform closer to that standard. The individual changes are small, but together they turn an Apex action into a proper Flow Builder surface.

The eight changes that matter

1. Design the Flow contract first

Start with the labels, descriptions and results an admin needs. Then write the private implementation.

public with sharing class CreateFollowUpAction {
    public class Request {
        public Request() {}

        @InvocableVariable(label='Source Record ID' required=true)
        public Id sourceRecordId;

        @InvocableVariable(label='Priority' required=true)
        public String priority;
    }

    public class Result {
        public Result() {}

        @InvocableVariable public Id sourceRecordId;
        @InvocableVariable public Id taskId;
        @InvocableVariable public Boolean success;
        @InvocableVariable public String message;
    }

    @InvocableMethod(label='Create Follow-Up Task')
    public static List<Result> run(List<Request> requests) {
        // Return one result for every request, in the same order.
        return new List<Result>();
    }
}

The method accepts a list, so its design must account for every item in that list. A structured result lets Flow branch, display an error or retain the created record ID.

2. Use picklists instead of magic strings

Inputs such as priority, mode or strategy should not be free text. Summer '26 lets action metadata provide static values or values from an Apex class extending VisualEditor.DynamicPicklist.

This turns “type the exact value the developer expects” into a valid set of choices. Dynamic providers should remain fast and predictable because they run while an admin is configuring the Flow.

3. Use selectors for metadata

An action can now present selectors for supported setup references, including Custom Metadata, Named Credentials, Reports, Documents and Static Resources.

That is better than asking an admin to copy a developer name into a text field. The builder chooses a valid record and the action receives its resolved ID.

4. Add custom editors only where they help

Custom property editors can target individual inputs rather than replacing the whole action panel. That suits related fields such as an assignee type and assignee lookup: the lookup can appear only when the chosen mode needs one.

Keep ordinary inputs on the standard editor. Custom UI should explain a concept, not decorate every field.

5. Make optional inputs genuinely optional

The redundant Include toggle is gone for optional Apex inputs. That makes the panel cleaner, but it also means blank must have safe, documented behaviour.

Prefer one optional template selector that falls back to a default over a Boolean switch plus a second conditionally required field.

6. Give generic sObjects visible context

For generic sObject actions, Flow Builder now shows the non-object inputs first and reveals object-related fields after an object type is selected.

The action still needs guardrails. A record collection and a controlled status field can be reasonable; free-text fields for a WHERE clause and update expression are not low code. They are an unsafe query editor.

7. Return data Flow can use

Custom types and collections can be exposed as action outputs. Use them for validation issues, created record IDs and per-item outcomes rather than returning JSON strings or sending an admin to the debug log.

A useful result normally answers three questions: which input was processed, did it succeed and what should the Flow do next?

8. Keep the no-argument constructor

Flow must be able to instantiate Apex-defined parameter types. Give each one a visible no-argument constructor, especially when a second convenience constructor exists.

public class Request {
    public Request() {}
    public Request(Id sourceRecordId) {
        this.sourceRecordId = sourceRecordId;
    }

    @InvocableVariable(required=true)
    public Id sourceRecordId;
}

For managed packages, check the required visibility carefully before release.

The standard to aim for

A good Apex action has a narrow business label, valid choices, safe defaults and structured results. It respects sharing, CRUD and field security, and its bulk behaviour is predictable.

Most importantly, an admin should be able to use it without knowing the class name, guessing a magic string or reading its implementation. Summer '26 supplies better building blocks; developers still have to turn them into a coherent product surface.

Sources