992: Migrating Legacy Code Just Got Easier

Summary of 992: Migrating Legacy Code Just Got Easier

by Wes Bos & Scott Tolinski - Full Stack JavaScript Web Developers

29mApril 1, 2026

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)

  1. 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).
  2. 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.
  3. 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.
  4. 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).
  5. Run automated conversions where appropriate
    • Let AI run code transforms (Wes reported a code transform phase that ran several hours and converted many endpoints).
  6. Test extensively
    • Unit tests + integration tests + manual checklist verification of every feature/edge case.
  7. Deploy and monitor
    • Merge, deploy, and use error monitoring (Sentry) to catch real-world issues quickly.
  8. 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.