Polyrepo to Monorepo Migration:
A Step-by-Step Playbook for 2026

The 8-step migration playbook, tool-specific notes, real migration stories from Proton and Cash App, and common failure modes. Year-stamped for 2026 tooling.

The 8-Step Playbook

01

Pick the tool first

Do not start importing repos before you know which build system you are targeting. Your tool choice determines how you structure the workspace, what CI commands look like, and how long the migration takes. JS/TS only: Nx or Turborepo. Polyglot: Bazel or Pants. See the tool comparison page.

Tool comparison
02

Inventory: list every repo

Spreadsheet with: repo name, owner, primary language, test framework, CI config location, deploy pipeline, last active commit, dependency graph (which repos import from which). Mark each as 'peripheral' (few dependents) or 'core' (many dependents). Peripheral repos join first.

03

Pick the join order: peripheral first, core last

Join peripheral repos (few dependents) before core repos (many dependents). Each join is an opportunity to test your CI pipeline. If something breaks, you want to break it on a low-risk repo. Core repos (your main API, your shared lib) join last when the process is proven.

04

Preserve git history

Do not throw away history. git subtree is the simplest approach. git filter-repo (or the older filter-branch) gives more control. tomono is a shell script that automates the merge. Lerna import supports package-level history (limited). Choose based on how much history matters: if your team does git blame daily, preserve it.

# Using git subtree (simplest)
git subtree add --prefix=packages/service-a \
  git@github.com:org/service-a.git main --squash

# Using tomono (full history, multi-repo)
npx tomono < repos.txt
# repos.txt format:
# git@github.com:org/repo-a.git  packages/repo-a
# git@github.com:org/repo-b.git  packages/repo-b
05

Build the unified CI pipeline before importing core repos

Get CI running on peripheral repos first. Prove that Nx affected or Turbo --filter works. Prove that remote caching works. Get team sign-off that CI is faster, not slower. Only then proceed to core repos. This prevents the scenario where the entire engineering team is blocked on a broken CI pipeline.

06

Cutover weekend: the Proton pattern

Proton's web engineering team moved their polyrepo to a monorepo in a single weekend cutover. The approach: freeze the old repos (read-only), run the migration scripts on Friday evening, validate on Saturday, unfreeze and switch branches on Sunday. This works when you have a team willing to own the weekend and a scope that is bounded (web stack, not 450 services).

Proton engineering blog
07

Two-week stabilisation

After cutover, enforce a no-new-features freeze for two weeks. Focus on: fixing CI flakiness, getting cache hit rates up, resolving import path issues, training engineers on the new workflow (nx affected, nx graph, turbo run). Resist the temptation to ship features while the foundation is unstable.

08

Decommission old repos

Mark old repos as read-only (GitHub: Settings > restrict pushes). Add a README.md with 'This repo has been migrated to [monorepo link]'. Keep them available for 90 days, then archive them. Do not delete immediately: engineers with local clones may still be working off old branches.

Real Migration Stories

Proton (web stack)

Saturday cutover. 1-2 weeks to stabilise.

Proton's web engineering team moved their polyrepo web codebase to a monorepo in a single weekend. Bounded scope (web only), JS/TypeScript stack, and thorough preparation made the weekend cutover possible.

proton.me/blog →

Cash App / Block

~450 JVM services. Multi-quarter. ~2 years to full win.

Block consolidated approximately 450 JVM services (Java/Kotlin) into a Bazel-based monorepo. The cutover was multi-quarter. The full productivity win took approximately two years. This is the honest benchmark for large-scale JVM monorepo migration.

engineering.block.xyz →

Common Failure Modes

Lost git history

Fix: Use git subtree or git filter-repo. Test history preservation on one repo before running at scale.

Broken CI for 2+ weeks post-cutover

Fix: Build and validate the unified CI pipeline on peripheral repos before cutting over core repos.

Unowned services in the merge

Fix: The inventory step (step 02) must include an owner for every repo. No owner = don't migrate it yet.

Circular dependencies discovered mid-migration

Fix: Run nx graph or similar dep analysis before starting. Circular deps block the monorepo's affected algorithm.

Tool chosen after migration started

Fix: Tool choice comes first (step 01). Changing tools mid-migration is a full restart.

Migration cost data →Need to split instead? →Pick a tool →