--- id: wiki-2026-0508-enterprise-scale-monorepo-manage title: Enterprise Scale Monorepo Management category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Monorepo, Polyrepo vs Monorepo, Bazel, Nx, Turborepo] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [monorepo, build-system, devops, ci-cd, scaling] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: nx-turborepo-bazel --- # Enterprise Scale Monorepo Management ## 매 한 줄 > **"매 single repo, many projects — 매 build graph 의 일관성"**. 매 monorepo 의 핵심은 모든 코드를 하나의 VCS root 안에 두되, 매 build 시스템이 dependency graph 를 이해해서 affected projects 만 build/test 한다는 점. Google (Piper/Bazel), Meta (Buck2), Microsoft (Rush), 그리고 매 OSS 의 Nx/Turborepo 가 이 패러다임을 driving. ## 매 핵심 ### 매 Monorepo 가치 - **Atomic cross-project changes**: 매 API + caller 의 한 PR 안에서 변경. - **Shared tooling**: 매 lint, format, build, test 의 unified config. - **Visibility**: 매 모든 코드 의 grep-able. - **Refactor confidence**: 매 type-checker 가 모든 caller 를 검증. ### 매 도전 과제 - **Build time scaling**: 매 naive build 의 N² growth — affected detection 필수. - **VCS performance**: 매 git 의 100GB+ repo 에서 sparse checkout / VFS 필요. - **Permissions**: 매 single repo + multiple teams = CODEOWNERS / branch protection. - **CI cost**: 매 모든 commit 의 모든 project rebuild 의 X — incremental + cache. ### 매 도구 선택 1. **Nx** (TypeScript-heavy, mid-large): smart caching + computation graph. 2. **Turborepo** (Vercel, JS/TS): Rust-based, simple config, remote cache. 3. **Bazel** (polyglot, mega-scale): hermetic builds, network-distributed. 4. **Pants** (Python-heavy): similar to Bazel, lighter setup. 5. **Rush** (.NET / TS): Microsoft's pnpm-based. ## 💻 패턴 ### Nx workspace structure ```bash my-org/ ├── nx.json ├── package.json ├── tsconfig.base.json ├── apps/ │ ├── web/ # Next.js app │ └── api/ # NestJS API ├── libs/ │ ├── ui/ # shared React components │ ├── data-access/# API clients │ └── utils/ └── tools/ ``` ### nx.json with affected + cache ```json { "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "cacheableOperations": ["build", "test", "lint", "e2e"], "accessToken": "${NX_CLOUD_TOKEN}" } } }, "targetDefaults": { "build": { "dependsOn": ["^build"], "inputs": ["production", "^production"] }, "test": { "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"] } }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/jest.config.ts"] } } ``` ### Affected detection in CI ```yaml # .github/workflows/ci.yml jobs: affected: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: nrwl/nx-set-shas@v4 - run: pnpm install --frozen-lockfile - run: npx nx affected -t lint test build --parallel=4 - run: npx nx affected -t e2e --parallel=1 ``` ### Turborepo pipeline ```json // turbo.json { "$schema": "https://turbo.build/schema.json", "globalDependencies": ["**/.env.*local"], "tasks": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**", "dist/**"] }, "test": { "dependsOn": ["build"], "outputs": ["coverage/**"] }, "lint": {}, "dev": { "cache": false, "persistent": true } } } ``` ### Bazel BUILD file (polyglot) ```python # libs/ui/BUILD.bazel load("@npm//:defs.bzl", "npm_link_all_packages") load("@aspect_rules_ts//ts:defs.bzl", "ts_project") ts_project( name = "ui", srcs = glob(["src/**/*.ts", "src/**/*.tsx"]), declaration = True, tsconfig = "//:tsconfig", deps = [ "//libs/utils", "@npm//react", "@npm//@types/react", ], visibility = ["//apps:__subpackages__"], ) ``` ### CODEOWNERS for team boundaries ``` # CODEOWNERS /apps/web/ @org/frontend-team /apps/api/ @org/backend-team /libs/ui/ @org/design-system /libs/data-access/ @org/backend-team @org/frontend-team /.github/ @org/platform-team /tools/ @org/platform-team ``` ### Remote cache with Turborepo ```bash # Self-hosted with turborepo-remote-cache docker run -p 3000:3000 \ -e TURBO_TOKEN=secret \ -e STORAGE_PROVIDER=s3 \ -e STORAGE_PATH=my-bucket \ ducktors/turborepo-remote-cache # In repo: echo 'TURBO_API=http://cache.internal:3000' > .turbo/config.json echo 'TURBO_TOKEN=secret' >> .turbo/config.json turbo build --remote-only ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | <50 packages, JS/TS only | Turborepo (simplicity) | | 50-500 packages, type-heavy | Nx (project graph + generators) | | Polyglot (Go+Rust+TS+Python) | Bazel (hermeticity) | | Python ML monorepo | Pants v2 | | <10 packages | pnpm workspaces alone | **기본값**: 매 TS/JS team 에게 Turborepo 또는 Nx (size dependent). ## 🔗 Graph - 부모: [[Version Control]] - 응용: [[Code Ownership]] - Adjacent: [[Bazel]] · [[Nx]] · [[Turborepo]] ## 🤖 LLM 활용 **언제**: refactoring across packages, generating new lib boilerplate (Nx generator), CODEOWNERS automation, dependency graph 분석. **언제 X**: 매 build cache invalidation logic 의 직접 결정 — Nx/Bazel hash algorithm 신뢰. ## ❌ 안티패턴 - **Single CI job for entire repo**: 매 affected detection 없이 매 commit 모든 build — 30분+ pipeline. - **No remote cache**: 매 each developer 가 cold rebuild — wasteful. - **Mixing app-specific and lib code**: 매 libs/ 와 apps/ 의 분리 안 함 — circular deps risk. - **Implicit dependencies**: 매 package.json 에 list 안 된 import — Bazel/Nx 가 catch. - **No CODEOWNERS**: 매 review fatigue + ownership 모호. ## 🧪 검증 / 중복 - Verified (Nx Cloud docs 2026, Turborepo 2.0, Bazel 7). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — monorepo tooling matrix + Nx/Turbo/Bazel patterns |