ADR 003: Hybrid Monorepo + Polyrepo Strategy

Status: Accepted Date: 2025-03-15 Last Updated: 2026-02-26

Context

We have shared libraries (design system, types, configs) and 5-10 independently deployable micro frontends maintained by 3-5 teams. We need a repository strategy that balances code sharing with team autonomy.

Decision

Hybrid approach: a monorepo (platform-core) for shared packages (design system, types, utils, configs) using pnpm 10.30.3 workspaces + Turborepo, and separate polyrepos for each micro frontend. Shared packages target React ^19.2.4.

Hybrid Mono + Polyrepo Decision Card Hybrid Mono + Polyrepo Positive Atomic shared changes Team autonomy Separate access control Changesets → Renovate Negative Cross-repo latency Need Verdaccio/yalc Two repos open

Consequences

Positive

  • Shared packages benefit from atomic changes (update design system + types in one PR)
  • MFE teams have full autonomy: own CI/CD, own branch strategy, own release cadence
  • Separate access control per MFE repo
  • Monorepo changes propagated via Changesets → npm publish → Renovate
  • No need to rebuild all MFEs when shared lib changes (only affected ones via Renovate PRs)
  • Simpler CI for each MFE (only builds one MFE, not the whole platform)

Negative

  • Cross-repo dependency updates have latency (publish → Renovate → merge)
  • Testing integration across repos requires Verdaccio or yalc
  • Developers working on both shared lib and MFE need two repos open
  • Renovate/Dependabot management across multiple repos
  • Shared package versioning requires discipline (Changesets helps)

Shared Package Deprecation Guidance

When deprecating a shared package from the platform-core monorepo, follow this process:

  1. Deprecation Notice: Add a ## Deprecated entry at the top of the package's CHANGELOG with the deprecation date, reason, and recommended replacement (if any).
  2. Migration Guide: Publish a migration guide in the package's README or a dedicated MIGRATION.md file that explains step-by-step how consuming MFEs should migrate away from the deprecated package.
  3. Minimum Support Window: Maintain the deprecated package for a minimum of 90 days (3 release cycles) after the deprecation notice. During this window, critical security patches must still be applied.
  4. Renovate Notification: Configure Renovate to add a deprecation warning label to PRs that still depend on the deprecated package.
  5. Removal Procedure: After the support window expires and all consuming MFEs have migrated (verified via npm download stats and Renovate dashboards), remove the package from the monorepo workspace and publish a final version with a postinstall script that prints a deprecation warning.

Alternatives Considered

  • Full monorepo (all MFEs + shared libs): Simpler dependency management, atomic cross-cutting changes. But: huge CI times, all teams in one repo creates merge conflicts, access control is coarse-grained, can't have independent deploy pipelines easily. Doesn't scale well past 3 teams.
  • Full polyrepo (everything separate): Maximum team autonomy. But: shared package versioning nightmare, no atomic changes across shared libs, config drift across repos, duplicate tooling setup. Cross-cutting refactors require coordinated PRs across 10+ repos.
  • Git submodules: Technical solution for sharing code across repos. But: complex workflow, merge conflicts in submodule references, poor developer experience, most teams dislike working with submodules.