Hierarchical Config for Monorepos

Module 05: AI Project Configuration | Expansion Guide

Back to Module 05

The Problem

Your monorepo has a React frontend, a Python backend, a Go microservice, and shared packages. You create one CLAUDE.md at the root. Now Claude is confused: "Use TypeScript strict mode" when working on Python? "Follow Django patterns" when editing React? AI needs context, but giving it everything at once creates noise.

The issue: Monorepos have different rules for different packages. A single config file forces you to choose between too generic (useless) or too detailed (overwhelming).

Different parts of your codebase need different guidance. The web app follows React conventions, the API follows Flask patterns, shared libraries have their own rules. AI should know where it is and what applies.

The Core Insight

Organize configuration hierarchically: root level for shared conventions, package level for specific rules. AI traverses up the tree, inheriting and overriding as needed.

Think of it like CSS specificity: root config applies everywhere (like *), package config applies to that package (like .class), and more specific configs override less specific ones.

The Hierarchy Pattern

Level 1: Root Configuration (Universal Rules)

At repo root, define only what applies to EVERYTHING:

# /CLAUDE.md

## Monorepo Overview

This is a monorepo containing multiple applications and packages.

**Monorepo Tool:** pnpm workspaces
**Node Version:** 20.x
**Package Manager:** pnpm (NOT npm or yarn)

## Global Conventions

- **Git:** Conventional Commits (feat:, fix:, docs:, etc.)
- **Testing:** Each package has its own test setup
- **Linting:** Configured per package (see package-specific CLAUDE.md)
- **CI/CD:** GitHub Actions in `.github/workflows/`

## Workspace Structure

```
apps/
  web/              # Next.js frontend (see apps/web/CLAUDE.md)
  api/              # Express API (see apps/api/CLAUDE.md)
  admin/            # Admin dashboard (see apps/admin/CLAUDE.md)
packages/
  ui/               # Shared UI components (see packages/ui/CLAUDE.md)
  utils/            # Shared utilities
  types/            # Shared TypeScript types
```

## Global Commands

```bash
# Install dependencies (run from root)
pnpm install

# Run specific workspace
pnpm --filter web dev
pnpm --filter api dev

# Run all workspaces
pnpm dev

# Build all
pnpm build

# Test all
pnpm test
```

## Important Rules

1. **NEVER install at root level** unless it's a dev dependency shared by all packages
2. **Use workspace protocol** for internal packages: `"@repo/ui": "workspace:*"`
3. **Each app/package has its own CLAUDE.md** - refer to those for specifics
4. When working in a package, ALWAYS read its CLAUDE.md first

Level 2: Application Configuration (App-Specific)

Each app gets its own config that extends root:

# /apps/web/CLAUDE.md

## Web App (Next.js Frontend)

**Inherits:** `/CLAUDE.md` (monorepo global rules)
**Extends:** This file adds web-specific conventions

## Stack

- **Framework:** Next.js 14 (App Router)
- **Language:** TypeScript 5.3
- **Styling:** Tailwind CSS
- **State:** Zustand + React Query
- **UI Components:** From `@repo/ui` package
- **Types:** From `@repo/types` package

## Local Commands

```bash
# From repo root:
pnpm --filter web dev        # Start dev server
pnpm --filter web build      # Build for production
pnpm --filter web test       # Run tests

# From apps/web/:
pnpm dev
pnpm build
pnpm test
```

## Conventions

### Importing Internal Packages
```typescript
// Always use alias imports:
import { Button } from '@repo/ui'
import { type User } from '@repo/types'

// NOT relative paths across workspaces:
// import { Button } from '../../../packages/ui/src/Button'  ❌
```

### Component Structure
```
src/
  app/              # Next.js App Router pages
  components/       # Web-specific components (NOT shared ones)
  lib/              # Web-specific utilities
  styles/           # Global styles
```

**Key Rule:** If a component could be used by `admin` app, it belongs in `@repo/ui`, not here.

## Anti-Patterns

- ❌ **DON'T** duplicate components from `@repo/ui`
- ❌ **DON'T** import from other apps (only from `packages/`)
- ❌ **DON'T** put shared types here (use `@repo/types`)

Level 3: Package Configuration (Library-Specific)

# /packages/ui/CLAUDE.md

## Shared UI Package

**Inherits:** `/CLAUDE.md` (monorepo global rules)
**Extends:** This file adds UI package conventions

## Purpose

Shared React components used by `web` and `admin` apps.

## Stack

- **React:** 18.x (no Next.js specifics)
- **TypeScript:** Strict mode
- **Styling:** Tailwind CSS (apps import classes)
- **Build:** tsup for bundling
- **Storybook:** For component development

## Commands

```bash
pnpm --filter @repo/ui dev       # Storybook dev server
pnpm --filter @repo/ui build     # Build package
pnpm --filter @repo/ui test      # Run tests
```

## Component Guidelines

### Creating New Components

1. **Must be framework-agnostic** (works in Next.js and vanilla React)
2. **No app-specific logic** (no auth checks, no routing)
3. **Documented in Storybook**
4. **Fully typed with TypeScript**

### Component Template

```typescript
// src/components/Button.tsx
import { type ButtonHTMLAttributes } from 'react'

export interface ButtonProps extends ButtonHTMLAttributes {
  variant?: 'primary' | 'secondary' | 'outline'
  size?: 'sm' | 'md' | 'lg'
}

export function Button({
  variant = 'primary',
  size = 'md',
  children,
  ...props
}: ButtonProps) {
  return (
    
  )
}
```

### Exports

```typescript
// src/index.ts
export { Button, type ButtonProps } from './components/Button'
export { Card, type CardProps } from './components/Card'
// ... etc
```

## Anti-Patterns

- ❌ **DON'T** import from Next.js (`next/link`, `next/image`, etc.)
- ❌ **DON'T** use hooks that depend on app context (auth, routing)
- ❌ **DON'T** include app-specific business logic
- ❌ **DON'T** make network requests (components are pure UI)

## Testing

- Test components in isolation
- Use Testing Library
- Mock all external dependencies
- Aim for 80%+ coverage

How AI Uses the Hierarchy

When AI works in /apps/web/src/components/Header.tsx:

  1. Reads /CLAUDE.md for global monorepo rules
  2. Reads /apps/web/CLAUDE.md for web-specific conventions
  3. Combines both contexts, with more specific (web) overriding general (root)

When AI works in /packages/ui/src/Button.tsx:

  1. Reads /CLAUDE.md for global rules
  2. Reads /packages/ui/CLAUDE.md for UI package conventions
  3. Knows this is a shared component, so no app-specific code allowed

Teaching AI The Hierarchy

In each package's CLAUDE.md, start with: "Inherits: /CLAUDE.md" and "Extends: This file adds [package]-specific rules." This explicitly tells AI to read root config first, then apply local overrides.

Cross-Package Dependencies

Document how packages interact:

# /CLAUDE.md (root)

## Package Dependencies

**Dependency Rules:**
- Apps CAN depend on packages
- Packages CAN depend on other packages
- Apps CANNOT depend on other apps
- Packages SHOULD minimize cross-package dependencies

**Dependency Graph:**
```
apps/web → packages/ui → (no deps)
         → packages/types → (no deps)
         → packages/utils → (no deps)

apps/api → packages/types → (no deps)
         → packages/utils → (no deps)

packages/ui → packages/types ← (shared dependency)
```

**Adding Dependencies:**
```bash
# Add to specific workspace
pnpm --filter web add lodash
pnpm --filter @repo/ui add react

# Add to root (dev deps only)
pnpm add -D -w typescript
```

Avoiding Configuration Bloat

Root: Keep It Minimal

Root CLAUDE.md should be < 100 lines. Only include:

Package: Be Specific

Package CLAUDE.md should be focused on that package's concerns:

The Duplication Trap

Don't copy the same rules to every package config. If it applies to all packages, put it in root. If it only applies to React packages, maybe you need a `REACT.md` that multiple packages reference.

Alternative Patterns

Pattern: Shared Template Files

# Create shared config for common patterns
/.ai/
  templates/
    REACT_APP.md      # Template for React apps
    SHARED_PACKAGE.md # Template for shared packages

# Reference from package configs:
# /apps/web/CLAUDE.md
## Stack
See `/.ai/templates/REACT_APP.md` for base React app conventions.

**Web-specific additions:**
- Uses Clerk for auth
- Deploys to Vercel
- ...

Pattern: Config Composition

# /apps/web/CLAUDE.md

**Inherits (in order):**
1. `/CLAUDE.md` - Global monorepo rules
2. `/.ai/templates/REACT_APP.md` - React app conventions
3. This file - Web-specific overrides

## Web-Specific Overrides
[Only what's unique to web app]

Quick Reference

Monorepo Config Structure:

/CLAUDE.md                    # Global rules (< 100 lines)
/apps/web/CLAUDE.md           # Web app (inherits root)
/apps/api/CLAUDE.md           # API (inherits root)
/packages/ui/CLAUDE.md        # UI package (inherits root)
/.ai/templates/               # Optional: shared templates

Root CLAUDE.md Should Include:

Package CLAUDE.md Should Include:

Commands to Document:

# Root level:
pnpm install                  # Install all dependencies
pnpm dev                      # Run all apps in dev mode
pnpm build                    # Build all packages
pnpm test                     # Test all packages

# Package level:
pnpm --filter web dev         # Run specific workspace
pnpm --filter @repo/ui build  # Build specific package

Anti-Patterns to Avoid: