Skip to main content
Don’t want to set this up manually? Paste a link to this page into a Devin session and ask it to set everything up for you.
1

Show Devin the monolith

You know the file — one Express router that grew for eighteen months. Every endpoint for every domain lives in src/routes/index.ts: user registration next to payment webhooks next to product search. Inline auth checks are copy-pasted across 40 handlers. Nobody wants to touch it because a change to the orders logic might break the user endpoints three hundred lines above.Here’s what the top of the file typically looks like:
src/routes/index.ts (before — 2,000 lines)
Tell Devin exactly what you want the target structure to look like.
2

Guide Devin with conventions

Devin reads your codebase to infer patterns, but refactoring is where Knowledge entries pay off the most. Add entries for conventions Devin should follow:
  • Router patterns — “Each domain router uses Router() and is mounted with app.use('/domain', domainRouter) in the root”
  • Middleware — “Auth middleware lives in src/middleware/ and is always imported, never defined inline”
  • Error handling — “All route handlers use our asyncHandler wrapper from src/lib/asyncHandler.ts — never raw try/catch”
Pointing Devin at an already well-structured router in your codebase often produces better results than describing conventions from scratch. Add a line like “Follow the pattern in src/routes/admin.ts, which is already cleanly separated” to your prompt.You can also ask Devin to generate Knowledge entries for you — just describe your conventions and it will create well-structured entries you can review and save.
3

Review Devin's PR

Devin maps every endpoint, traces the import graph, extracts shared logic, creates the domain files, rewires the root router, and runs your test suite. Here’s what the PR typically looks like:
Here’s what the clean root router looks like after the split:
src/routes/index.ts (after — 15 lines)
And a domain route file with the shared middleware properly imported:
src/routes/orders.ts (after — excerpt)
Every URL path stays identical — /orders is now handled by ordersRouter mounted at /orders, so existing clients and tests work without changes.
4

(Optional) Check out the branch and test locally

For a structural refactor like this, it’s worth pulling the branch and verifying locally before merging. Check it out in Devin Desktop or your preferred IDE, boot the app, and hit a few endpoints to confirm routing, middleware, and error handling all behave the same as before.
If anything looks off, leave a comment on the PR — Devin will pick it up and push a fix.
5

Continue the cleanup

Once the router is split, use follow-up prompts to extend the refactoring: