Chapter 25
Architecting a Multi-Team SPA
Architecting a multi-team React SPA — monorepo vs polyrepo at the organisational level, shared design systems as packages, and versioning strategies for shared code.
Published 2026-05-23
🎯 Chapter Goal — After this chapter you can decide between monorepo and polyrepo at the organisational level (the technical answer is Ch 19), structure a shared design system as a package, and pick a versioning strategy for shared code that survives multi-team usage.
🧭 Prerequisites — Ch 11 (component library), Ch 19 (monorepo mechanics).
🔹 25.1 Monorepo vs polyrepo — the organisational decision
Ch 19 covered the technical monorepo. This chapter is about whether your organisation should adopt it. Different question.
Monorepo pays organisationally when:
- Multiple teams ship into the same product surface.
- Cross-team refactors are frequent and need to land atomically.
- Code review can span teams without political friction.
- You have (or can build) a platform team to maintain the monorepo tooling.
Monorepo struggles organisationally when:
- Teams have different release cadences and can’t unify.
- Compliance / security boundaries require code isolation.
- Team ownership is unclear → “everyone touches everything” pathology.
- No platform team → toolchain rot.
The polyrepo answer is right when:
- Teams are autonomous units with separate release calendars.
- Shared code is small and rare (a couple of libraries).
- Versioned interfaces are how teams already coordinate.
Most organisations of >30 frontend engineers benefit from a monorepo for the SPA. Most organisations of <10 engineers don’t need one.
CODEOWNERS — the single most important file
# .github/CODEOWNERS
/apps/web/ @acme/web-team
/apps/admin/ @acme/admin-team
/packages/ui/ @acme/design-system
/packages/api-client/ @acme/platform
/tooling/ @acme/platform
/turbo.json @acme/platform
Without CODEOWNERS, the “everyone touches everything” pathology kills the monorepo within a year. With it, ownership is loud and reviews land with the right people.
🔹 25.2 Shared design system as a package
Ch 11 is how to build the package. This section is where it lives organisationally.
Three patterns:
Inside the monorepo (most common)
packages/ui/ consumed by every app via the workspace protocol (Ch 19). Owned by a design-system team. Released internally on every PR; published externally only if there are external consumers.
Pros: instant feedback loop; atomic refactors across consumers; single source of truth. Cons: the DS team must serve every consumer; sometimes friction over breaking changes.
Separate repo, published library (less common in 2026)
A standalone @acme/ui repo. Apps consume via npm. Releases are versioned, documented, formal.
Pros: clear API contract; consumers control upgrade timing; works when consumers are outside the org. Cons: slow refactor cycle; consumers stuck on old versions; coordination cost.
Federated (rare, advanced)
The DS team ships its components via Module Federation (Ch 26). Apps consume at runtime, not build time. Updates roll out without app rebuilds.
Pros: instant rollout of fixes; one runtime version everywhere. Cons: complexity is significant; small bugs become big incidents (the DS is now infrastructure).
Default recommendation: in-monorepo for new organisations; separate published library if you have external consumers; federated only when you’ve outgrown both.
🔹 25.3 Versioning strategies for shared code
Three viable strategies:
Fixed versioning
Every package in the design system bumps together. @acme/ui@1.5.0, @acme/icons@1.5.0, @acme/tokens@1.5.0 — all on the same version. Consumers can be confident about compatibility.
Pros: simple; no peer-dependency math. Cons: “I only changed an icon” still bumps everything; signal-to-noise on the CHANGELOG suffers.
Independent versioning
Each package versions on its own cadence. @acme/ui@2.0.0 can coexist with @acme/icons@1.4.7.
Pros: accurate semver per package; meaningful CHANGELOGs. Cons: peer-dependency math; consumers need to track multiple versions.
Linked versioning (Changesets’ middle ground)
Like fixed, but only the affected packages bump. If @acme/ui changes, @acme/icons doesn’t bump — but if both change, they get the same new version.
Pros: clean signal; predictable for consumers. Cons: subtle to explain; less common; tools vary in support.
Recommended default: independent for monorepos with >3 packages and varied maturity; linked for tightly-coupled design systems with 2–3 packages.
Caret vs tilde vs exact pins
| Range syntax | Example | Allows | Use when… |
|---|---|---|---|
Caret ^ | ^1.5.0 | 1.x.y, x ≥ 5 | Default for trusted-publisher libraries |
Tilde ~ | ~1.5.0 | 1.5.x | Conservative — only patch updates |
| Exact | 1.5.0 | 1.5.0 only | When the lib has burned you with minor breakage |
Workspace * | workspace:* | The local workspace | Internal monorepo packages (Ch 19) |
For published @acme/* packages, use ^ if your DS team follows strict semver, ~ otherwise. For workspace-internal packages, always workspace:*.
🪤 Common Pitfalls
- Monorepo without
CODEOWNERS→ ownership rot within a year. - Polyrepo with shared library that updates daily → consumers always 3 versions behind.
- Fixed versioning across many packages → noisy CHANGELOGs.
- Pinning internal deps to exact versions → manual bump on every release.
- No platform team for the monorepo → tooling becomes everyone’s part-time job and nobody’s full-time job.
✅ Recap
- The organisational decision is separate from the technical one.
- Monorepo for orgs > 30 frontend engineers with cross-team work; polyrepo otherwise.
CODEOWNERSis the single load-bearing file.- Design system inside the monorepo by default; published library if external consumers; federated only at scale.
- Independent versioning is the safe default for >3 packages.
🔗 Further Reading
- Vercel / Shopify engineering blogs — large monorepo case studies.
- GitHub —
CODEOWNERSreference. - Ch 19 (monorepo mechanics); Ch 11 (component library packaging).
In the book — not on the site
Each topic has an 🧠 Under-the-hood subsection — the algorithm, the data structures, what React DevTools surfaces, debugging recipes. Plus a 🧪 hands-on lab per chapter with a starter repo. Reserved for the book.