Legacy Migrations Work Better as a Kill List

repomigrationinfrastructurecoursebuildereggheadinngestmuxnextjspostgresstrangler-fig

phase gates, idempotent scripts, and agent-readable runbooks map directly to joelclaw workload planning for retiring legacy services

The skillrecordings/migrate-egghead repo is not a polite migration plan. It’s a kill list for egghead-rails and egghead-next, with Coursebuilder as the system that survives.

That’s the clever bit. The repo doesn’t pretend a legacy migration is a neat rewrite. It names the systems, numbers the blast radius, and turns the work into phase gates: content first, users next, Stripe webhooks into Inngest, Sidekiq jobs into durable functions, then cutover. No 404s, minimal downtime, idempotent scripts, human gates. Boring in exactly the right way.

The more interesting move is the interim strangler path: egghead-next can bypass Rails for high-volume read paths and hit PostgreSQL directly while the full Coursebuilder migration continues. The repo’s agent docs call out that getLessonComments and getLesson account for 88.5% of GraphQL traffic, and UsersController#current accounts for 55% of REST traffic. That’s a nasty little migration lever: kill most of the old system’s traffic before the old system is fully gone.

Useful for joelclaw because this is the shape long-lived system work needs: an agent-readable repo, phase done criteria, canary deploy commands, production log queries, and explicit stop/go gates. Less grand rewrite manifesto, more executable murder board. 🐀

Key Ideas

  • Treat migration as a retirement campaign, not a rewrite: egghead-rails and egghead-next have explicit fates, while Coursebuilder becomes the canonical backend.
  • Put content before users when content has no user dependency: migrate Sanity and Rails course data first so auth, entitlement, and playback paths can be tested against real material.
  • Use phase gates with evidence: each phase has done criteria, reconciliation checks, and human approvals instead of vibes-based migration confidence.
  • Make scripts idempotent because migrations get rerun: the repo records duplicate-content failures, cleanup scripts, and checks that preserve native Coursebuilder posts.
  • Strangle hot paths directly: bypassing GraphQL and Rails for pure PostgreSQL reads can remove most legacy traffic before the final cutover.
  • Keep the agent interface sharp: AGENTS.md, tools/me.ts, deploy status commands, route-error queries, and project sync commands make the repo operable by agents instead of tribal-memory bullshit.