Overview of 992: Migrating Legacy Code Just Got Easier
Wes Bos (with Scott Tolinski) walks through his recent migration of a long-running course platform off Express and Pug to a web-standards-based stack (Hono + JSX/TSX templates). He explains the planning, patterns, AI-assisted code transforms, testing strategy, and monitoring that made the migration feasible and relatively low-risk. The episode mixes practical migration steps, tooling recommendations, and lessons learned.
Key points / main takeaways
- Migrations should be lateral moves (feature-for-feature parity) — don’t try to add big new features during the migration.
- Establish consistent patterns first (so AI and automation can work reliably).
- Separate framework-specific API usage from business logic (extract request data early).
- Use small adapters/converters to bridge old and new frameworks while migrating incrementally.
- Use LLMs/AI to plan, scaffold, and even perform code conversions — but combine that with heavy testing and monitoring.
- Templating changes (Pug → JSX/TSX) are often the hardest part; business logic tends to translate more easily.
- Sentry (or equivalent) is essential to detect issues quickly after deployment.
The migration approach (step-by-step)
- Choose the target stack carefully
- Pick something similar enough to the old stack to allow a gradual, low-risk migration (Wes chose Hono for its web-standards API and similarity to Express).
- Prepare the codebase / establish patterns
- Move to web-standard patterns wherever possible (e.g., use fetch-compatible semantics, SyncLocalStorage, standard form handling).
- Avoid putting framework-specific calls deep in business logic; extract parameters and put them in simple variables early.
- Prototype/adapt
- Convert a few routes manually to learn the target patterns.
- Create adapters: Wes wrote a middleware that converted Express requests into standard web Request objects so existing code could be incrementally shifted.
- Built a JSX/TSX template renderer for Express so templates could be converted gradually from Pug to JSX.
- Plan with AI
- Use an LLM to scan the codebase and produce a migration plan and a detailed manual testing checklist (Wes got a ~150-item checklist).
- Run automated conversions where appropriate
- Let AI run code transforms (Wes reported a code transform phase that ran several hours and converted many endpoints).
- Test extensively
- Unit tests + integration tests + manual checklist verification of every feature/edge case.
- Deploy and monitor
- Merge, deploy, and use error monitoring (Sentry) to catch real-world issues quickly.
- Iterate and remove adapters
- Fix issues from monitoring, then once fully migrated delete the converter/middleware adaptors.
Tools, libraries, and techniques used
- Target framework: Hono (modern, web-standards-based routing)
- Source framework: Express.js
- Template transition: Pug → custom JSX/TSX renderer (used React-like JSX server rendering)
- Runtime pattern: SyncLocalStorage to replace Express request.locals-style hot-potato middleware
- Adapter: Middleware to convert Express Request -> standard web Request
- AI: LLMs to scan codebase, generate plan and test checklist, and perform code rewrites
- Monitoring: Sentry (sentry.io/syntax) to quickly surface post-deploy errors
- Other mentioned tools/picks: Whisper Flow (voice dictation), Display Placer (macOS display layout CLI), Phases.fm (shameless plug — podcast by Wes’s wife)
Testing & rollout strategy
- Create a comprehensive manual checklist covering every endpoint, template, and edge-case (Wes got ~150 checks from the AI).
- Run unit and integration tests, but rely heavily on manual walkthroughs for UI and templating edge cases.
- Deploy when comfortable; be ready to patch quickly using monitoring alerts.
- Expect a handful of production issues (Wes had ~20–30 small fixes after deploy, many template or progress-tracking related).
- Keep previous route signatures identical where necessary (e.g., progress-tracking API endpoints) to avoid breaking clients.
Pitfalls encountered and how they were handled
- Templating edge cases: converting Pug to JSX required refactoring large partials into reusable components and fixing many rendering issues.
- Live users mid-deploy: users watching videos triggered progress API calls to mixed versions — Sentry helped identify and fix these quickly.
- Over-ambition risk: adding new features during migration was intentionally avoided to keep scope manageable and finish the migration.
Practical recommendations (for teams planning a similar migration)
- Do a lateral migration: match features and APIs as closely as possible.
- Establish migration-friendly patterns ahead of time (extract request data, use standard storage patterns).
- Build adapters to allow incremental migration (migrate routes piece-by-piece).
- Use AI to scan and plan, but don’t treat it as a full autopilot — pair it with careful manual review.
- Produce a detailed manual test checklist before running bulk conversions.
- Monitor with an error-tracking tool and be ready for quick fixes after deploy.
- Resist the temptation to refactor or introduce new features during migration; stabilize first.
Estimated timeline (based on Wes’s experience)
- LLM/code conversion run: a few hours (3–4h reported)
- Testing, planning, iterative fixes, and getting to deploy-ready: ~1–1.5 weeks (part-time work)
- Post-deploy fixes: first hours/days monitored and patched
Notable quotes / insights
- “If you try to migrate and start to put new features in, that’s just way too big of a move... Those types of projects never, ever get shipped.”
- “AI does really well when you have established patterns.”
- “The actual writing the code was almost nothing; the planning and the testing were the two things that took the longest.”
Actionable checklist (short)
- Pick a lateral target stack (feature parity).
- Audit codebase: list controllers, middleware, templates, auth, rate-limiting, CSRF, tokens, etc.
- Establish framework-agnostic patterns (extract params, use SyncLocalStorage or equivalents).
- Implement adapter(s) to let old and new routes coexist.
- Convert a few critical routes/templates manually as examples.
- Ask an LLM to generate a full migration plan + manual test checklist.
- Run automated conversions / refactors (with human review).
- Run manual checklist tests (150+ items if necessary).
- Deploy with monitoring (Sentry) and be ready to patch.
- Remove adapters after full migration.
Picks & plugs mentioned in the episode
- Whisper Flow — recommended voice-dictation app (more reliable than other dictation for Wes).
- Display Placer — CLI (and Raycast UI) to save and restore macOS display layouts.
- Phases.fm — Wes’s wife’s podcast on parenting and growth mindset (shameless plug).
- Sentry — recommended error monitoring (sentry.io/syntax).
This episode is especially useful if you’re planning a medium-to-large migration and want a pragmatic, AI-assisted approach that emphasizes planning, patterns, adapters, and monitoring rather than a big-bang rewrite.
