Claude Code monorepo CLAUDE.md subagents team setup developer tools workflow

Claude Code in a Monorepo: The Complete Setup Guide

The Prompt Shelf ·

A monorepo works against Claude Code’s defaults. Claude loads your CLAUDE.md once and carries it everywhere — which means your frontend developer gets database migration advice, and your backend service gets design token instructions. The context gets noisy, the token budget inflates, and Claude starts making decisions based on instructions that don’t apply.

This guide covers the full setup for running Claude Code effectively in a monorepo: how to structure nested CLAUDE.md files, build package-scoped subagents, manage token budgets at scale, and roll out the configuration to a team.

The Core Problem With Monorepos

A standard single-project CLAUDE.md works because the context is coherent. Everything in the file is relevant, all the time.

In a monorepo, that breaks down fast. Consider a typical setup:

my-monorepo/
  packages/
    api/          # Node.js + PostgreSQL
    web/          # Next.js + Tailwind
    mobile/       # React Native + Expo
    shared/       # Shared TypeScript types
  infra/          # Terraform + AWS
  .github/        # CI/CD workflows

If you put everything into one root CLAUDE.md, you end up with a file that is simultaneously explaining Prisma schema conventions, Tailwind utility patterns, Expo build quirks, Terraform state management, and GitHub Actions syntax. Claude loads all of it on every session, regardless of where you’re working.

The result is a bloated context window, confused priorities, and Claude applying the wrong conventions in the wrong package. One developer in the community reported their CLAUDE.md growing to 47,000 words before they hit Claude’s recommended 40k-word limit and started seeing degraded responses.

The fix is hierarchical context loading — a pattern Claude Code supports natively.

How Claude Code Loads Nested CLAUDE.md Files

Claude Code walks the directory tree from your current working directory upward and loads every CLAUDE.md it finds. The loading order is:

  1. Global: ~/.claude/CLAUDE.md (applies to every project on your machine)
  2. Project root: ./CLAUDE.md
  3. Subdirectories: loaded as you work within them

Files are additive — all levels are merged and injected into context simultaneously. Subdirectory files don’t replace the root file; they extend it. For conflicting instructions, deeper files take precedence.

This means Claude working inside packages/api/ has access to:

  • Your global personal preferences
  • Monorepo-wide conventions (root CLAUDE.md)
  • API-specific rules (packages/api/CLAUDE.md)

And critically, it does not carry packages/web/CLAUDE.md into that session.

Structuring Your CLAUDE.md Hierarchy

The Root File: Monorepo-Wide Rules Only

Your root CLAUDE.md should contain only what applies across every package in the repo. Keep it lean. A 200-300 line root file is healthy; beyond 500 lines, start questioning whether something belongs in a subdirectory instead.

# my-monorepo

## Repository structure
- packages/api — Node.js REST API (PostgreSQL, Prisma)
- packages/web — Next.js frontend (Tailwind, shadcn/ui)
- packages/mobile — React Native app (Expo)
- packages/shared — Shared TypeScript types and utilities
- infra/ — Terraform infrastructure (AWS)

## Universal rules
- TypeScript everywhere. No plain JavaScript files.
- All commits follow Conventional Commits format.
- Never commit secrets. Use environment variables for all credentials.
- Tests required for all new features. Run `pnpm test` before committing.

## Tooling
- Package manager: pnpm (workspaces)
- Build: turbo
- Lint: eslint with @my-org/eslint-config
- Format: prettier (config at root .prettierrc)

## Monorepo commands
- `pnpm build` — build all packages
- `pnpm test` — run all tests
- `pnpm lint` — lint all packages
- `turbo run build --filter=@my-org/api` — build a single package

## What NOT to do
- Do not install packages with npm or yarn — use pnpm
- Do not import across packages without adding them to package.json
- Do not modify shared/ without checking all consumers

Package-Level Files: Domain-Specific Context

Each package gets its own CLAUDE.md with rules that only make sense in that context.

packages/api/CLAUDE.md

# API package

## Stack
Node.js 22 + Express 5 + Prisma 6 + PostgreSQL 16

## Database conventions
- All database access goes through Prisma client (never raw SQL)
- Migrations: `pnpm prisma migrate dev --name <description>`
- Never modify existing migrations. Create new ones.
- Seed data: `pnpm prisma db seed`

## Route conventions
- REST routes in src/routes/ — one file per resource
- Route handlers are thin. Business logic goes in src/services/
- All routes use zod validation via validateRequest middleware
- Error handling: throw ApiError — the error middleware handles formatting

## Testing
- Framework: Vitest
- Integration tests use a test database (TEST_DATABASE_URL)
- Run: `pnpm test` or `pnpm test:watch`
- Factory functions for test data are in tests/factories/

## Environment variables
Required in .env.local (see .env.example for all keys):
- DATABASE_URL — primary database
- JWT_SECRET — token signing
- REDIS_URL — session store

packages/web/CLAUDE.md

# Web package

## Stack
Next.js 15 (App Router) + Tailwind CSS 4 + shadcn/ui + React Query

## Component rules
- Server Components by default. Use "use client" only when needed (interactivity, hooks).
- shadcn/ui for all UI primitives. Do not build from scratch what shadcn covers.
- Tailwind utility classes only — no custom CSS files unless absolutely necessary.
- Co-locate tests with components: Button.tsx → Button.test.tsx

## Directory structure
- app/ — Next.js App Router pages and layouts
- components/ui/ — shadcn/ui components (do not edit these)
- components/ — custom components
- lib/ — utilities, hooks, API client
- types/ — TypeScript types (non-shared)

## Data fetching
- Server Components: fetch directly in the component (async component)
- Client Components: React Query via useQuery/useMutation
- API calls go through lib/api-client.ts — never call fetch directly from components

## Styling
- No inline styles
- Responsive classes in order: base → sm: → md: → lg:
- Design tokens via CSS variables defined in app/globals.css

infra/CLAUDE.md

# Infrastructure

## Stack
Terraform 1.9 + AWS + Terragrunt

## Rules
- Never run terraform apply directly. Use the CI pipeline.
- State is in S3 (see backend.tf). Never modify state manually.
- All changes require a plan review before apply.
- Tag every resource: Environment, Team, CostCenter (see locals.tf)

## Structure
- modules/ — reusable Terraform modules
- environments/ — environment-specific configs (dev, staging, prod)
- Use terragrunt for DRY environment configs

## Sensitive values
- Secrets go in AWS Secrets Manager, not in Terraform state
- Use data.aws_secretsmanager_secret for lookups

Package-Scoped Subagents

Subagents take the nested context pattern one step further. Instead of just giving Claude the right instructions for a package, you can create agents that are experts in that package’s domain, with the right tool restrictions for the task.

Store project subagents in .claude/agents/. They’re checked into version control and available to everyone on the team.

Database Migration Agent

This agent handles Prisma migrations so the main context doesn’t fill up with schema details.

.claude/agents/db-migrator.md

---
name: db-migrator
description: Use when creating, reviewing, or applying Prisma database migrations. Handles schema changes, migration files, and database seeding.
tools: Read, Write, Edit, Bash, Glob
model: sonnet
color: blue
---

You are a database migration specialist for this monorepo.

Your scope: packages/api/prisma/

When creating migrations:
1. Read the current schema.prisma to understand the current state
2. Understand the requested change
3. Generate the migration with: pnpm --filter @my-org/api prisma migrate dev --name <description>
4. Review the generated migration file for correctness
5. Check if seed data in prisma/seed.ts needs updating

Safety rules:
- Never modify existing migration files
- Always check for data loss risks (dropping columns/tables)
- For destructive changes, explain the risk and suggest a safer multi-step approach
- Never run prisma migrate deploy (that's for CI only)

For production deployments, explain that prisma migrate deploy should be run in CI, not locally.

Frontend Auditor Agent

A read-only agent for catching accessibility, performance, and consistency issues across the web package.

.claude/agents/web-auditor.md

---
name: web-auditor
description: Use for reviewing React components and Next.js pages for accessibility, performance, and Tailwind/shadcn consistency issues.
tools: Read, Glob, Grep
model: haiku
color: green
---

You are a frontend code quality auditor for the packages/web directory.

You are read-only. You never suggest edits — you report findings.

Check for:

**Accessibility**
- Missing aria-labels on interactive elements without visible text
- Images without alt attributes
- Form inputs without associated labels
- Focus management issues in modals/dialogs

**Performance**
- "use client" on components that don't need it
- Large components that could be split into server/client boundaries
- Images not using next/image
- Missing Suspense boundaries around dynamic content

**Consistency**
- Custom CSS where Tailwind utilities would suffice
- Direct fetch() calls outside lib/api-client.ts
- Shadcn components rebuilt from scratch instead of using the library

Format findings as:
- File path
- Issue type
- Line reference (if applicable)
- Brief explanation

Infrastructure Planner Agent

For Terraform work, a plan-only agent prevents accidental applies.

.claude/agents/infra-planner.md

---
name: infra-planner
description: Use for reviewing Terraform changes, understanding infrastructure state, and planning infrastructure modifications. Never applies changes.
tools: Read, Glob, Grep, Bash
model: sonnet
permissionMode: plan
color: orange
---

You are an infrastructure planning specialist. You analyze Terraform configurations and explain changes, risks, and dependencies.

You operate in plan mode. You never apply Terraform changes.

When reviewing infrastructure changes:
1. Read the relevant .tf files
2. Check for resource dependencies
3. Identify potential risks (downtime, data loss, cost implications)
4. Suggest the safest execution order for multi-step changes
5. Flag anything that should go through a change management process

For cost analysis, look at resource types and sizes. Flag anything that looks unexpectedly expensive.

Always end with: "To apply these changes, run the CI pipeline rather than applying locally."

Token Budget Management

Token costs scale with monorepo size. Here are the patterns that keep usage manageable.

Split Large Package Files

If a single package’s CLAUDE.md grows beyond 200-300 lines, split it using @ references to separate documentation files:

# API package

## Quick reference
- Stack: Node.js 22 + Express 5 + Prisma 6
- Test command: pnpm test
- Lint command: pnpm lint

## Detailed guides
@docs/database-conventions.md
@docs/error-handling.md
@docs/testing-guide.md

The key distinction: @ references in CLAUDE.md are conditionally loaded — Claude fetches them when it needs them, rather than loading all content upfront. This significantly reduces context at session start.

Compare this to embedding content directly, which loads everything unconditionally:

# Embedded (loaded unconditionally — avoid for long docs)
## Database conventions
[500 lines of detail...]

# Referenced (loaded on demand — prefer for large docs)
@docs/database-conventions.md

Use Haiku for Exploration Subagents

The built-in Explore subagent already uses Haiku for read-only codebase exploration. For your custom subagents that only need to read and report (like the web-auditor above), explicitly set model: haiku. It’s 25x cheaper than Opus for tasks that don’t require deep reasoning.

Set maxTurns on Focused Subagents

For subagents with a narrow scope, limit the number of turns to prevent runaway sessions:

---
name: pr-description-writer
description: Use when you need to write a pull request description based on the current git diff.
tools: Bash, Read
model: sonnet
maxTurns: 5
---

Write a clear, concise pull request description based on the git diff.
Run `git diff main...HEAD` to get the changes, then produce:
1. A one-line title following Conventional Commits
2. A bullet list of the key changes
3. Testing instructions (what to check manually)

Team Rollout

What Goes in Version Control

Everything that should be consistent across the team:

.claude/
  agents/           # Project subagents — commit these
    db-migrator.md
    web-auditor.md
    infra-planner.md
  settings.json     # Project-wide Claude settings — commit this
  settings.local.json  # Personal overrides — add to .gitignore

CLAUDE.md                    # Root — commit
packages/api/CLAUDE.md       # Package — commit
packages/web/CLAUDE.md       # Package — commit
packages/mobile/CLAUDE.md    # Package — commit
infra/CLAUDE.md              # Package — commit

Add .claude/settings.local.json to .gitignore — that’s for personal settings like API key overrides, personal MCP servers, and individual preferences that shouldn’t be shared.

settings.json for Team Consistency

The .claude/settings.json file enforces shared defaults. A useful monorepo configuration:

{
  "model": "sonnet",
  "permissions": {
    "deny": [
      "Bash(npm install:*)",
      "Bash(yarn:*)",
      "Bash(git push --force:*)",
      "Bash(terraform apply:*)"
    ]
  },
  "env": {
    "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8000"
  }
}

This blocks npm install (enforce pnpm), yarn (same), force pushes, and direct terraform applies — regardless of whether a team member’s personal settings would allow them.

Onboarding New Team Members

Add a section to your root CLAUDE.md that explains the setup to Claude itself. This helps when someone onboards and Claude is helping them understand the project:

## For new team members
This is a pnpm monorepo using Turborepo. To get started:
1. `pnpm install` — install all dependencies
2. `pnpm dev` — start all packages in development mode
3. `cp .env.example .env.local` — set up environment variables

Each package has its own CLAUDE.md with package-specific context.
When working in packages/api, read packages/api/CLAUDE.md for database and API conventions.
When working in packages/web, read packages/web/CLAUDE.md for frontend conventions.

Common Mistakes

Putting everything in the root CLAUDE.md. The root file should contain only cross-cutting concerns. If a rule only applies to one package, it belongs in that package’s CLAUDE.md.

Duplicating conventions across package files. If you find yourself writing the same TypeScript rule in three package CLAUDE.md files, it belongs in the root. One source of truth.

Creating subagents without clear delegation signals. Claude chooses which subagent to use based on the description field. Vague descriptions lead to the wrong agent being chosen or no agent being chosen. Write descriptions as “when to use this” statements, not “what this is” statements.

Ignoring the isolation flag for risky subagents. For subagents that modify files, the isolation: worktree setting runs them in a temporary git worktree. If the changes look wrong, you can discard the worktree without affecting your working tree. Use it for refactoring agents and migration agents.

---
name: safe-refactorer
description: Use for large refactoring tasks that touch many files at once.
tools: Read, Write, Edit, Bash, Glob, Grep
model: opus
isolation: worktree
---

Not committing subagent files. If your .claude/agents/ directory isn’t in version control, every developer on the team has to recreate the same agents independently. Commit them.

Full Example Repository Structure

Here is a complete reference structure for a TypeScript monorepo with Claude Code fully configured:

my-monorepo/
├── CLAUDE.md                        # Monorepo-wide rules
├── .claude/
│   ├── agents/
│   │   ├── db-migrator.md           # Prisma migration specialist
│   │   ├── web-auditor.md           # Frontend code quality (Haiku)
│   │   ├── infra-planner.md         # Terraform planning (plan mode)
│   │   └── pr-description-writer.md # PR description generator
│   ├── settings.json                # Team settings (committed)
│   └── settings.local.json          # Personal settings (gitignored)
├── packages/
│   ├── api/
│   │   ├── CLAUDE.md                # API-specific rules
│   │   └── docs/                    # Referenced from CLAUDE.md
│   │       ├── database-conventions.md
│   │       └── testing-guide.md
│   ├── web/
│   │   └── CLAUDE.md                # Web-specific rules
│   ├── mobile/
│   │   └── CLAUDE.md                # Mobile-specific rules
│   └── shared/
│       └── CLAUDE.md                # Shared package rules
└── infra/
    └── CLAUDE.md                    # Infrastructure rules

Further Reading

If you’re starting from scratch with CLAUDE.md, see the guide on how to write a CLAUDE.md file for the fundamentals. For team setups that use multiple AI tools alongside Claude Code, AGENTS.md vs CLAUDE.md covers how the two formats complement each other. Browse the The Prompt Shelf gallery for real-world CLAUDE.md examples from popular open-source projects.

More from the blog

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules