Back office ops · Production

Block migrates go/console from Base Web to Fluent UI without a flag day using an agentic migration system

The problem

Block's internal platform go/console had 11k tracked files mostly tied to Base Web, which was scaling back maintenance. With 40 to 60 engineers landing changes daily and no acceptable freeze window or downtime, a standard migration cutover was impossible.

First attempt

Single-shot prompting produced code that was syntactically valid but semantically wrong — the model would generate React-incorrect patterns like creating makeStyles hook factories inside the component body. Attempting to fix output quality with global hint libraries consumed 25% of the 256k context window before the model even saw the target file.

Workflow diagram · grounded in source
1
File selected for migration
Trigger
The TypeScript language server answers which file to migrate next and what references and related files matter.
source quote
“what file should we migrate next? - what does this symbol resolve to? - what references and related files matter?”
2
AST-based selective hint injection
Ai action
The TypeScript AST inspects the file's import statements and injects only the component hints relevant to that file.
source quote
“We used the TypeScript AST to inspect the file's import statements and injected only the hints that mattered for that file. If a file imported Button and Tag, it got the Button and Tag hints.”
3
Model migrates file in tool-backed loop
Ai action
A model migrates each file inside a tool-backed loop, calling back into the TypeScript server's tools.
source quote
“migrate a file inside a tool-backed loop”
4
Import rule validation
Validation
DSL-defined import rules check whether each Base Web import has been correctly replaced, returning specific steering messages when rules are violated.
source quote
“this import cannot be removed yet - this Base Web import must be replaced with that Fluent import - these new imports are not allowed - this required binding exists but is still unused”
5
Custom linter enforcement
Validation
More than 20 custom linters enforce component-level, framework-level, and behavioral correctness rules accumulated from repeated model failures.
source quote
“more than 20 custom linters. That was not prompt bloat for its own sake. It was accumulated operational knowledge from repeated failures.”
6
TypeScript language server diagnostics
Validation
The model edits a file and receives near-real-time feedback from the TypeScript language server against the relevant slice of the codebase.
source quote
“the model could edit a file and get near-real-time feedback from the TypeScript language server against the relevant slice of the codebase”
7
Warm Jest test execution
Validation
A warm Jest process with an RPC layer runs only the tests related to the migrated file.
source quote
“Starting Jest for even one file in a large monorepo can take 10 to 30 seconds, and that delay becomes expensive when a model keeps running tests after each change. To reduce the startup cost, we kept a Jest process …”
8
Temporary Fluent migration lane
Routing
A temporary Fluent lane inside the repo lets new surfaces migrate gradually while old ones keep working.
source quote
“we created a temporary Fluent lane inside the repo. New surfaces could move onto that lane gradually while old ones kept working.”
Reported outcome

The migration succeeded: more than 80 distinct targets were migrated to Fluent, Base Web was fully removed in February 2026, and the entire effort across 451 days was driven primarily by one IC, producing 1,004 commits and 14,713 file changes.

Reported metrics
Tracked files in repo11k tracked files
TypeScript and TSX files10k
Engineers landing changes daily40 to 60
Context window consumed by hint library25%
Show all 19 reported metrics
tracked files in repo11k tracked files
TypeScript and TSX files10k
engineers landing changes daily40 to 60
context window consumed by hint library25%
module-hint filesmore than 50
custom lintersmore than 20
distinct targets migrated to Fluentmore than 80
pre-work duration210 days
pre-work commits809
pre-work file changes4,912
migration phase duration240 days
migration-related commits195
migration phase file changes9,801
total project duration451 days
total commits1,004
total file changes14,713
migration team sizedriven primarily by one IC
full project typecheck durationaround four minutes
Jest startup time per file10 to 30 seconds
Reported stack
TypeScript language serviceGooseANTLRJest
◆ Does this fit your context?

Compare to your context

Tell us your scale, team, and constraints. We'll show what changes at your size, what fails at your scale, and whether this case is a fit, needs adaptation, or won't scale to you. Free demo, no signup.

Compare to your context →
~30 seconds · free
Source
https://engineering.block.xyz/blog/from-base-web-to-fluent-ui-without-a-flag-day
Read source ↗

Frequently asked questions

What did this team achieve with this AI workflow?

The migration succeeded: more than 80 distinct targets were migrated to Fluent, Base Web was fully removed in February 2026, and the entire effort across 451 days was driven primarily by one IC, producing 1,004 commit…

What tools did this team use?

TypeScript language service, Goose, ANTLR, Jest.

What results were reported?

Tracked files in repo: 11k tracked files; TypeScript and TSX files: 10k; Engineers landing changes daily: 40 to 60; Context window consumed by hint library: 25% (source-reported, not independently verified).

What failed first in this deployment?

Single-shot prompting produced code that was syntactically valid but semantically wrong — the model would generate React-incorrect patterns like creating makeStyles hook factories inside the component body.

How is this back office ops AI workflow structured?

File selected for migration → AST-based selective hint injection → Model migrates file in tool-backed loop → Import rule validation → Custom linter enforcement → TypeScript language server diagnostics → Warm Jest test execution → Temporary Fluent migration lane.

WHAT TO DO WITH THIS

Now compare it to your context

This case is one data point. Whether its pattern fits you depends on your volumes, your stack, and your exception load — that comparison is the step no case study can do for you.