Skip to content

ADR-05: Polyrepo Repository Strategy ​

한국어 버전

DateAuthorRepos
2024-12-17@KubrickCodeAll

Context ​

Problem Statement ​

Multi-service platforms require a fundamental decision on code organization strategy. This decision impacts:

  1. Development Workflow: How developers work across service boundaries
  2. Release Management: How services are versioned and deployed
  3. Team Collaboration: How ownership and responsibilities are defined
  4. Technical Evolution: How technology choices can evolve independently

Repository Strategy Options ​

StrategyDescriptionTypical Use Case
MonorepoAll services in a single repositoryTightly coupled services, same stack
PolyrepoEach service in its own repositoryIndependent services, diverse stacks
HybridCore in monorepo, specific components separatedGradual migration, mixed requirements

Key Decision Factors ​

  • Technology Stack Diversity: Different languages/frameworks across services
  • Release Independence: Need for independent deployment cycles
  • Team Structure: Ownership boundaries and access control requirements
  • Code Sharing Patterns: Frequency of shared code changes
  • Open Source Strategy: Plans to open-source specific components
  • Platform Distribution Requirements: Marketplace and registry constraints

Platform Distribution Constraints ​

Certain distribution platforms require or strongly prefer independent repositories:

Distribution TargetRepository RequirementMonorepo Limitation
GitHub Actionaction.yml must be at repository rootImpossible - cannot register on Marketplace
VSCode Extensionpackage.json at root, vsce packagingComplex workspace configuration required
Go Modulego.mod path becomes import pathSubmodule paths are awkward (repo/pkg)
npm PackageIndependent package managementPossible but requires workspace tooling
Docker HubDockerfile context at root preferredMulti-context builds add complexity

This constraint is particularly critical when planning ecosystem expansion (IDE extensions, CI integrations, CLI tools).

Decision ​

Adopt Polyrepo strategy with repositories separated by deployment unit and technology boundary.

Repository separation criteria:

  • Deployment Unit: Services that deploy independently get separate repositories
  • Technology Boundary: Different primary languages/frameworks warrant separation
  • Ownership Boundary: Clear team ownership maps to repository boundary

Options Considered ​

Option A: Polyrepo Strategy (Selected) ​

Each service maintains its own repository with independent lifecycle.

Pros:

  • Independent Release Cycles: Deploy any service without coordinating with others
  • Clear Ownership: Repository boundary equals ownership boundary
  • Technology Freedom: Each service can use optimal stack without constraints
  • Granular Access Control: Repository-level permissions for sensitive code
  • Open Source Enablement: Open-source specific components without exposing entire codebase
  • Focused CI/CD: Faster builds, only affected service tested
  • Simplified Dependencies: No build system complexity for cross-language support

Cons:

  • Cross-Repository Changes: Multi-repo PRs require coordination
  • Dependency Synchronization: Library versions must be tracked across repos
  • Code Duplication Risk: Common utilities may be duplicated
  • Development Setup: Local environment requires multiple repository clones

Option B: Monorepo Strategy ​

All services in a single repository with shared tooling.

Pros:

  • Atomic Changes: Single PR can modify multiple services
  • Code Reuse: Easy sharing of common code and utilities
  • Consistent Tooling: Unified build system and CI/CD
  • Simplified Refactoring: Large-scale changes easier to execute
  • Single Source of Truth: All code in one place

Cons:

  • Build System Complexity: Multi-language support requires sophisticated tooling (Bazel, Nx)
  • Scaling Challenges: Repository size impacts clone/checkout time
  • Coupled Releases: Changes may trigger unnecessary rebuilds
  • Access Control Limitations: Fine-grained permissions are complex
  • Technology Lock-in: Pressure to standardize on single stack
  • Platform Distribution Blockers: GitHub Actions cannot be registered on Marketplace; VSCode extensions require complex setup

Option C: Hybrid Approach ​

Core services in monorepo, specific components (libraries, infra) in separate repos.

Pros:

  • Best of Both: Combine monorepo benefits for related services
  • Selective Separation: Isolate components with special requirements

Cons:

  • Dual Complexity: Must maintain both patterns and tooling
  • Boundary Decisions: Unclear criteria for what belongs where
  • Migration Friction: Moving code between mono and poly sections is complex

Consequences ​

Positive ​

  1. Independent Release Cycles

    • Services deploy on their own schedule
    • No release coordination overhead
    • Rollback scope limited to single service
  2. Clear Ownership Boundaries

    • Repository ownership = service ownership
    • Code review scope well-defined
    • Accountability clearly established
  3. Technology Flexibility

    • Each service uses optimal technology
    • Framework/library upgrades don't cascade
    • Build systems optimized per-language
  4. Access Control

    • Sensitive code (infrastructure, secrets) isolated
    • Contributor permissions per-repository
    • Audit trails separated
  5. Open Source Capability

    • Library components publishable independently
    • Separate licensing possible
    • Community contributions scoped
  6. Ecosystem Distribution

    • GitHub Actions registrable on Marketplace (requires root action.yml)
    • VSCode extensions publishable without complex workspace setup
    • Each tool/extension has clean repository identity

Negative ​

  1. Cross-Repository Changes

    • Multi-service features require coordinated PRs
    • API contract changes need careful sequencing
    • Mitigation: Stable API contracts, versioning strategy, feature flags
  2. Dependency Synchronization

    • Shared library updates across repositories
    • Breaking changes must be communicated
    • Mitigation: Automated dependency updates (Renovate/Dependabot), semantic versioning
  3. Code Duplication

    • Utility code may be duplicated
    • Common patterns reimplemented
    • Mitigation: Extract shared code to dedicated library repository
  4. Development Environment

    • Multiple repositories to clone and configure
    • E2E testing requires service orchestration
    • Mitigation: Docker Compose for local development, documentation

Technical Implications ​

AspectImplication
Dependency MgmtGo modules for Go services, npm for TypeScript
Version StrategySemVer for libraries, independent versioning for services
CI/CDPer-repository pipelines, integration tests in dedicated environment
Code SharingPublish shared libraries as packages (Go modules, npm packages)
Local DevelopmentDocker Compose or development scripts for multi-service setup

When to Reconsider ​

  • Cross-repository changes become majority of development work
  • Team consolidation makes shared ownership more practical
  • Technology stack converges to single language/framework
  • Monorepo tooling (Bazel, Nx) becomes compelling for the scale

Open-source test coverage insights