Overview
A 3,000-line index.js is not a codebase, it's a hostage situation. This playbook uses Cursor's Composer with a plan.md file to extract routes into modules, introduce a services layer, and add zod-validated request schemas — while running tests between each phase.
How it works
- Author plan.md with target file structure, naming rules, and validation contract.
- Composer processes one route group per phase (auth, users, orders, admin).
- Between phases: typecheck + integration tests must pass before continuing.
- Each phase becomes a commit; the plan file is the north star and doc.
Benefits
- Refactors ship in days, not months.
- Plan.md is the review artifact reviewers actually engage with.
- Type safety introduced without stopping the world.
Use cases
- Legacy Express/Koa APIs with growing complexity.
- Consolidating microservices back into a modular monolith.
- Preparing a codebase for a new team to inherit.
Step-by-step guide
Step 1: Write plan.md
Target: src/routes/{group}/index.ts, src/services/{name}.ts. Every route validates input with zod. No inline SQL.
Step 2: Phase 1: skeleton
Create empty folders/files. Composer generates the barrel exports and app.ts wiring. Commit.
Step 3: Phase 2: extract routes group by group
Auth first (highest risk). Move handlers into the new file, keep tests green, delete the old code. Commit per group.
Step 4: Phase 3: services layer
Extract business logic into src/services. Route handlers become 5-10 lines: validate → call service → shape response.
Step 5: Phase 4: zod validation
Add request schemas per route. Fail fast on invalid input. Update tests to expect 400s for missing fields.
Step 6: Phase 5: enforce with lint
ESLint rule to ban route files > 80 lines and services with unvalidated input. Locks in the pattern.
Example
plan.md rule: 'Every route file must export default a router. Handlers must be < 15 lines and call at most one service method.'