Code governance
In large monorepos, teams need clear ownership boundaries. Without governance, anyone can change critical packages, reviews get routed to the wrong people, and quality suffers. CODEOWNERS assigns clear team responsibility, while GitHub branch protection enforces review requirements before code ships.
Outcome
Set up code ownership rules and branch protection for team accountability and code quality.
Fast track
- Create CODEOWNERS file with team assignments
- Configure ESLint module boundaries
- Test boundary enforcement
- Verify PR review requirements
Hands-on exercise 9.3
1. Create CODEOWNERS file
Create .github/CODEOWNERS:
# TODO: Add ownership rules following this pattern:
# - default owner (root files)
# - packages ownership (UI team, config team, utils team)
# - apps ownership (web team, app team, docs team)
# - CI/CD ownership (devops team)
#
# Format: <file-pattern> <@github-username-or-team>Solution
# Default owner for all files
* @geniusgarage/leads
# Package ownership
/packages/ui/ @geniusgarage/frontend-team
/packages/typescript-config/ @geniusgarage/platform-team
/packages/eslint-config/ @geniusgarage/platform-team
/packages/utils/ @geniusgarage/platform-team
# App ownership
/apps/web/ @geniusgarage/marketing-team
/apps/snippet-manager/ @geniusgarage/product-team
/apps/docs/ @geniusgarage/docs-team
# Infrastructure
/.github/ @geniusgarage/devops-team
/turbo.json @geniusgarage/platform-team
/package.json @geniusgarage/platform-team2. Understand CODEOWNERS patterns
Pattern matching:
/apps/web/ # Exact directory
*.md # All markdown files
/packages/*/src/ # All package src directories
turbo.json # Specific file
Multiple owners:
/packages/ui/ @geniusgarage/frontend-team @geniusgarage/design-team
Both teams must approve changes!
Override patterns:
* @geniusgarage/leads
/packages/ui/ @geniusgarage/frontend-team
/packages/ui/src/button.tsx @geniusgarage/design-system-lead
More specific patterns override general ones.
3. Test CODEOWNERS
Create a PR that changes packages/ui/src/button.tsx:
export function Button({ children, variant = 'primary', onClick }: ButtonProps) {
const variants = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
success: 'bg-green-500 text-white hover:bg-green-600', // New variant
}
return (
<button onClick={onClick} className={`${variants[variant]} px-4 py-2 rounded`}>
{children}
</button>
)
}GitHub will:
- Auto-request review from
@geniusgarage/frontend-team - Block merge until team approves
- Show required reviewers on PR
CODEOWNERS in action!
While CODEOWNERS enforces team ownership, you may also want to enforce architectural boundaries (e.g., prevent apps from importing other apps).
Turborepo is developing a native boundary enforcement feature that will be more powerful and integrated than third-party ESLint plugins. This feature is currently in alpha and will provide first-class support for:
- Package-to-package import rules
- Public API enforcement
- Cross-app isolation
- Type-safe boundaries
For now, use TypeScript path aliases and clear documentation to guide developers on import patterns. Watch the Turborepo roadmap for updates on native boundary support.
GitHub branch protection
Combine CODEOWNERS with branch protection:
1. Enable branch protection rules
In GitHub repository settings:
- Settings → Branches → Add rule
- Branch name pattern:
main - Enable:
- ✅ Require a pull request before merging
- ✅ Require approvals (1)
- ✅ Require review from Code Owners
- ✅ Require status checks (CI)
- ✅ Require branches to be up to date
2. Test protection
Create PR changing packages/ui/:
- Auto-requested reviewer:
@geniusgarage/frontend-team - Status checks: CI must pass
- Merge blocked until review approval
No one can bypass team ownership!
Benefits
Before Governance:
- Anyone changes any package
- No review requirements
- Inconsistent code quality
- No clear team ownership
With Governance:
- CODEOWNERS enforces team reviews
- GitHub blocks merges without approval
- Required CI checks before merge
- Clear team ownership and accountability
For 10-person team:
- 50% fewer bugs from unauthorized changes
- Faster PR reviews (right team, first time)
- Clearer ownership and responsibility
- Reduced context-switching for reviewers
Governance best practices
Start small, expand gradually
# Phase 1: High-level ownership
/apps/ @geniusgarage/snippet-manager-team
/packages/ @geniusgarage/platform-team
# Phase 2: Team-specific ownership
/apps/web/ @geniusgarage/marketing-team
/apps/snippet-manager/ @geniusgarage/product-team
# Phase 3: Component-level ownership
/packages/ui/src/button.tsx @geniusgarage/design-system-leadDocument ownership decisions
Add comments to CODEOWNERS explaining team responsibilities:
# Design system components - reviewed by design team for consistency
/packages/ui/ @geniusgarage/frontend-team @geniusgarage/design-team
# Platform packages - critical infrastructure requiring platform team review
/packages/typescript-config/ @geniusgarage/platform-team
/packages/eslint-config/ @geniusgarage/platform-team
# App-specific code - product teams own their apps
/apps/web/ @geniusgarage/marketing-team
/apps/snippet-manager/ @geniusgarage/product-teamDocumentation helps new team members understand ownership rationale!
Regular ownership audits
Quarterly review:
- Are teams still aligned with CODEOWNERS?
- Has team structure changed?
- Are reviews getting to the right people?
- Should any ownership be more granular?
Adjust CODEOWNERS based on team structure and product evolution.
Done-when
Verify code governance:
- Created
.github/CODEOWNERSfile - Assigned teams to packages and apps
- Documented ownership rationale with comments
- Created PR changing owned package
- Verified CODEOWNERS auto-requested correct team
- Enabled branch protection rules (require review from code owners)
- Tested that merge is blocked without approval
- Understood how CODEOWNERS integrates with CI
- Set up quarterly ownership audit process
What's Next
Course complete! 🎉
You've built a production-ready Turborepo monorepo with:
- Multiple apps sharing packages
- Intelligent caching and filtering
- CI/CD with GitHub Actions
- Environment variable validation
- Comprehensive testing
- Code generators
- Automated versioning
- Team governance
Next steps:
- Deploy to production - All 3 apps to Vercel
- Add more apps - Scale to 5+ apps with same patterns
- Publish packages - Use Changesets to publish to npm
- Expand governance - Add more teams and boundaries
- Monitor performance - Track cache hit rates and build times
Resources:
Keep building!
Was this helpful?