Understanding Polyrepos
A polyrepo (or multi-repo) strategy involves storing each project or component in its own separate Git repository. This traditional approach offers autonomy and clear boundaries but requires coordination across repositories.
bash
# Polyrepo structure
github.com/myorg/
├── web-app/ # Frontend repository
├── api-service/ # Backend repository
├── mobile-app/ # Mobile repository
├── design-system/ # UI library repository
└── shared-utils/ # Utilities repositoryWhen to Use Polyrepos
Ideal Use Cases
bash
# 1. Unrelated projects
- Website
- Mobile app
- Internal tools
# 2. Different teams
- Frontend team owns web-app
- Backend team owns api-service
- Mobile team owns mobile-app
# 3. Independent deployment
- Frontend deploys to Vercel
- API deploys to AWS
- Mobile deploys to App Store
# 4. Different tech stacks
- React frontend
- Python backend
- Swift mobile appPolyrepo Advantages
Clear Boundaries
bash
# Each repo has:
✓ Clear ownership
✓ Independent versioning
✓ Separate CI/CD
✓ Focused codebase
✓ Faster git operationsTeam Autonomy
bash
# Frontend team
- Owns web-app repo
- Makes decisions independently
- Deploys without coordination
# Backend team
- Owns api-service repo
- Sets own standards
- Releases independentlyManaging Polyrepos
Repository Organization
bash
# GitHub organization structure
myorg/
├── web-frontend/ # React app
├── api-backend/ # Node.js API
├── mobile-ios/ # iOS app
├── mobile-android/ # Android app
├── design-system/ # Component library
├── shared-types/ # TypeScript types
└── docs/ # Documentation
# Use consistent naming
- Prefix with platform (web-, api-, mobile-)
- Clear purpose from name
- Avoid ambiguityCode Sharing Strategies
Published Packages
bash
# Create shared library
cd design-system/
npm init -y
# Build and publish
npm run build
npm publish
# Use in other repos
cd web-frontend/
npm install @myorg/design-systemGit Submodules
bash
# Add shared code as submodule
cd web-frontend/
git submodule add https://github.com/myorg/shared-utils.git lib/utils
# Update submodule
git submodule update --remotePrivate npm Registry
bash
# Set up private registry
npm set registry https://npm.myorg.com
# Publish private packages
npm publish --registry=https://npm.myorg.com
# Install from private registry
npm install @myorg/shared-utilsDependency Management
Version Pinning
json
// web-frontend/package.json
{
"dependencies": {
"@myorg/ui-components": "^2.1.0",
"@myorg/api-client": "^1.5.2",
"@myorg/types": "^3.0.0"
}
}
// Use semantic versioning
// ^2.1.0 - Compatible with 2.x.x
// ~2.1.0 - Compatible with 2.1.x
// 2.1.0 - Exact versionDependency Updates
bash
# Update dependencies regularly
npm update
# Check for outdated
npm outdated
# Use Renovate or Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"CI/CD Coordination
Independent Pipelines
yaml
# web-frontend/.github/workflows/deploy.yml
name: Deploy Frontend
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm test
- run: npm run build
- run: npx vercel deploy --prodOrchestration
yaml
# orchestration-repo/.github/workflows/deploy-all.yml
name: Deploy All Services
on:
workflow_dispatch:
jobs:
deploy-frontend:
uses: myorg/web-frontend/.github/workflows/deploy.yml@main
deploy-backend:
uses: myorg/api-backend/.github/workflows/deploy.yml@main
deploy-mobile:
uses: myorg/mobile-ios/.github/workflows/deploy.yml@mainCommunication Between Services
API Contracts
typescript
// OpenAPI specification
// api-backend/openapi.yaml
openapi: 3.0.0
info:
title: My API
version: 1.0.0
paths:
/users:
get:
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: stringShared Types
bash
# Publish types from shared-types repo
cd shared-types/
npm run build
npm publish
# Use in other repos
cd web-frontend/
npm install @myorg/types
# Now types are in sync
import { User } from '@myorg/types';Common Challenges
Synchronization Issues
bash
# Problem: API changes break frontend
# Backend updates user object
# Frontend still using old fields
# Solution: API versioning
GET /api/v1/users
GET /api/v2/users
# Or use contract testing
Pact for consumer-driven contractsCode Duplication
bash
# Problem: Same utility in multiple repos
# web-frontend/src/utils/format.ts
# api-backend/src/utils/format.ts
# Solution: Extract to shared library
cd shared-utils/
cp ../web-frontend/src/utils/format.ts ./src/
npm publish
# Use in both repos
npm install @myorg/utilsVersion Conflicts
bash
# Problem: Different repos use different versions
# web-frontend uses react@18
# mobile-frontend uses react@17
# Solution: Standardize versions
# Use org-wide eslint rule
// .eslintrc.js
module.exports = {
rules: {
'no-restricted-imports': ['error', {
patterns: [{
group: ['react'],
message: 'Use react@18 only'
}]
}]
}
};Best Practices
1. Clear Ownership
markdown
# Each repo README
## Ownership
- Maintainer: Frontend Team
- Contact: frontend-team@myorg.com
- On-call: @frontend-lead
## Support
- Issues: GitHub Issues
- Slack: #frontend-support2. Documentation
markdown
# API documentation
# api-backend/README.md
## API Endpoints
### GET /users
Get all users
### POST /users
Create new user
See [docs.myorg.com/api](https://docs.myorg.com/api)3. Consistent Standards
bash
# Shared linting config
cd eslint-config-myorg/
npm publish
# Use in all repos
npm install --save-dev eslint-config-myorg
# .eslintrc.js
module.exports = {
extends: ['myorg']
};4. Automate Cross-Repo Operations
bash
# Script to update all repos
#!/bin/bash
repos=("web-frontend" "api-backend" "mobile-ios")
for repo in "\${repos[@]}"; do
cd ../\$repo
git pull origin main
npm update
npm test
doneTooling for Polyrepos
Multi-Repo Management
bash
# Google's Repo tool
# Manage multiple git repos
# .repo/manifest.xml
<manifest>
<remote name="github" fetch="https://github.com/" />
<project name="myorg/web-frontend" path="web" />
<project name="myorg/api-backend" path="api" />
</manifest>
# Sync all repos
repo syncGitHub Organization
bash
# Use GitHub features
- Teams for access control
- CODEOWNERS for approvals
- Protected branches
- Required checks
- Dependabot for updatesMonitoring Polyrepos
Centralized Monitoring
yaml
# observability-repo/docker-compose.yml
services:
prometheus:
image: prom/prometheus
grafana:
image: grafana/grafana
loki:
image: grafana/loki
# All repos send metrics hereDistributed Tracing
typescript
// Add tracing to all services
import * as tracing from '@opentelemetry/api';
// web-frontend/src/telemetry.js
const tracer = tracing.trace.getTracer('web-frontend');
// api-backend/src/telemetry.js
const tracer = tracing.trace.getTracer('api-backend');
// Trace requests across servicesMigration: Monorepo to Polyrepo
Extract Repository
bash
# Split monorepo into separate repos
# Extract frontend
git subtree push --prefix=apps/web origin web-frontend
# Extract backend
git subtree push --prefix=apps/api origin api-backend
# Now in separate repos
cd web-frontend/
git remote set-url origin git@github.com:myorg/web-frontend.gitWhen Polyrepos Shine
Large Organizations
bash
# Google, Meta, Microsoft
- Thousands of repositories
- Independent teams
- Clear ownership
- Scalable approachOpen Source Projects
bash
# Separate repos for:
- Core library
- Plugins
- Documentation
- Examples
- CLI toolsDifferent Tech Stacks
bash
# Frontend: JavaScript/TypeScript
# Backend: Go/Rust
# Mobile: Swift/Kotlin
# ML: Python
# Each repo optimized for its stackComparison
Monorepo vs Polyrepo Decision Matrix
bash
# Use Monorepo when:
✓ Projects are closely related
✓ Frequent cross-project changes
✓ Shared dependencies
✓ Single team or small org
✓ Unified tooling
# Use Polyrepo when:
✓ Projects are independent
✓ Different teams/tech stacks
✓ Separate deployment cycles
✓ Large organization
✓ Clear boundaries neededConclusion
Polyrepos offer autonomy, clear boundaries, and independent deployment at the cost of coordination overhead. They're ideal for large organizations with multiple teams and diverse tech stacks.
Key takeaways:
- Use polyrepos for independent projects
- Establish clear ownership
- Automate cross-repo operations
- Share code via published packages
- Maintain consistent standards
Choose polyrepos when autonomy and clear boundaries matter most!