Mengelola Monorepo dengan Claude Code: pnpm, Turborepo, Nx, dan CI
Panduan monorepo Claude Code: repo map, pnpm workspace, affected tasks, CODEOWNERS, dependency policy, dan CI.
Monorepo adalah struktur ketika beberapa aplikasi dan library dikelola dalam satu repository Git. Dengan pola seperti apps/web, apps/api, packages/ui, dan packages/shared, tim bisa berbagi tipe, komponen UI, konfigurasi, dan CI. Tetapi tanpa batas yang jelas, Claude Code bisa membuat perubahan terlalu luas: satu edit kecil di packages/shared dapat memengaruhi beberapa aplikasi sekaligus.
Workflow yang aman adalah membuat repo map terlebih dahulu, menentukan package boundaries, menggunakan pnpm workspace dan workspace:*, lalu menjalankan affected tasks dengan Turborepo atau Nx. Untuk rujukan resmi, lihat Nx why monorepos, Nx affected, Nx mental model, pnpm, dan Turborepo docs.
Struktur yang dituju
Sebelum meminta Claude Code mengedit, minta ia memahami arsitektur repository.
graph TD
WEB["apps/web"] --> UI["packages/ui"]
WEB --> SHARED["packages/shared"]
API["apps/api"] --> SHARED
UI --> CONFIG["packages/config"]
SHARED --> CONFIG
CI["CI affected tasks"] --> WEB
CI --> API
apps/* adalah aplikasi yang dideploy. packages/* adalah modul yang dipakai ulang. Package boundary adalah aturan tentang package mana yang boleh bergantung pada package lain. Aturan ini harus diberikan secara eksplisit kepada Claude Code.
Prompt awal untuk Claude Code
Analisis repository ini sebagai monorepo.
Asumsi:
- apps/web adalah aplikasi Next.js
- apps/api adalah API server
- packages/ui berisi reusable UI
- packages/shared berisi type, validation, dan pure function
- packages/config berisi ESLint, TypeScript, Prettier, dan konfigurasi test
Aturan:
- apps/* tidak boleh bergantung langsung pada apps/*
- packages/* tidak boleh bergantung pada apps/*
- internal package harus memakai workspace:*
- setelah perubahan, lint/test/build dijalankan sebagai affected tasks
Pertama buat repo map: dependencies, risky cycles, file yang terlalu dibagi, dan command CI untuk verifikasi.
Jangan edit file dulu.
Kalimat terakhir penting. Dalam monorepo, risiko utama sering bukan syntax error, melainkan arah dependency yang salah.
pnpm workspace
packages:
- "apps/*"
- "packages/*"
Di package.json root, siapkan command yang konsisten.
{
"name": "acme-monorepo",
"private": true,
"packageManager": "pnpm@10.12.1",
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck",
"ci:affected": "turbo run lint test build --affected",
"check:deps": "node scripts/check-workspace-deps.cjs"
}
}
Internal dependency ditulis dengan workspace:*.
{
"dependencies": {
"@acme/shared": "workspace:*",
"@acme/ui": "workspace:*"
}
}
Prompt yang lebih aman:
Buat apps/web dapat menggunakan @acme/ui dan @acme/shared.
Gunakan workspace:* di package.json.
Jangan buat import lewat ../../packages.
Perubahan harus bisa diverifikasi dengan pnpm check:deps dan pnpm ci:affected.
Turborepo dan Nx affected
Turborepo cocok ketika setiap package sudah punya scripts sendiri.
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Nx berguna ketika tim membutuhkan project graph dan analisis affected yang lebih kuat.
pnpm dlx nx@latest init
pnpm nx affected -t lint test build --base=origin/main --head=HEAD
Untuk Claude Code, gunakan instruksi “jalankan checks yang affected” daripada “jalankan semuanya”. Ini menjaga CI tetap cepat dan murah.
CODEOWNERS dan dependency policy
/apps/web/ @acme/frontend
/apps/api/ @acme/backend
/packages/ui/ @acme/design-system
/packages/shared/ @acme/platform
/packages/config/ @acme/platform
/pnpm-workspace.yaml @acme/platform
/turbo.json @acme/platform
Policy juga perlu dicek dengan code. Simpan sebagai scripts/check-workspace-deps.cjs.
const fs = require("node:fs");
const path = require("node:path");
const ROOT = process.cwd();
const WORKSPACE_DIRS = ["apps", "packages"];
const DEP_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function findPackageDirs(baseDir) {
const absoluteBase = path.join(ROOT, baseDir);
if (!fs.existsSync(absoluteBase)) return [];
return fs
.readdirSync(absoluteBase, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => path.join(absoluteBase, entry.name))
.filter((dir) => fs.existsSync(path.join(dir, "package.json")));
}
const packages = WORKSPACE_DIRS.flatMap(findPackageDirs).map((dir) => {
const manifest = readJson(path.join(dir, "package.json"));
return { dir, name: manifest.name, manifest };
});
const byName = new Map(packages.map((pkg) => [pkg.name, pkg]));
let failed = false;
for (const pkg of packages) {
for (const field of DEP_FIELDS) {
const deps = pkg.manifest[field] || {};
for (const [name, range] of Object.entries(deps)) {
const internal = byName.get(name);
if (!internal) continue;
const fromDir = path.relative(ROOT, pkg.dir).replace(/\\/g, "/");
const toDir = path.relative(ROOT, internal.dir).replace(/\\/g, "/");
if (!String(range).startsWith("workspace:")) {
console.error(`${pkg.name}: ${name} must use workspace:* in ${field}`);
failed = true;
}
if (toDir.startsWith("apps/")) {
console.error(`${pkg.name}: ${fromDir} must not depend on app package ${toDir}`);
failed = true;
}
}
}
}
if (failed) process.exit(1);
console.log(`Checked ${packages.length} workspace packages.`);
Checklist CI
name: monorepo-ci
on:
pull_request:
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm check:deps
- run: pnpm ci:affected
fetch-depth: 0 diperlukan karena affected checks membutuhkan Git history yang cukup.
Use case praktis
-
Mengubah Button di
packages/ui. Claude Code harus menjaga public API tetap stabil dan mencantumkan screen yang terdampak diapps/web. -
Memindahkan shared DTO ke
packages/shared. DTO adalah bentuk data untuk API dan UI, bukan model database. -
Upgrade TypeScript, Next.js, atau testing tools. Mulai dari
packages/config, lalu verifikasi aplikasi yang affected. -
Menambahkan fitur lintas tim seperti billing, search, atau onboarding. Minta Claude Code membagi pekerjaan menjadi beberapa PR kecil.
Pitfall yang sering terjadi
Pertama, packages/shared menjadi tempat menaruh semua hal. Simpan hanya code yang stabil, generik, dan mudah dites.
Kedua, memakai import relatif seperti ../../packages/shared/src. Ini melewati package boundary dan membuat build sulit dilacak.
Ketiga, mengadopsi Turborepo dan Nx terlalu dalam sekaligus. Pilih satu model utama dulu.
Keempat, menganggap “jalan di lokal” sudah cukup. PR harus menyebut package yang berubah, aplikasi terdampak, command yang dijalankan, dan risiko tersisa.
Prompt review
Review diff ini dari sudut pandang monorepo.
Cek:
- tidak ada dependency langsung dari apps/* ke apps/*
- packages/* tidak bergantung pada apps/*
- internal dependencies memakai workspace:*
- packages/shared hanya berisi stable shared code
- affected lint/test/build sudah cukup
- CODEOWNERS jelas menunjukkan reviewer
Return:
- blocker
- rekomendasi perbaikan
- command yang sudah diverifikasi
- ringkasan impact untuk PR body
Lanjutkan dengan Claude Code dan Nx workspace, Claude Code dan pnpm workspace, Claude Code dengan Turborepo, dan kolaborasi tim dengan Claude Code.
Jika tim ingin memakai Claude Code di monorepo nyata, hal utama adalah boundary, owner, CI, dan prompt review. ClaudeCodeLab dapat membantu melalui training dan konsultasi Claude Code dengan CLAUDE.md, CODEOWNERS, CI, dan PR workflow berbasis repository nyata.
Ringkasan
Claude Code efektif untuk monorepo jika constraint jelas. Repo map, package boundaries, pnpm workspace, Turborepo/Nx affected tasks, CODEOWNERS, dependency policy, dan CI checklist mengubah output AI menjadi workflow engineering yang bisa diulang. Dalam praktik, memaksa workspace:* dan menstandarkan pnpm ci:affected langsung mengurangi review miss dan full CI yang tidak perlu.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission receipt Claude Code: mencatat scope, bukti, dan rollback
Pola permission receipt untuk Claude Code: aksi yang diizinkan, batas approval, command verifikasi, rollback, dan cek CTA revenue.
Agent Harness Aman untuk Claude Code dan Codex: Permission, Verifikasi, dan Rollback
Rancang Agent Harness praktis untuk Claude Code dan Codex dengan policy, plan, verification, dan recovery layer.
Subagent Claude Code: panduan praktis untuk delegasi artikel dan kode
Panduan subagent Claude Code untuk membagi pekerjaan artikel dan kode: aturan delegasi, prompt, risiko, dan checklist.