Advanced (Mis à jour: 01/06/2026)

Gérer un monorepo avec Claude Code : pnpm, Turborepo, Nx et CI

Guide pratique pour gérer un monorepo avec Claude Code : repo map, pnpm workspace, affected tasks, CODEOWNERS et CI.

Gérer un monorepo avec Claude Code : pnpm, Turborepo, Nx et CI

Un monorepo est un dépôt Git unique qui contient plusieurs applications et bibliothèques. Cette approche facilite le partage de types, de composants UI, de configuration et de règles CI. Mais sans frontières claires, Claude Code peut produire une modification trop large : une petite évolution dans packages/shared peut toucher apps/web, apps/api et les tests en même temps.

La bonne méthode consiste à faire créer une carte du dépôt, définir les package boundaries, utiliser pnpm workspace avec workspace:*, puis vérifier uniquement les tâches affectées avec Turborepo ou Nx. Pour les concepts officiels, garde sous la main Nx Why Monorepos, Nx affected, Nx mental model, pnpm et la documentation Turborepo.

Structure cible

Avant de demander une correction, demande à Claude Code de comprendre la structure.

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/* représente les applications déployables. packages/* représente les modules réutilisables. Une package boundary définit quelles dépendances sont autorisées. Claude Code doit recevoir ces règles explicitement.

Premier prompt pour Claude Code

Analyse ce dépôt comme un monorepo.

Hypothèses:
- apps/web est l'application Next.js
- apps/api est le serveur API
- packages/ui contient l'UI réutilisable
- packages/shared contient les types, validations et fonctions pures
- packages/config contient ESLint, TypeScript, Prettier et la configuration de test

Règles:
- apps/* ne dépend pas directement de apps/*
- packages/* ne dépend pas de apps/*
- les packages internes utilisent workspace:*
- après modification, lint/test/build passent via affected tasks

Commence par produire une repo map: dépendances, cycles risqués, fichiers trop partagés et commandes CI à exécuter.
Ne modifie pas encore les fichiers.

Cette dernière phrase évite les changements prématurés. Dans un monorepo, le vrai risque est souvent la direction des dépendances, pas une erreur de syntaxe isolée.

Configurer pnpm workspace

packages:
  - "apps/*"
  - "packages/*"

Le package.json racine doit exposer des commandes stables.

{
  "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"
  }
}

Les dépendances internes doivent être déclarées ainsi :

{
  "dependencies": {
    "@acme/shared": "workspace:*",
    "@acme/ui": "workspace:*"
  }
}

Prompt conseillé :

Rends @acme/ui et @acme/shared utilisables depuis apps/web.
Utilise workspace:* dans package.json.
N'ajoute pas d'import via ../../packages.
La modification doit être vérifiable avec pnpm check:deps et pnpm ci:affected.

Turborepo et Nx affected

Turborepo est un bon choix lorsque les packages ont déjà leurs scripts.

{
  "$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 devient intéressant quand tu veux un project graph plus fort et une analyse d’impact plus fine.

pnpm dlx nx@latest init
pnpm nx affected -t lint test build --base=origin/main --head=HEAD

Avec Claude Code, demande “exécute les checks affectés” plutôt que “exécute tout”. C’est plus rapide, moins coûteux et plus facile à relire.

CODEOWNERS et politique de dépendances

/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

Ajoute ensuite une vérification automatique dans 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 est indispensable pour que les checks affected aient l’historique Git nécessaire.

Cas d’usage concrets

  1. Modifier un Button dans packages/ui. Claude Code doit garder l’API publique stable et lister les écrans affectés dans apps/web.

  2. Déplacer des DTO partagés dans packages/shared. Un DTO est une forme de données pour l’API et l’UI, pas un modèle de base de données.

  3. Mettre à jour TypeScript, Next.js ou l’outillage de test. Commence par packages/config, puis vérifie les apps affectées.

  4. Ajouter une fonctionnalité transversale comme la facturation ou la recherche. Demande à Claude Code de proposer plusieurs PRs au lieu d’un énorme patch.

Pièges fréquents

Le premier piège est de transformer packages/shared en fourre-tout. Il doit contenir du code stable, générique et facile à tester.

Le deuxième est l’import relatif comme ../../packages/shared/src, qui contourne la frontière du package.

Le troisième est d’adopter Turborepo et Nx en profondeur en même temps. Choisis d’abord un modèle principal.

Le quatrième est de croire qu’une app qui passe localement prouve que tout le monorepo va bien. Le PR doit indiquer les packages modifiés, les apps affectées, les commandes exécutées et les risques restants.

Prompt de revue

Relis ce diff du point de vue monorepo.

Vérifie:
- pas de dépendance directe apps/* vers apps/*
- pas de dépendance packages/* vers apps/*
- les dépendances internes utilisent workspace:*
- packages/shared ne contient que du code partagé stable
- affected lint/test/build couvre le changement
- CODEOWNERS clarifie la responsabilité de revue

Retourne:
- blockers
- corrections recommandées
- commandes vérifiées
- résumé d'impact pour le PR

À lire ensuite : Claude Code et Nx workspace, Claude Code et pnpm workspace, Claude Code avec Turborepo, collaboration d’équipe avec Claude Code.

Pour déployer Claude Code dans un monorepo d’équipe, il faut surtout définir les frontières, owners, checks CI et prompts de revue. ClaudeCodeLab peut aider via la page formation et consultation Claude Code avec un vrai dépôt comme base de travail.

Résumé

Claude Code est efficace dans un monorepo lorsque les contraintes sont explicites : repo map, package boundaries, pnpm workspace, Turborepo/Nx affected tasks, CODEOWNERS, politique de dépendances et checklist CI. En pratique, imposer workspace:* et standardiser pnpm ci:affected réduit vite les oublis de revue et les builds inutiles.

#Claude Code #monorepo #pnpm workspace #Turborepo #Nx
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.