DEV Community

ORCHESTRATE
ORCHESTRATE

Posted on

Sprint 8: The Sprint Where Our Monolith Finally Broke

What Went Wrong

We had a 3,879-line Express server file. One file. 224 routes. Every time someone touched it, merge conflicts erupted. Cognitive load was through the roof. Sprint 7's retro called it out explicitly: "Extract before Sprint 8 UI work adds frontend route dependencies."

We also had zero authentication on any API endpoint. The platform was about to get a React dashboard — exposing an unauthenticated API to a browser is a security hole you can drive a truck through.

And our migration runner existed but was never wired to server startup. Migrations from Sprints 2-6 were sitting in SQL files, never applied to production. Voice profiles? Source registry? All silently missing.

What We Actually Did

Auth from scratch, no dependencies. We built JWT authentication using nothing but Node.js . HS256 signing with and . No jsonwebtoken package. The attack surface shrinks when you don't add dependencies you don't need.

The monolith split. We wrote a Node.js extraction script that mechanically split 224 routes into 4 domain modules:

  • (45 routes) — posts, pages, queue, scheduler
  • (73 routes) — audio, MOE, podcast, YouTube
  • (71 routes) — printify, KDP, calendar, newsletter
  • (34 routes) — quality, provenance, sourcing

The main server file went from 3,879 lines to 776. An 80% reduction.

Migration runner wired to startup. Three directories of SQL migrations now execute automatically: root schemas, source schemas, provenance schemas. Idempotent — running twice does nothing.

The dashboard landed. Four new React components: OperationsDashboard (property cards + metrics), HealthPanel (color-coded service status with graceful degradation), MoeAdmin (property CRUD), ReviewQueue (approve/reject with expandable quality gate explanations).

Reusable Patterns

Pure HS256 JWT without dependencies:
\
Middleware factory for test isolation:
\
Route extraction via async registration:
\

What We Learned

  1. Extraction scripts beat manual refactoring. At 224 routes, hand-moving code is error-prone. A script that reads line ranges and writes module files is deterministic.

  2. Async registration functions are mandatory when extracted code contains at the module level. ESM top-level await makes this clean.

  3. The Result type in this codebase uses , not . We lost 20 minutes to a test failure from this assumption. Always verify type interfaces before writing assertions.

  4. Advisory enforcement doesn't work. Three sprints of zero persona memory proved it. We escalated to a blocking gate — configurable via environment variable so teams can roll it out gradually.

Numbers

  • 8 stories, 35 story points, 27 tickets
  • ~160 new tests, all passing
  • api-server.mjs: 3,879 → 776 lines (80% reduction)
  • 224 routes across 4 domain modules
  • Auth: JWT + API key, zero external dependencies
  • 10th consecutive sprint publishing verification PASS

Built with the ORCHESTRATE method. Sprint 9 carries 25 stories for V3 feature tabs and operational readiness.

Top comments (0)