Turning Cloudflare Workflows code into visual diagrams
Cloudflare Workflows is a durable execution engine that chains steps, retries on failure, and persists state across long-running processes. Developers use it to build background agents, data pipelines, human-in-the-loop approval systems, and similar applications.
Every workflow deployed to Cloudflare now ships with a complete visual diagram in the dashboard. That capability matters because coding agents increasingly produce code developers may not fully read — but the shape of what gets built still matters: how steps connect, where they branch, and what actually happens at runtime.
Most visual workflow builders work from declarative formats like JSON or YAML. Cloudflare Workflows, by contrast, are just code. They can include Promises, Promise.all, loops, conditionals, and nesting inside functions or classes. That dynamic execution model makes rendering a static diagram more involved, so Cloudflare uses Abstract Syntax Trees (ASTs) to derive the graph, tracking Promise and await relationships to identify parallelism, blocking behavior, and connections between steps.
Dynamic vs. sequential execution
Workflow engines generally follow one of two models: dynamic execution or sequential (static) execution. Sequential execution triggers a workflow, runs step A, then step B, then step C — each starting as the previous one completes. Cloudflare Workflows use the dynamic model instead: because workflows are code, steps execute as the runtime encounters them. When the runtime discovers a step, it hands that step to the workflow engine, which manages execution. Steps are not sequential unless explicitly awaited; the engine executes all unawaited steps in parallel, letting developers write flow control naturally without wrappers or directives.
The runtime handoff works like this:
- An engine — a "supervisor" Durable Object for the instance — starts, handling the actual workflow execution logic.
- The engine triggers a user worker via dynamic dispatch, passing control to the Workers runtime.
- When the runtime encounters a
step.do, execution returns to the engine. - The engine runs the step, persists the result (or throws an error), and triggers the user Worker again.
The engine does not inherently know the order of the steps it executes — but order is essential for a diagram. The challenge is translating the broad range of workflow code into a diagnostically useful graph.
Parsing the code at deploy time
Fetching the script at deploy time — rather than runtime — allows Cloudflare to parse the full workflow and statically generate the diagram. After the internal configuration service bundles the Worker, the diagram service fetches the script, uses a parser to build an AST, and generates an intermediate graph containing all workflow entry points and calls to workflow steps. The final diagram is rendered from that graph via the API.
Bundling introduces complications. Workers deploy through esbuild by default and minify code unless told otherwise, and output varies significantly by bundler. Cloudflare's team tested parsing minified code with a Rust container: script IDs flowed through a Cloudflare Queue to messages that the container processed. Once that worked, they moved to a Rust-based Worker — Workers supports Rust via WebAssembly, and the package size made that practical.
The Rust Worker converts minified JavaScript into AST node types, then translates those AST nodes into the visual workflow representation. It generates a graph of predefined node types for each workflow and maps AST nodes onto that graph structure.
Handling step and function relationships
Rendering a workflow diagram requires tracking relationships correctly and defining node types simply enough to cover all cases. Collecting both function names and step names is essential based on how broadly functions and steps can interact — steps may be wrapped in functions, defined as functions, called from functions in different modules, or renamed.
Several patterns demand extra parsing care. Within a WorkflowEntrypoint, functions might call steps directly, indirectly, or not at all. For a function like functionA containing console.log(await functionB(), await functionC()) where functionB calls a step.do(), both functionA and functionB belong on the diagram, but functionC does not. The parser creates a subgraph for each function and checks whether it contains a step call itself or calls another function that might. Each such subgraph becomes a function node containing relevant child nodes. If a function node has no direct or indirect workflow steps inside it, it gets trimmed from the output.
The parser also examines other patterns, including static steps and variables defined in multiple ways. Scripts containing multiple workflows get treated similarly to functions, with the abstraction lifted one level. Every AST node type required consideration of every possible use inside a workflow — loops, branches, promises, parallels, awaits, arrow functions, and permutations of each. For instance, loops alone can take several forms, and branching adds its own range of variation.
Tracking execution order
Multiple code patterns — namely await, Promise, and Promise.all() — make step order inferable. A workflow that avoids these is assumed to execute steps in the order they appear. Otherwise, some mechanism must capture concurrency relationships.
Cloudflare's solution tracks execution order through starts: and resolves: fields on each node. These indices indicate when a promise started executing and when it ended, relative to the first promise that started without an immediate, subsequent conclusion. Vertically in the diagram UI, all steps with starts:1 line up. Steps that are awaited upon declaration have undefined starts and resolves, and execute in order of appearance to the runtime.
Encountering an unawaited Promise or Promise.all() marks that node (or nodes) with an entry number in starts. When an await appears on that promise, the entry number increments by one and is stored as the exit number in resolves. That reveals which promises run concurrently and their completion order relative to one another.
After cataloguing these patterns, Cloudflare settled on a defined set of workflow node types. Sample API responses illustrate the shape: separate structures represent function calls, if conditions branching to step.do, and parallel execution combining step.do with waitForEvent.
Roadmap
The workflow diagrams are intended as a full-service debugging tool. Planned capabilities include:
- Real-time execution tracing through the graph
- Error discovery, waiting on human-in-the-loop approvals, and skipping steps for tests
- Visualizations in local development



