Claude Code Dynamic Workflow — How to Automate Large-Scale Migration and Security Audits with Hundreds of Subagents
This past May, Jarred Sumner, the creator of the Bun runtime, posted a brief note on X (Twitter): it took him 6 days to port 750,000 lines of Zig code to Rust. Normally, a migration like this would keep a skilled team busy for months, so that 6-day figure was hard to believe — I spent a long time staring at it myself. The secret was Dynamic Workflow, a feature Anthropic had just added to Claude Code.
If you've been blocked by a "problem of scale" — analysis spanning thousands of files, or a large-scale migration — I think this article will be useful. Whether you're a frontend developer or a DevOps engineer, if you need to touch a codebase that's simply too large to handle all at once, this is a practical approach. Dynamic Workflow isn't just a story about "AI writing better code" — it's a subagent orchestration system that automatically breaks a single goal into hundreds of subtasks and executes them in parallel.
In this article, I'll give you an honest rundown of how Dynamic Workflow works, patterns for applying it in production, and how to avoid an unexpected token bill. If you're new to orchestration, start with the core concepts section; if you already know the concepts, feel free to jump straight to "Practical Application."
Core Concepts
A JavaScript Orchestration Script Manages Context on Your Behalf
Most existing AI agent systems have the model reason through "what to do next" at every step to continue the work. The longer the task, the more the context window (the maximum amount of text an AI model can process at once) fills up, and the more likely it is to lose the thread midway.
Dynamic Workflow takes a different approach. The user enters a goal in natural language, Claude generates a JavaScript orchestration script, and the runtime built into Claude Code executes that script in the background. There's no need to configure a separate server or set up a separate environment — all you need is Claude Code v2.1.154 or later. Orchestration logic such as task decomposition, agent count decisions, and loop conditions are all preserved in script variables, so only the final validation results enter the model's context window.
User input (natural language)
↓
Claude automatically generates a JS orchestration script
↓
Script executed by Claude Code's built-in runtime
↓
Distributed in parallel across tens to hundreds of subagents
↓
Only validated results are returnedThe key difference: Because orchestration state is preserved in script variables rather than inside the AI model's context, the context window never gets exhausted no matter how large the task grows.
Three Core Primitives
Dynamic Workflow scripts are organized around three fundamental functions.
| Function | Role | Characteristics |
|---|---|---|
agent() |
Executes a single subagent | Returns structured output when a JSON Schema is specified |
pipeline() |
Sequential streaming processing | Stage chaining, no barrier, recommended default |
parallel() |
Executes all tasks simultaneously | Barrier-based, waits for all to complete |
What is barrier-based?
parallel()does not proceed to the next step until all agents have finished.pipeline(), on the other hand, streams each stage's results to the next stage as soon as they're ready, so it streams without waiting for everything to complete.
The reason pipeline() is the recommended default is that it can pass the results of the preceding stage to the next stage as soon as they're available, which often makes it actually faster than parallel(). I initially thought "isn't parallel faster?" — but the advantage of barrier-free streaming turns out to make a bigger difference than you'd expect.
Six Official Patterns
Here are six compositional patterns officially documented by Anthropic. I've organized them with concrete examples of when to use each in production.
| Pattern | Description | When It Fits | Example |
|---|---|---|---|
| Classify-and-act | Classifies input, then routes to the appropriate agent | Triaging diverse types of issues | Classify 1,000 GitHub issues as bug/feature/inquiry, then distribute to the responsible agent |
| Fan-out-and-synthesize | Distributed processing, then consolidate results | Large-scale codebase analysis | Analyze dependencies per module → generate an overall architecture map |
| Adversarial verification | An independent verifier attempts to refute the result | Work requiring high accuracy | A separate agent reviews security scan results for false positives |
| Generate-and-filter | Generate many candidates, then filter | Code suggestions, test case generation | Generate 5 function implementations, then select the top 1 by performance criteria |
| Tournament | Evaluate multiple candidates competitively | Selecting the optimal implementation | Implement 3 algorithms, then determine the winner by benchmark |
| Loop-until-done | Repeat until no new findings emerge | Bug hunting, security audits | Fix type errors → re-check → repeat until no errors remain |
Practical Application
I've chosen three scenarios. Each combines different patterns, and as you walk through the code you'll get a feel for how the patterns actually fit together. The most impactful case is the first one — the security audit — because it's a task that was genuinely hard to perform with a single agent due to context window limits.
Example 1: Full Repository Security Audit
I'll be honest — this pattern surprised me the first time I used it. It's not just blindly scanning thousands of files; the structure that separates scan agents from verification agents to reduce the false-positive rate is quite clever. This is a combination of the Fan-out-and-synthesize and Adversarial verification patterns.
export const meta = {
name: 'security-audit',
description: 'Full repository security audit',
phases: [{ title: 'Scan' }, { title: 'Verify' }],
}
// Define these to match your project when using this in practice
const FINDING_SCHEMA = {
type: 'object',
properties: {
file: { type: 'string' },
severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low'] },
title: { type: 'string' },
description: { type: 'string' },
},
required: ['file', 'severity', 'title', 'description'],
}
const VERDICT_SCHEMA = {
type: 'object',
properties: {
confirmed: { type: 'boolean' },
reason: { type: 'string' },
},
required: ['confirmed', 'reason'],
}
// Array of module paths to analyze
const MODULES = ['src/auth', 'src/api', 'src/db', 'src/middleware']
// Step 1: Collect scan results per module (streaming via pipeline)
const findings = await pipeline(
MODULES,
m => agent(
`Detect authentication vulnerabilities, missing input validation, and unsafe patterns in the ${m} module`,
{ phase: 'Scan', schema: FINDING_SCHEMA }
)
)
// Step 2: An independent verifier attempts to refute each discovered vulnerability
const verdicts = await parallel(
findings.map(f => () =>
agent(
`Review the following vulnerability report for false positives: ${f.title} — ${f.description}`,
{ phase: 'Verify', schema: VERDICT_SCHEMA }
)
)
)
// Report only items that passed verification
return findings.filter((f, i) => verdicts[i].confirmed)The key to this pattern is separating scanning from verification. The first set of agents finds suspicious patterns; the second set independently attempts to refute each one — "is this actually a vulnerability?" This dramatically reduces the false-positive rate.
How to run it: Save this file as
security-audit.js, then run it in Claude Code with/workflow run security-audit.js, or request a security audit in natural language and Claude will automatically generate a similar script.
Example 2: Large-Scale Code Migration
Like the Zig → Rust porting work mentioned earlier, this pattern works well for language or framework migrations. Because file dependencies make ordering important, you can't just throw everything at parallel() — and it's easy to overlook the need to analyze the dependency graph first. The Loop-until-done pattern automates validation after conversion.
export const meta = {
name: 'framework-migration',
description: 'Express → Hono migration',
phases: [{ title: 'Analyze' }, { title: 'Convert' }, { title: 'Validate' }],
}
// List of files to migrate
const SOURCE_FILES = ['src/routes/auth.js', 'src/routes/user.js', /* ... */]
// FILE_ANALYSIS_SCHEMA: defined as { path, deps, expressPatterns }
// Step 1: Analyze per-file dependencies (in parallel)
const fileMap = await parallel(
SOURCE_FILES.map(f => () =>
agent(`Analyze the Express API patterns and middleware dependencies in ${f}`, {
phase: 'Analyze',
schema: FILE_ANALYSIS_SCHEMA,
})
)
)
// topologicalSort: topologically sorts the file dependency graph
// (determines conversion order without circular dependencies — if A imports B, convert B first)
const converted = await pipeline(
topologicalSort(fileMap),
f => agent(`Convert ${f.path} to Hono patterns. Dependent files: ${f.deps}`, {
phase: 'Convert',
})
)
// ERROR_SCHEMA: defined as { file, message, line }
// Step 3: Type-check converted files and fix errors (Loop-until-done)
let iterations = 0
while (iterations < 5) {
const errors = await agent('Detect type errors and runtime errors in the converted code', {
phase: 'Validate',
schema: ERROR_SCHEMA,
})
if (errors.length === 0) break
await parallel(errors.map(e => () => agent(`Fix error in ${e.file}: ${e.message}`)))
iterations++
}Loop-until-done pattern caution: To prevent infinite loops, always set a maximum iteration count (a guard like
iterations < 5). This is a situation you encounter often in production, and forgetting it can lead to unexpected token exhaustion.
Example 3: Performance Optimization Exploration
This is the task of finding optimization opportunities across an entire codebase based on profiler results. Using the Tournament pattern lets you pit multiple optimization approaches against each other and choose the best outcome. This is the case where I really felt "this is why some tasks simply can't be done with a simple chat" — 20 hotspots, each with 3 approaches, means 60 evaluations running in parallel all at once.
export const meta = {
name: 'perf-optimization',
description: 'Profiler-based performance optimization',
phases: [{ title: 'Identify' }, { title: 'Propose' }, { title: 'Rank' }],
}
// HOTSPOT_SCHEMA: defined as { location, avgMs, callCount }
const hotspots = await agent(
'Extract the top 20 hotspots from profiler results',
{ phase: 'Identify', schema: HOTSPOT_SCHEMA }
)
// PROPOSAL_SCHEMA: defined as { description, estimatedGain, riskLevel }
// Generate 3 optimization proposals per hotspot (Tournament pattern)
const proposals = await parallel(
hotspots.map(h => () =>
agent(`Propose 3 optimization approaches for ${h.location}`, {
phase: 'Propose',
schema: PROPOSAL_SCHEMA,
})
)
)
// RANK_SCHEMA: defined as { score, pros, cons }
// Independently evaluate each proposal and select the highest-ranked
const ranked = await parallel(
proposals.flat().map(p => () =>
agent(`Evaluate the expected impact and risk level of this optimization approach: ${p.description}`, {
phase: 'Rank',
schema: RANK_SCHEMA,
})
)
)
return proposals.flat()
.map((p, i) => ({ ...p, score: ranked[i].score }))
.sort((a, b) => b.score - a.score)
.slice(0, 5) // Top 5 recommendationsPros and Cons
Honestly, the first time I used this feature I only looked at the benefits, threw a large task at it without thinking, and was shocked by the token bill. Before you read the table below, I strongly recommend reading the cost item in the cons section first.
Pros
| Item | Details |
|---|---|
| Scalability | Up to 1,000 agents in a single session, up to 16 concurrent |
| Context independence | Orchestration logic preserved in script variables — no context window exhaustion |
| Resumability | All agent() calls are journaled, allowing interrupted runs to be resumed |
| Adaptive design | Supports complex control flow: conditional branching, loops, dynamic agent count decisions |
| Built-in verification | Adversarial verification pattern can filter out spurious results |
| Performance gains | Opus 4 lead agent + Sonnet 4 subagent configuration shows 90.2% performance improvement over a single Opus 4 (Anthropic internal evaluation) |
Cons and Caveats
The other items can be avoided by accounting for them at design time, but token costs are the one thing people often only learn after getting burned — which is why it's listed first.
| Item | Details | Mitigation |
|---|---|---|
| Token cost explosion | Reports of a single day's run consuming 20% of the weekly quota on the Max plan ($200/month) | Estimate agent count before running, test with a small scope first |
| Non-determinism prohibited | Date.now(), Math.random(), new Date() with no arguments cannot be used inside the script |
Replace with fixed values or external inputs |
| Concurrency limit | Maximum 16 concurrent agents, 1,000 total | Account for these limits when designing work batches |
| Inefficient for simple tasks | Overhead is wasteful for predictable, simple repetitive work | Replace with custom subagents or simple API calls |
Pre-Start Checklist
Operational items worth verifying in advance.
- Check your version: You can run
claude --versionto confirm you're on v2.1.154 or later. - Check your plan: Available on Max, Team, and Enterprise plans. If you're not on one of those plans, you can also use the Claude API directly with a key; access via Amazon Bedrock, Google Vertex AI, and Microsoft Foundry is also supported.
- Enterprise activation: In Enterprise environments, Dynamic Workflow is disabled by default. An administrator must explicitly enable it before it can be used.
- Research Preview caution: This is currently in research preview; the API and behavior may change before GA (general availability). It's safer to start applying it to internal tools and automation scripts rather than production-critical pipelines.
The Most Common Mistakes in Production
- Running without estimating agent count — Not calculating 100 files × 3 agents × 2 passes beforehand can result in token consumption 10× what you expected. Start with a dry run on 10 files to get a sense of actual consumption.
- Overusing
parallel()— Thinking "if it runs concurrently, it should all beparallel()" and using it everywhere creates unnecessary barriers that wait for full completion. If you can stream the results of the previous step directly to the next,pipeline()is more efficient. - Using
Math.random()inside the script — This invalidates the resume cache and prevents interrupted work from being picked up again. If you need a random seed, receive the value externally and pass it in as a fixed argument.
Closing Thoughts
Dynamic Workflow is not about "AI getting smarter" — it's an architectural approach that bypasses the context window limitation that was the bottleneck of existing AI, at the architecture level. If a single agent already handles your work well, there's no need to introduce this. But if you've been stuck on a "problem of scale" — analysis spanning thousands of files, or a large-scale migration — it's worth trying.
Three steps you can take right now:
- Check your version and plan. Run
claude --versionto confirm you're on v2.1.154 or later, and check whether you're on a Max, Team, or Enterprise plan. If not, you can also access it via a Claude API key. As a note, entering/effort ultracodein Claude Code activates Ultracode mode, after which Claude will automatically plan and execute a workflow for every substantial task you request. - Do a dry run on a small scope first. Run a security audit or refactoring task on one module or about 10 files in your actual project, then review the generated orchestration script and token consumption. This makes cost estimation much easier before scaling up to the full codebase.
- Writing one Adversarial verification pattern yourself accelerates understanding. This pattern is the most distinctive part of Dynamic Workflow. Building a structure where verification agents attempt to refute the results found by scan agents will give you a hands-on feel for the overall design philosophy of the system.
References
- Orchestrate subagents at scale with dynamic workflows | Claude Code official docs
- Introducing dynamic workflows in Claude Code | Anthropic blog
- Claude Code Adds Dynamic Workflows for Parallel Agent Coordination | InfoQ
- How we built our multi-agent research system | Anthropic Engineering
- Multiagent sessions | Claude API official docs
- Claude Code Dynamic Workflows: Orchestrating Agents at Scale | Context Studios
- Claude Code Dynamic Workflows Complete Guide | alexop.dev