Migrating to a Monorepo Without Coupling Deploys
We merged our API and platform UI into one repo last week. The goal wasn’t fashion; it was to remove two‑repo choreography without losing independent deploys.
The rule was simple: if the API and UI ever had to ship together, we wouldn’t do the move.
The constraint that mattered
- API deploys must stay independent.
- Platform deploys must stay independent.
- CI must stay fast enough that people don’t avoid it.
- Evidence has to fall out of normal work (PRs, CI runs, deploy logs).
If any of those were at risk, the migration was a “no.”
The layout we chose (and why it’s boring)
We used:
apps/apiapps/platform
It’s intentionally dull. Dull makes path filters easy and keeps the service boundary obvious.
Path-scoped CI is the hinge
Separate workflows, separate triggers:
- API CI runs on
apps/api/**plus shared paths. - Platform CI runs on
apps/platform/**plus shared paths.
That cut the “build everything on every change” habit without weakening checks.
Separate images, separate deploy buttons
Each app publishes its own image tag:
ghcr.io/sam-dawnops/dawnops-api:sha-<sha>ghcr.io/sam-dawnops/dawnops-platform:sha-<sha>
Deploys take a specific tag as input. That keeps releases explicit and audit-friendly. It also means the UI can stay pinned while the API moves, or vice versa.
How we migrated without losing history
- Created the new repo.
- Imported each repo with
git subtreeso history came along. - Added the path-scoped workflows.
- Updated
dawnops-metato check out the monorepo paths. - Marked the old repos read-only with a pointer to the new home.
Small gotchas we hit
- Shared files don’t trigger path filters unless you add them.
- Secrets have to move with the workflows or deploys quietly fail.
- Platform production isn’t Cloudflare Pages. Keep that boundary obvious.
One rule that kept us honest
Every deploy takes a specific image tag or SHA. No “latest,” no hidden coupling.
If I had to do it again
Start from the deploy boundary, not the repo structure. Once the deploy boundary is protected, the rest is mechanics.