How to Modularize Team-Specific AI Rules with `Claude Code .claude/rules/` — A Separation Strategy for Frontend, Backend, and Security Teams
Have you ever experienced CLAUDE.md growing bloated as your team scales? I started out cramming all the rules into a single file, and at some point it crossed 500 lines and I thought, "Who on earth is going to maintain this?" When the frontend team modified React rules, it caused merge conflicts with the backend team, and whenever the security team wanted to add a new policy, we had to summon every PR reviewer.
The recently updated .claude/rules/ directory in Claude Code solves this problem quite cleanly. The key is to split rule files by team and use the paths: field in YAML frontmatter so that each rule is selectively loaded only when it's needed. By the end of this article, you'll have real-world rule files for three teams plus a CI validation pipeline in hand. Let me walk through how to design and operate this — including the pitfalls you'll encounter in practice.
Core Concepts
Two Loading Modes: Global vs. Conditional
.md files placed inside .claude/rules/ operate in two distinct ways.
.claude/
├── CLAUDE.md # Company-wide common rules
└── rules/
├── frontend.md # Conditionally loaded (has paths:)
├── backend.md # Conditionally loaded (has paths:)
├── security.md # Always loaded (no paths:)
└── testing.md # Conditionally loaded (has paths:)| File Type | paths: Frontmatter |
When Loaded |
|---|---|---|
| Global rule | Absent | Always, at session start |
| Conditional rule | Present | When Claude reads a matching glob-pattern file with the Read tool |
Glob Pattern: An expression like
src/app/**/*.tsxthat uses wildcards (*,**) to match file paths.**can skip any number of intermediate directories, making it flexible for deeply nested paths.
Frontmatter: A YAML metadata block wrapped in
---at the top of a Markdown file. It lets you embed structured information likepaths:andtitle:alongside the file content.
One important caveat here: a paths: condition is triggered when Claude reads a file at that path using the Read tool. There is a known limitation where it is not triggered by simply mentioning the filename in conversation or performing write operations with the Edit or Write tools (GitHub Issue #23478, unresolved as of May 2025). For rules that must be enforced on writes as well, the safe approach is to duplicate them in a global file.
The real value of conditional loading lies in token efficiency. When Claude reads src/api/users.ts, there's no need for React component rules to occupy context. This is the same pattern that Cursor implemented natively in .cursor/rules/*.mdc via the globs: field to great reception — Claude Code is now bringing it in natively.
Hierarchical Loading in Monorepos
In a monorepo environment, you can leverage the full hierarchy. Claude Code walks upward from the current working directory toward the root, cumulatively loading CLAUDE.md files along the way, and also traverses .claude/rules/ at each level.
my-platform/
├── CLAUDE.md # TypeScript strict, pnpm, Git conventions
├── .claude/
│ └── rules/
│ └── security.md # Company-wide security rules — always loaded
├── apps/
│ ├── web/
│ │ ├── CLAUDE.md # Next.js app common rules
│ │ └── .claude/
│ │ └── rules/
│ │ └── frontend.md # React/Next.js detailed rules
│ └── api/
│ ├── CLAUDE.md # NestJS app common rules
│ └── .claude/
│ └── rules/
│ └── backend.md # NestJS/API detailed rules
└── packages/
└── shared/
└── CLAUDE.md # Shared library rulesWhen working in apps/web/, rules stack in this order: root CLAUDE.md → root security.md → apps/web/CLAUDE.md → apps/web/.claude/rules/frontend.md, and the backend rules from apps/api/ are never loaded. Each team gets only its own context without interference from other teams' rules.
Practical Application
Writing Team-Specific Rule Files
Here are real-world examples of rule files for three teams commonly used in practice.
Frontend Team (frontend.md)
---
paths:
- "src/app/**/*.tsx"
- "src/components/**/*.tsx"
- "src/app/**/*.ts"
---
# Frontend Rules
- Use React Server Components by default; apply Client Components
only to the minimal unit that needs state or event handling
- All API calls must go through Server Actions (no client-side fetch)
- Prefer shadcn/ui components; custom components go in src/components/ui/
- Compose Tailwind CSS classes using the cn() utility
- Use named exports (no default exports)Backend Team (backend.md)
---
paths:
- "src/api/**/*.ts"
- "src/services/**/*.ts"
- "src/modules/**/*.ts"
---
# Backend Rules
- Business logic lives only in the Service layer; Controllers handle only request/response transformation
- All API responses must follow the standard { data, error, meta } shape
- DB migrations are managed exclusively under drizzle/migrations/
- Environment variables must be validated with a Zod schema in src/env.ts before use
- Use async/await; no .then() chainingSecurity Team (security.md) — always loaded, no paths:
# Security Rules (globally applied)
- SQL queries must always be parameterized (no raw string queries)
- User input is validated only at service boundaries (DTO + Zod)
- XSS prevention: dangerouslySetInnerHTML requires a security team review
- JWT tokens must be stored in httpOnly cookies, not localStorage
- Follow the latest OWASP Top 10 standards (especially supply chain failures and vulnerable dependency management)
- When adding external dependencies, attach CVE scan results to the PRSecurity rules must be in effect regardless of which file is being touched, so it's natural to place them globally without a paths: frontmatter. This location is also ideal for dropping in community-managed open-source security rule sets based on OWASP and NIST AI RMF, such as TikiTribe's claude-secure-coding-rules on GitHub.
Connecting a CI Validation Pipeline
Rule files can be put through a CI/CD pipeline just like code. This pipeline runs only on PRs that modify files under .claude/rules/, reducing unnecessary CI costs and automatically catching YAML frontmatter syntax errors.
# .github/workflows/validate-rules.yml
name: Validate Claude Rules
on:
pull_request:
paths:
- ".claude/rules/**"
- "**/CLAUDE.md"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: YAML frontmatter syntax check
run: |
pip install pyyaml -q
python3 - << 'PYEOF'
import sys, re, yaml, glob
errors = []
for filepath in glob.glob('.claude/rules/**/*.md', recursive=True):
content = open(filepath).read()
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if match:
try:
yaml.safe_load(match.group(1))
print(f'OK: {filepath}')
except yaml.YAMLError as e:
errors.append(f'ERROR: {filepath} — {e}')
if errors:
for e in errors:
print(e, file=sys.stderr)
sys.exit(1)
PYEOF
- name: File size warning
run: |
echo "Rule file line counts:"
wc -l .claude/rules/*.md
for file in .claude/rules/*.md; do
lines=$(wc -l < "$file")
if [ "$lines" -gt 200 ]; then
echo "WARNING: $file — ${lines} lines (exceeds recommended 200)"
fi
doneHonestly, at first I thought, "Do we really need CI for rule files?" But once a team grows past five people, someone will inevitably drop a YAML quote or quietly sneak in a duplicate rule. Automating this noticeably reduces code review overhead.
Pros and Cons
Advantages
| Item | Description |
|---|---|
| Separation of concerns | Team rules are split into independent files, minimizing merge conflicts |
| Context efficiency | Glob patterns load only relevant rules, preventing token waste |
| Independent ownership | Each team manages only their own file via PR, with no interference from others |
| Incremental adoption | Can be introduced one file at a time while keeping the existing CLAUDE.md |
| Git history tracking | Rule changes are recorded in PRs alongside code changes |
Drawbacks and Caveats
| Item | Description | Mitigation |
|---|---|---|
| YAML parsing errors | Globs starting with { or * cause YAML errors (Issue #13905, #17204) |
Always wrap patterns in double quotes |
| Write trigger not supported | paths: rules only trigger on the Read tool, not on write operations (Issue #23478, unresolved as of May 2025) |
Duplicate critical write rules in the global file |
| User-level rules not applied | Known bug where paths: is ignored in ~/.claude/rules/ (Issue #21858) |
Write global rules directly in ~/.claude/CLAUDE.md |
The Most Common Mistakes in Practice
Here are the three mistakes most often seen when introducing this structure to a team.
-
Omitting quotes from glob patterns — Writing
paths: - src/app/**/*.tsxwithout quotes causes the YAML parser to interpret*as a special character and fail silently. Always wrap patterns in double quotes like"src/app/**/*.tsx". Connecting the CI pipeline above will catch this mistake automatically. -
Making company-wide security rules conditional — Security policies must apply regardless of which file is being worked on. If they're not in a global file that loads unconditionally without
paths:, security rules can be absent when Claude creates new files or performs write operations. -
Not designating owners for rule files — It's tempting to think "the whole team will manage it," but in practice nobody ends up managing it. Explicitly stating the responsible team and a monthly review schedule in each file ensures the rules can keep pace with codebase changes.
Closing Thoughts
.claude/rules/ is the most practical way to let teams own AI rules like code and evolve them through Git. You don't need to build a perfect structure from day one. Just extracting one file for the team that's hurting the most right now is enough.
One more thing worth adding — once this structure stabilizes, the next bottleneck won't be the rule files themselves, but how often the team actually reviews them. In the long run, a monthly 30-minute team ritual to inspect the rules makes more difference than a perfectly crafted rule file.
Three steps you can take right now:
-
Split team-specific rules out of the existing
CLAUDE.md: Runmkdir -p .claude/rules, then extract frontend-related content intofrontend.md. Double quotes are mandatory for glob patterns in the frontmatter at the top of the file. -
Isolate security rules into
security.md: Place it as a globally loaded file withoutpaths:. Starting from TikiTribe's open-source security rule set lets you apply production-level guidelines immediately without writing them from scratch. -
Add a rule validation step to GitHub Actions: Paste the example pipeline above into
.github/workflows/validate-rules.yml. YAML parsing error detection and the 200-line warning alone will be a significant help as your team grows.
References
- Claude Code Official Memory Docs — rules directory explained
- Claude Code Rules Directory: Modular Instructions That Scale — claudefa.st
- Modular Rules in Claude Code: Organizing Project Instructions with .claude/rules/
- Claude Code Configuration Blueprint: The Complete Guide for Production Teams — DEV Community
- Claude Code in Monorepos: Hierarchical CLAUDE.md and Package-Scoped Instructions — DEV Community
- Claude Code Gets Path-Specific Rules (Cursor Had This First) — paddo.dev
- GitHub Issue #13905: Invalid YAML syntax in .claude/rules frontmatter
- GitHub Issue #17204: paths frontmatter format incorrect
- GitHub Issue #23478: Path-based rules not loaded on Write tool
- Claude Secure Coding Rules: Open Source Security That Scales — Rock Cyber Musings
- GitHub — TikiTribe/claude-secure-coding-rules
- OWASP AISVS Official Documentation
- Best Practices for Claude Code — Official Documentation