This article discusses Cloudflare's approach to automatically generating visual diagrams for their Workflows engine, which executes dynamic code rather than declarative configurations. The core challenge involves using Abstract Syntax Trees (ASTs) to statically analyze JavaScript code, including minified versions, to derive the execution flow and relationships between steps, Promises, and functions. This system design enables developers to visualize complex, dynamically executed workflows, improving observability and debugging.
Read original on Cloudflare BlogCloudflare Workflows is a durable execution engine designed for long-running processes, enabling features like chained steps, retries, and state persistence. Unlike traditional workflow builders that rely on declarative configurations (e.g., JSON/YAML), Cloudflare Workflows are written directly in code, supporting dynamic execution patterns such as Promises, loops, and conditionals. The article focuses on the system developed to generate visual diagrams for these code-driven workflows, which is crucial for understanding complex execution flows and debugging.
Workflow engines typically operate under either static (sequential) or dynamic execution models. Cloudflare Workflows adopt a dynamic model where steps execute as the runtime encounters them. Unawaited steps run in parallel, giving developers significant flexibility in flow control without needing explicit wrappers. The workflow engine, implemented as a Durable Object, orchestrates step execution, persisting results and managing retries. This dynamic nature, however, complicates static analysis for diagram generation as the engine doesn't inherently know the complete step order beforehand.
To overcome the challenge of visualizing dynamic code, Cloudflare fetches the workflow script at deploy time (after bundling and minification) and uses an Abstract Syntax Tree (AST) parser. The AST allows static analysis to derive the workflow's structure, tracking `Promise` and `await` relationships to identify parallel execution, blocking operations, and overall step connectivity. This process involves generating an intermediate graph from the AST and then mapping it to a predefined set of workflow node types for rendering.
Handling Minified Code
A significant hurdle is parsing minified JavaScript, which varies depending on the bundler (e.g., esbuild, rspack, vite). Cloudflare addresses this by using `oxc-parser` from the Rust-based JavaScript Oxidation Compiler (OXC), deployed as a WebAssembly Worker. This Rust Worker converts minified JS into AST node types and then into the graphical representation.
Accurately tracking step and function relationships is critical. The system collects both function and step names, even when steps are wrapped in functions, defined as functions, or dynamically called. For functions, a subgraph is created to determine if it contains direct or indirect step calls; leaf functions without any step involvement are trimmed. The parsing logic also accounts for various control flow patterns, including different types of loops (`for...of`, `while`, `map`, `forEach`), branching (`switch`, `if/else`, ternary operators), and error handling (`try/catch/finally`).
const summaryPromise = step.do("summary agent", async () => { /* ... */ });
const correctnessPromise = step.do("correctness agent", async () => { /* ... */ });
const clarityPromise = step.do("clarity agent", async () => { /* ... */ });
// All three 'step.do' calls are executed in parallel due to Promises