Структурирование проектов Claude Code, чтобы агенты могли сосредотачиваться в крупных репозиториях
Шесть подходов для крупных кодовых баз: многоуровневые файлы CLAUDE.md, навыки, субагенты, правила отклонения, разреженные деревья задач и плагины языкового сервера, которые сохраняют чистоту контекста агента.
Источники: How I Structure Claude Code Projects So Agents Don’t Get Lost in Large Codebases
Представьте простую просьбу к Claude Code: исправить ошибку таймаута сессии в промежуточном компоненте аутентификации. На самом деле для её устранения достаточно трёх строк кода. Однако прежде чем добраться до них, агент обходит систему логирования, историю миграций, половину точек входа API и, по причинам, которые никто не может объяснить, конфигурации Terraform. После прочтения 147-го файла разработчик уже собирается нажать клавишу Escape.
В подобной ситуации виновником не является сама модель — виноват структурированный порядок проекта. Все правила, команды сборки и архитектурные решения были собраны в один корневой файл CLAUDE.md объёмом 340 строк, который агент верно читал при запуске, тратя остаток своих ресурсов на изучение кода, не связанного с аутентификацией.
Вот что происходит, когда с агентом обращаются как с новым сотрудником, который должен прочитать весь руководство компании, прежде чем исправить опечатку. Ему нужен правильный контекст в нужный момент, а не всё сразу. Далее приведены шесть способов решения этой проблемы, а также критерии для определения того, когда стоит использовать каждый из них.
Почему контекст быстро исчерпывается
Окно контекста Claude Code, составляющее примерно 200 000 токенов на используемой здесь модели, кажется огромным, пока вы не узнаете, что снижает его объем: каждое отправленное сообщение, каждый прочитанный файл, весь вывод команд, каждая инструкция в формате CLAUDE.md и любые автоматически сохранённые заметки. Один токен соответствует примерно четырем символам.
В крупной базе кода 50 000 токенов могут исчезнуть ещё до того, как будет написана хотя бы одна строка кода. Файл CLAUDE.md с 300 строками занимает примерно 1 200 токенов, файл из 500 строк — ещё 2 000, а поиск с помощью grep или find увеличивает этот показатель. К тому моменту, когда агент поймёт задачу, половина временного окна может уже исчезнуть. По мере его заполнения производительность снижается: ранее данное указания забываются, файлы перечитываются, и появляются ошибки, которых не было бы при наличии чистого контекста. Большие окна — не решение; ключ к успеху — исключение нерелевантных данных из окна.
Шаблон 1: многоуровневые файлы CLAUDE.md
Первая ошибка — собирать всё в один корневой файл: команды сборки для API, шаблоны фронтенд-компонентов, правила миграции, чек-листы развертывания и правила тестирования вместе.
При запуске сессии Claude Code читает файлы CLAUDE.md из рабочей директории вверх до корня репозитория. Если запустить его в поддиректории, он будет читать файлы этой папки, а также все файлы родительских папок вплоть до корня. Поэтому разделяйте контент по области применения:
monorepo/
CLAUDE.md # repository-wide rules only
packages/
api/
CLAUDE.md # API-specific conventions
.claude/skills/
src/
web/
CLAUDE.md # frontend-specific conventions
.claude/skills/
src/
shared/
CLAUDE.md # shared library conventions
src/
Файл корня содержит лишь структуру репозитория и правила создания коммитов — примерно 40 строк, — а каждый пакет имеет собственные детали, связанные с его технологической стеком. Запустив агента в папке packages/api/, он загружает файл корня вместе с файлом API, не учитывая правила фронтенда. При переходе в папку packages/web/ та же логика предоставляет другой набор информации.
В файле корня может быть следующий текст:
# Repository Structure
This is a monorepo with three packages under packages/:
- packages/api: Node.js REST API with Express, TypeScript, PostgreSQL
- packages/web: React frontend with Vite, TypeScript, TailwindCSS
- packages/shared: shared TypeScript utilities
Run commands from the package directory, not the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.
# Commit Conventions
- Prefix commits with the package name: "api: fix session timeout"
- One commit per logical change
- Run tests before committing
Пакет API содержит только то, что действительно важно внутри него:
# API Package
This is the REST API server.
## Commands
- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: copy `.env.example` to `.env`
## Code Patterns
API routes are in src/routes/. Each route file exports an Express router.
Database queries use Knex in src/db/. Never write raw SQL in route handlers.
## Testing
Tests are in src/__tests__/ mirroring the src/ directory.
Use supertest for HTTP assertions, not raw fetch.
Always wrap database tests in a transaction that rolls back.
В этом примере вместо 340 строк загружаемого контекста используется 80 строк, а для устранения проблем с аутентификацией требуется прочитать 12 файлов вместо 147.
Шаблон 2: навыки для получения знаний по запросу
Даже многоуровневая структура может привести к избыточности. Шаблоны тестирования, списки проверки перед развертыванием и руководства по отладке являются процедурами, а не фактами; агенту они не нужны при запуске, а только тогда, когда задача этого требует. Именно для этого и существуют навыки.
Навыки хранятся в папке .claude/skills/ и загружаются тогда, когда Claude считает их релевантными, или при прямом вызове с помощью команды с слэшем. В описании навыка указано, когда им следует пользоваться, а его содержимое содержит сам фактический материал. В данном случае руководство по тестированию перемещается из файла API в отдельный навык:
# .claude/skills/api-testing/SKILL.md
---
name: api-testing
description: Testing patterns for the API package. Use when writing or modifying tests in packages/api/.
---
## Test Structure
Tests are in `src/__tests__/` mirroring the `src/` directory structure.
Each route file has a corresponding `.test.ts` file.
## Running Tests
- All tests: `npm test`
- Single file: `npm test -- src/__tests__/routes/users.test.ts`
- Watch mode: `npm test -- --watch`
## Test Utilities
- `src/__tests__/helpers/db.ts`: provides `setupTestDb()` and `teardownTestDb()`
- `src/__tests__/helpers/auth.ts`: provides `createTestUser()` and `getAuthToken()`
## Patterns
- Use `supertest` for HTTP assertions, not raw fetch
- Always wrap database tests in a transaction that rolls back
- Mock external services in `src/__tests__/mocks/`
Попросите провести тестирование мидлвэра аутентификации — агент загрузит навык тестирования и будет следовать его шаблонам. Попросите его исправить проблему с таймаутом, но навык останется незагруженным, поскольку тестирование здесь неактуально.
Навыки также подходят для повторяемых рабочих процессов. Навык, вызываемый через /fix-pr-comments, может считывать комментарии к ревью в GitHub-пул-реквесте, определять необходимые изменения, редактировать код, запускать тесты и делать пуш. Весь процесс находится внутри навыка, а не в файле CLAUDE.md, который с каждой сессией потребляет дополнительный контекст.
Ориентир: факты, необходимые в каждой сессии, помещаются в CLAUDE.md, тогда как процедуры и справочные материалы превращаются в навыки.
Шаблон 3: сабагенты для изолированного исследования
Даже с использованием слоев и специальных навыков процесс исследования становится слишком обременительным. Вопрос «Как работает аутентификация?» может привести к чтению 40 файлов, поскольку агент анализирует процессы входа в систему, хранения сессий, обновления токенов и выхода из системы, причем весь этот текст остается в основном диалоге после ответа на вопрос, загрязняя остальную часть сессии.
Подагент работает в собственном окне контекста, выполняет исследования и возвращает только краткое резюме. Данные о прочитанных файлах и результатах команды grep остаются в его контексте. Основной поток получает резюме, что сохраняет чистоту диалога. Запрос на исследование может выглядеть следующим образом:
Use a subagent to investigate how our authentication system handles
session timeout and token refresh. Report back what files are involved
and how the flow works.
Подагент читает всё необходимое, отслеживает ход событий и докладывает об этом, так что основной диалог получает чёткое объяснение примерно из 200 слов, а не содержимое 40 файлов. Подагенты также помогают при проверке после реализации:
Use a subagent to review the session timeout fix for edge cases
and consistency with our existing auth patterns.
Подагент, выполняющий проверку, видит свежий вариант с изменениями, но не видит обоснование, приведшее к ним.
Одно предупреждение: краткие сводки теряют детали. Если вам нужны точные номера строк или фрагменты кода, основной агент, возможно, будет вынужден перечитать файлы, которые уже рассматривал подагент. Подагенты отвечают за основной процесс реализации, в то время как исследования происходят в других местах.
Шаблон 4: сохранение нечитанными генерированных и приобретенных файлов
Все, что указано в файле .gitignore, по умолчанию игнорируется, что позволяет исключить папки node_modules/, dist/ и build/ из результатов поиска. Однако некоторый генерированный или приобретенный код все равно сохраняется: скомпилированные определения protobuf, приобретенные SDK, генерированные типы GraphQL. Агент с радостью откроет эти файлы, если они появятся в результате поиска, хотя этого делать никогда не следует.
Правила отказа в доступе блокируют чтение выбранных шаблонов файлов. Общая структура приведена ниже, а точная синтаксис должна быть проверена в документации к вашей версии Claude Code:
# packages/api/.claude/settings.json
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
Когда поиск по запросу «GraphQL resolvers» находит сгенерированный файл схемы, результат действительно отображается, но инструмент Read возвращает ошибку «доступ запрещен», и агент обычно спрашивает, где находится настоящий источник кода. Следует знать о побочном эффекте: те же правила мешают выполнению запросов на чтение, сделанных вручную в окне чата. Если попросить его открыть файл src/schema.generated.ts, запрос будет отклонён; в таком случае следует временно убрать соответствующее правило. Преимущество заключается в том, что сгенерированный «шум» не попадает в контекст, а агент направляется к настоящему источнику кода.
Шаблон 5: разреженные worktrees
Claude Code может создавать git worktrees для параллельной или изолированной работы, причём по умолчанию каждый из них загружает весь репозиторий. В монорепозитории это неэффективно, поскольку для исправления API не требуется фронтенд.
Механизм sparse-checkout в Git записывает на диск только выбранные директории, оставляя остальные в индексе, а параметр worktree.sparsePaths работает на основе этого принципа. Проверьте значение этой настройки в документации к вашей версии:
# packages/api/.claude/settings.json
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
]
}
}
В таких деревьях проектов хранятся только папки .claude/, packages/api/ и packages/shared/, а также корневые файлы вроде package.json и tsconfig.base.json. Их легче создавать, и они занимают меньше места на диске. Если агент пытается прочитать файл, который не был извлечен, git выдает ошибку, и агент обычно спрашивает, какие директории существуют. Для режима разреженного извлечения требуется git 2.25 или новее, что можно проверить с помощью команды git --version.
Шаблон 6: интеллект кода вместо сканирования файлов
Чтобы найти место, где определена или используется функция в большом кодовом базисе, необходимо выполнить множество вызовов команды grep и прочитать множество файлов, поскольку агент анализирует результаты, открывает потенциальные варианты и продолжает поиски.
Плагины интеллекта кода соединяют агента с сервером языка. Вместо сканирования они запрашивают, где определён refreshAuthToken, и сразу получают файл и номер строки. Для проектов TypeScript официальный плагин LSP устанавливается таким образом, хотя синтаксис установки может отличаться в зависимости от версии, поэтому проверьте документацию:
/plugin install typescript-lsp@claude-plugins-official
Задайте вопрос о том, где используется refreshAuthToken, и агент обращается к серверу языка, получая в ответ:
src/middleware/auth.ts:47
src/routes/users.ts:103
src/__tests__/auth.test.ts:22
Не требуется чтение файлов, поэтому ответы появляются быстрее, а контекст остается чистым. Плагин также выявляет ошибки типов после внесения изменений без запуска tsc, так что ошибки сразу становятся заметными. Он хорошо сочетается с правилами отклонения: они блокируют ненужный шум, а функция анализа кода сокращает необходимость сканирования оставшегося контента.
Все шесть компонентов вместе
При применении каждого шаблона процесс исправления исходной ошибки выглядит примерно так:
- Запускаем Claude из папки
packages/api/. - Он загружает основной файл
CLAUDE.md(40 строк) и файл API (50 строк). - Мы просим его исправить проблему истечения времени сессии в модуле аутентификации.
- Он отправляет вспомогательного агента для анализа процесса аутентификации, который в своем контексте читает примерно 20 файлов.
- Вспомогательный агент сообщает, что логика истечения времени находится в файле
src/middleware/auth.tsна строке 47, и используется неверный оператор сравнения. - Основной разработчик читает этот файл, исправляет баг, пишет тест и запускает его, изменяя примерно четыре файла в основном контексте.
На этом примере до и после показаны 147 операций чтения файлов, три неудачных попытки и 35 минут в сравнении с примерно четырьмя операциями чтения, одной успешной попыткой и 6 минутами. Рассматривайте это как анекдот одного разработчика, а не как критерий оценки, но важен сам направление: такая структура устраняет препятствия между вопросом и ответом.
Выбор необходимого
Не каждому проекту нужны все шесть элементов. Один репозиторий с 50 файлами не требует слоистой структуры файлов или разреженных рабочих деревьев. Как только объем кодовой базы превышает то, что один человек может запомнить, эти элементы начинают приносить пользу:
- Многоуровневые файлы
CLAUDE.mdподходят, когда вы прокручиваете страницу мимо нерелевантных разделов в основном файле или когда коллеги спрашивают, почему файл API содержит правила фронтенда. - Навыки подходят, когда раздел
CLAUDE.mdпревращается в процедуру или чек-лист, или когда вы постоянно вставляете одни и те же инструкции в чат. - Подагенты подходят, когда расследование заканчивается требованием «прочитать эти 30 файлов» или когда вам нужен обзор без предвзятости.
- Правила отклонения подходят, когда агент продолжает открывать сгенерированный или приобретенный код вопреки файлу
.gitignore. - Редкие рабочие деревья подходят, когда монорепозиторий занимает более 10 секунд на развертывание или когда вы создаете множество рабочих деревьев одновременно.
Цель не в том, чтобы слепо применять всё. Речь идёт о том, чтобы замечать, когда контекст становится перегруженным, и выбирать подход, который его очищает. Другие схемы также могут подходить для крупных кодовых баз, однако эти шесть способов превращают агента, который читает всё, в агента, который читает нужные файлы, вносит исправления и прекращает работу.