Contents
AI-assisted engineering
What I Learned Trying to Automate a Frontend Migration
A large frontend migration became an experiment in agent architecture. I went in curious, broke a few of my own assumptions, and learned that the useful part was not a bigger prompt — it was bounded work, explicit evidence, and independent review.
Adarsh Trivedi · August 1, 2025 · 12 min read
Takeaway: Give coding agents bounded problems, not custody of an entire migration. Keep requirements and evidence outside the conversation, trace data back to working code, and make verification a separate job.
Executive summary
I spent a few months experimenting with LLM coding agents on a large frontend migration. This is not a victory lap or a claim that I found a perfect system. It is a field note from a curious engineer who tried several workflows, broke many of them, and learned where agents helped — and where they confidently said “done” too early.
The work was a component migration across a legacy frontend, a new frontend architecture, a CMS change, and a shared design system. I have anonymized internal names here. Think of it as a legacy frontend, a new frontend, a design system, and a migration engine.
| Experiment | What I tried | What broke | What I kept |
|---|---|---|---|
| Six-phase pipeline | Research → context → plan → build → integrate → review | Context filled up before verification | The phase order |
| Architect/executor | One agent designs; smaller agents implement | The architect still ran out of context | Separate planning from implementation |
| ERD/spec-first | Exhaustive component document before coding | Intent did not identify exact utilities or data sources | Research artifacts |
| Lead agent + skills | One orchestrator with passes, skills, IDs, state files | The lead session accumulated too much context | Traceability and on-disk artifacts |
| Visual test agent | Agent compares screenshots and fixes | It accepted differences and skipped root-cause checks | Component screenshots + computed styles |
| Script-controlled loop | Shell script controls iterations; fresh agents do bounded work | Still needs prompt tuning | This became the most useful direction |
The actual problem
At first, I treated the migration as a code generation task. That was too simple.
A component migration usually required understanding what the legacy component did, identifying variants and fallbacks, deciding what belonged in the design system versus the application layer, recreating the data boundary in the new frontend, matching desktop and mobile output, and proving that the result did not just look plausible.
This is why a “convert this component” prompt was never enough. The code could compile and still use the wrong data source. The screenshot could look close and still use the wrong token. A migrated component could be structurally wrong even if the UI seemed fine.
The hardest part was not getting an agent to write code. It was getting the workflow to preserve judgment, evidence, and verification across a task that was too large for one conversation.
Early workflows: useful shape, weak boundaries
The first serious attempt was a clean pipeline:
Research → Context → Plan → Build → Integrate → Review
Each phase had its own instructions. The idea was reasonable: the researcher should not be writing code, and the builder should not be doing review. The phase structure matched how I would manually approach the work, but the same long-running session still carried too much history.
Next, I tried an architect/executor model. One agent would read everything and produce a detailed design; smaller workers would implement it without making architectural decisions. This improved some things, but the architect had to absorb so much source context that its own window became crowded before the design was complete.
Then I tried exhaustive research documents. The research artifact was genuinely helpful, but it still did not prevent incorrect code. “Fetch related content” describes an intention. It does not identify the existing utility, required filter, response shape, or fallback behavior in the destination frontend.
More orchestration did not automatically mean more reliability
The next version was more elaborate: a migration lead orchestrated passes, generated state files, and linked artifacts with stable IDs.
Lead agent
├─ extract intent
├─ define boundary
├─ design design-system component
├─ implement design-system work
├─ plan frontend integration
├─ implement frontend work
├─ run visual test
└─ review
This introduced good ideas: artifacts on disk, stable IDs for tracing requirements, explicit pass status, and specialized skills. But the lead agent still ran too much of the show in one conversation.
By late passes, it had seen the research, plans, implementation attempts, partial fixes, and tool output. The summaries it wrote were also too optimistic. A status file might say “design complete” while missing an unresolved variant. A later session would trust that label and move on.
The lesson for me was simple: state on disk is good. State written as an optimistic summary by an overloaded agent is not enough.
Visual testing needed evidence, not vibes
Visual testing looked like the obvious answer: compare old and new, fix, repeat. In practice, this exposed another set of problems.
The agent would sometimes fix one thing and stop without re-running the comparison, dismiss a mismatch as “expected,” compare full-page screenshots where local differences disappeared, or inspect screenshots when exact computed styles were needed.
The useful version became more mechanical:
1. Capture component-scoped screenshots
2. Extract computed styles and measurements
3. List all differences
4. Investigate root causes in source
5. Batch fixes
6. Rebuild once
7. Verify again
Screenshots were good for layout. Computed styles were better for exact values like color, font weight, dimensions, and spacing. “Looks close” was not enough when the difference was a wrong token or stale build.
The version that worked better
The version that worked better was simpler: a shell script controlled the loop, and agents did bounded work inside it.
Research phase
│
▼
Human gate
- confirm scope
- choose one variant
- provide reference screenshots
│
▼
Implementation loop
│
├─ fresh implementer session
├─ controlled rebuild / server restart
├─ fresh verifier session
└─ status decides next iteration
The script was not smarter than the agent. That was the point. It did not get tired, did not want to declare victory, and did not forget to run the next iteration because the previous one felt close.
Agents were still useful. They researched, implemented, and reviewed. But they no longer owned the loop. That distinction mattered.
How my workflow has evolved
My current setup has moved from a single all-purpose agent toward supervised, bounded subagents.
Main session
├─ scout: map files and risks
├─ context builder: collect conventions and patterns
├─ planner: write the implementation plan
├─ worker: implement one coherent slice
├─ reviewer: inspect the diff independently
└─ simplicity pass: remove unnecessary complexity
For UI work, browser checks are part of the loop. For code changes, I try to separate implementation from review. For over-engineering, I run a deliberately strict simplicity pass: what can be deleted, inlined, or deferred?
This is not magic. It still depends on good prompts, good evidence, and a human who is willing to stop when something is assumed. But it is less dependent on one long context remembering everything.
Rules I would keep
- Every important field needs evidence. If you cannot trace it, mark it unknown.
- Pick one variant before implementation. Let the agent identify variants, but make the target explicit.
- Keep composition boundaries explicit. Decide what belongs in the design system and what belongs in the application adapter.
- Do not let the implementer verify itself. Verification should be a separate job with fresh context and concrete checks.
- Use screenshots and measurements together. Screenshots catch layout; computed styles catch exact values.
- Automate boring infrastructure. Rebuilds, server restarts, required artifacts, and retry limits should not depend on memory.
The main lesson for me was not “agents are bad at migration.” They were useful. The lesson was that broad ownership is different from bounded help. Do not start by designing the perfect autonomous agent. Start by making wrong assumptions easier to catch.