CLAUDE.md для Claude Code по наблюдениям Кэрпати
Один файл CLAUDE.md, который улучшает поведение Claude Code на основе наблюдений Андрея Кэрпати о типичных ошибках LLM при кодировании.
Что такое CLAUDE.md и зачем он нужен Claude Code
Что такое CLAUDE.md и зачем он нужен Claude Code
CLAUDE.md — это файл с инструкциями для Claude Code, который лежит в корне вашего проекта и читается моделью в начале каждой сессии. Он не заменяет системный промпт, а дополняет его проектным контекстом: стиль кода, архитектурные решения, правила тестирования и соглашения по именованию.
Андрей Кэрпати в своих наблюдениях за работой LLM с кодом отмечал, что модели часто склонны к поверхностным решениям: пишут функцию, которая работает изолированно, но не учитывает существующие абстракции в репозитории, или предлагают библиотеку, которая дублирует уже подключённую зависимость. CLAUDE.md фиксирует ожидания команды в одном месте, чтобы модель не гадала, а опиралась на соглашения.
В результате вы получаете предсказуемость: Claude Code предлагает код, который вписывается в ваш репозиторий, а не выглядит как чужеродное вставление. CLAUDE.md для проекта — это как инструкция по сборке мебели: без неё можно собрать что-то, но скорее всего с лишними деталями и кривыми полками.
Отличие от системного промпта
Системный промпт задаёт общие правила поведения модели — как она думает, какие ограничения соблюдает, как форматирует ответ. CLAUDE.md работает на другом уровне: он описывает конкретный проект, о котором системный промпт ничего не знает.
Во-первых, системный промпт одинаков для всех проектов. CLAUDE.md уникален для каждого репозитория.
Во-вторых, системный промпт не видит вашу кодовую базу. CLAUDE.md рассказывает модели, где лежит бизнес-логика, какие тесты запускать и как называются переменные.
С практической точки зрения разница выглядит так. Системный промпт может сказать модели: «Пиши чистый код на Python». CLAUDE.md уточняет: «Используйте type hints, именуйте переменные в snake_case, бизнес-логика выносится в app/services, а не в роутеры».
Пример содержимого CLAUDE.md:
markdown# CLAUDE.md
## Стиль кода
- Используйте type hints во всех публичных функциях.
- Именуйте переменные в стиле `snake_case`, константы — `UPPER_SNAKE_CASE`.
- Импорты сортируйте с помощью `isort`, форматируйте код через `ruff`.
## Архитектура
- Бизнес-логика живёт в `app/services`, не в роутерах FastAPI.
- Внешние API оборачиваются в адаптеры в `app/clients`.
## Тестирование
- Каждый новый эндпоинт покрывается тестом в `tests/api/`.
- Запускайте `pytest` перед коммитом.
Комментарий в коде объясняет, почему строка такая, а не пересказывает её. То же самое относится к CLAUDE.md: файл не дублирует системный промпт, он добавляет контекст, которого у модели нет по умолчанию.
Где файл должен лежать
Файл должен лежать в корне репозитория, рядом с .gitignore и README.md. Claude Code ищет его там при старте сессии. Если положить глубже — в docs/ или instructions/ — модель его не подхватит автоматически.
Структура типичного проекта:
text. ├── CLAUDE.md ├── README.md ├── .gitignore ├── pyproject.toml ├── src/ └── tests/
Если проект монолитный и состоит из нескольких компонентов, можно положить несколько CLAUDE.md — по одному на компонент. Claude Code подхватит ближайший к текущей рабочей директории. Для начала достаточно одного файла в корне.
CLAUDE.md не панацея. Он не исправит ошибки в логике и не заменит код-ревью. Но он снижает вероятность того, что модель предложит решение, которое работает в вакууме, но ломается в вашем репозитории.
Наблюдения Кэрпати о ловушках LLM при кодировании
Наблюдения Кэрпати о ловушках LLM при кодировании
Андрей Кэрпати в своих наблюдениях за работой больших языковых моделей при написании кода выделил несколько повторяющихся проблем. Они проявляются независимо от размера модели и особенно заметны при использовании Claude Code. Две из них критичны для настройки CLAUDE.md: чрезмерная уверенность модели и игнорирование контекста репозитория.
Чрезмерная уверенность модели
LLM не проверяет истинность того, что генерирует. Она предсказывает следующий токен, а не выполняет код в голове. В результате модель может выдать решение, которое выглядит безупречно, но содержит тонкую ошибку: несуществующий флаг команды, неверную версию зависимости или метод, который был удалён в новой версии библиотеки.
Это напоминает стажёра, который уверенно заявляет, что собрал сервер, но забыл подключить кабель питания. Всё стоит на месте, индикаторы горят, а система не стартует. Модель ведёт себя так же — она не сомневается, потому что у неё нет механизма сомнения.
Пример из практики. Модель предлагает добавить в Dockerfile строку:
dockerfileRUN apt-get update && apt-get install -y python3-pip && pip install some-package
Сборка пройдёт, но если в образе уже есть Python 3.11, а пакет требует 3.9, приложение упадёт при запуске. Модель не предупредит о конфликте, потому что она не запускала код — она только сгенерировала текст, который выглядит правдоподобно.
В CLAUDE.md я прописываю правило: модель должна явно помечать неопределённость. Если она не уверена в версии зависимости или в существовании API — она должна сказать об этом, а не выдумывать.
Пример инструкции для CLAUDE.md:
# CLAUDE.md
## Правила генерации кода
- Не предлагай код, если не уверен в точности API или версии зависимости.
- Если версия не указана в репозитории, укажи это явно и предложи проверить в package.json / requirements.txt / go.mod.
- Не выдумывай флаги командной строки. Если не уверен — предложи проверить `command --help`.
Это снижает количество ложных срабатываний и экономит время на отладке кода, который никогда не должен был быть написан.
Игнорирование контекста репозитория
Вторая ловушка — модель генерирует код, не глядя на существующий репозиторий. Она предлагает решение, которое технически верно, но не вписывается в проект: другой стиль именования, неиспользуемые паттерны, дублирование уже существующего функционала.
Это как нанять подрядчика на ремонт, который не смотрит на проект дома и ставит розетки там, где удобно ему, а не там, где нужны хозяевам. Розетки работают, но в стене появилось лишнее отверстие, а проводка не соответствует общему плану.
Пример. В репозитории уже есть утилита для чтения конфигурации:
gopackage config
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}
Модель, не зная об этом, предлагает в другом файле написать свою версию:
gofunc readConfig(path string) (Config, error) {
// собственная реализация, дублирующая config.Load
}
В CLAUDE.md я прописываю требование: перед предложением изменений модель должна изучить репозиторий, найти похожий код и переиспользовать его.
Пример инструкции:
# CLAUDE.md
## Работа с репозиторием
- Перед внесением изменений изучи существующий код в каталоге.
- Если функция с похожей логикой уже существует — переиспользуй её, а не пиши новую.
- Следуй стилю именования и структуре каталогов, принятым в репозитории.
- Не дублируй код из пакетов, которые уже подключены в go.mod / Cargo.toml / requirements.txt.
Это делает изменения предсказуемыми. Когда код пишется в рамках одного стиля, его проще читать, рефакторить и поддерживать.
В следующем разделе разберём, как перевести эти наблюдения в конкретные правила для CLAUDE.md.
Как настроить CLAUDE.md для своих проектов
Как настроить CLAUDE.md для своих проектов
CLAUDE.md — это не инструкция для пользователя, а системный промпт для Claude Code. Модель читает его перед началом работы и старается следовать описанным правилам. Наблюдения Андрея Кэрпати о типичных ошибках LLM при кодировании показывают, что модель чаще всего ошибается там, где нет чёткого контекста: выдумывает несуществующие API, игнорирует типы, пишет код без обработки ошибок и не до конца понимает структуру репозитория. Хороший CLAUDE.md закрывает эти пробелы ещё до того, как модель начнёт генерировать.
Ниже — пример минимального файла и правила, которые я сам прописываю в своих проектах.
Пример минимального файла
Скопируйте этот шаблон в корень репозитория и подправьте под свой стек. Я обычно начинаю с минимального набора и добавляю правила по мере работы — не стоит пытаться описать всё сразу.
markdown# CLAUDE.md
## Проект
- Стек: Python 3.11, FastAPI, PostgreSQL, Docker Compose
- Сборка и запуск: `docker compose up --build`
- Тесты: `pytest tests/`
## Стиль кода
- Пиши на Python 3.11, используй type hints во всех функциях
- Следуй PEP 8, длина строки — 88 символов
- Именование: snake_case для функций и переменных, PascalCase для классов
## Архитектура
- Бизнес-логика в `app/services/`, маршруты в `app/api/`, модели в `app/models/`
- Не смешивай слои: API-роутер не должен напрямую работать с БД
## Обработка ошибок
- Всегда оборачивай внешние вызовы в try/except с логированием
- Не возвращай сырые исключения клиенту, преобразуй в HTTPException с понятным detail
## Git
- Коммиты в формате conventional commits: feat, fix, docs, refactor
- Перед коммитом запусти `ruff check` и `pytest`
Этого достаточно для начала. Модель поймёт, какой стек используется, где лежит код и чего от неё ждать. Если в проекте есть особенности — например, кастомный мидлварь или нестандартная структура тестов — добавь отдельный пункт, иначе Claude Code будет гадать.
Какие правила стоит прописать
Я делю правила на четыре группы. Каждая закрывает конкретный класс ошибок, которые Кэрпати отмечал в работе LLM.
Контекст проекта
Укажи явно, что это за проект, какая у него цель и какие технологии используются. Без этого модель может предложить решение для другого стека или выдумать библиотеку, которой нет в зависимостях.
Пример:
markdown## Контекст
Это внутренний API для обработки платежей. Стек: Go 1.22, Gin, PostgreSQL.
Не предлагай решения на Node.js или Python, если не оговорено отдельно.
Соглашения по коду
Опиши стиль, именование и структуру папок. LLM склонна к хаотичному именованию и смешению подходов, если не дать ей шаблон.
Пример:
markdown## Стиль кода
- TypeScript, строгий режим, без `any`
- Компоненты React в `src/components/`, хуки в `src/hooks/`
- Именование файлов: kebab-case, компоненты — PascalCase
Обработка ошибок и краевые случаи
Одна из типичных ошибок LLM — написать «счастливый путь» и забыть про обработку ошибок. Пропиши это явно.
Пример:
markdown## Надёжность
- Все сетевые запросы — с таймаутом и повторными попытками
- Не игнорируй ошибки от базы данных, всегда логируй с stack trace
- Валидируй входные данные на границе системы, а не внутри бизнес-логики
Работа с репозиторием
Укажи, как запускать тесты, линтер и сборку. Это защищает от ситуации, когда модель предлагает код, который не проходит даже базовую проверку.
Пример:
markdown## Разработка
- Запуск тестов: `make test`
- Проверка типов: `make typecheck`
- Линтер: `golangci-lint run ./...`
Если в проекте есть Makefile или скрипты в package.json, сошли на них ссылку, а не дублируй команды. Модель и так читает репозиторий, ей достаточно знать, где смотреть.
С практической точки зрения, начните с минимального файла и добавляйте правила по мере работы. Если заметили, что Claude Code повторяет одну и ту же ошибку — добавьте правило против неё в CLAUDE.md. Это работает быстрее, чем объяснять модель в каждом новом чате.
Ограничения и когда подход не работает
Ограничения и когда подход не работает
CLAUDE.md — это не волшебная кнопка, а дополнение к системному промпту. Модель читает его в начале сессии, но это не гарантирует, что она будет следовать каждому правилу в каждой ситуации. Инструкции работают как рекомендации, а не как жесткие ограничения компилятора.
Во-первых, у модели есть контекстное окно. Если CLAUDE.md раздут до нескольких тысяч токенов, важные правила могут вытесниться длинными логами, выводом команд или историей переписки. С практической точки зрения, файл должен быть коротким — до 200–300 строк, иначе модель начнёт игнорировать хвост.
Во-вторых, инструкции в файле не отменяют обучающую выборку. Если в CLAUDE.md написано «всегда добавляйте type hints», а модель видит в репозитории сотни функций без аннотаций, она может решить, что локальная конвенция важнее вашего файла. Пример:
python# Модель сгенерирует так, несмотря на CLAUDE.md
def process_data(data):
result = []
for item in data:
result.append(item.transform())
return result
В-третьих, модель не может выполнить команду за вас. Она может посоветовать запустить тесты, но не запустит их сама и не увидит результат, пока вы не скопируете вывод обратно в чат. Инструкция «перед коммитом прогоняй pytest» остаётся пожеланием.
Наконец, конфликт инструкций. Если вы пишете в чате «сделай быстро, без тестов», а в CLAUDE.md прописано «тесты обязательны», модель часто выберет текущий запрос пользователя. Статический файл не перебивает прямой приказ в диалоге.
Когда инструкции не помогают
Даже краткий и грамотно написанный CLAUDE.md бессилен перед рядом паттернов поведения LLM.
Первый паттерн — выдуманные API. Модель может уверенно импортировать несуществующий метод из библиотеки, потому что такое сочетание имен часто встречается в обучающих данных. Никакое правило «используй только документированные функции» не остановит галлюцинацию, если модель не имеет доступа к актуальной документации в контексте.
Второй паттерн — игнорирование границ файлов. Вы просите отрефакторить одну функцию, а модель переписывает три смежных модуля, ломая импорты. CLAUDE.md с пунктом «не трогай лишнее» не помогает, потому что модель не видит последствий своих изменений до тех пор, пока вы не запустите тесты.
Третий паттерн — ложная уверенность. Модель может утверждать, что код рабочий, хотя не запустила его ни разу. Инструкция «проверяй код перед отправкой» не имеет смысла, если у модели нет инструментов для верификации.
Альтернативы CLAUDE.md
Если файл с инструкциями не спасает, стоит перенести правила из области «попросить модель» в область «заставить систему». Предсказуемость конфигурации достигается не просьбами, а автоматизацией.
Первая альтернатива — репозиторий как источник истины. Вместо того чтобы объяснять модели стиль кода, подключите линтер и форматтер:
yaml# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Теперь модель может предложить код с нарушениями, но pre-commit не пустит коммит дальше. Это работает даже если CLAUDE.md проигнорирован.
Вторая альтернатива — Makefile с целями проверки. Описываете один раз, запускаете всегда:
makefile.PHONY: check test lint
check: lint test
lint:
ruff check .
ruff format --check .
test:
pytest -q
Третья альтернатива — инструкции прямо в чате. Для разовой задачи проще написать «рефакторь эту функцию, добавь типизацию и комментарии к сложным участкам», чем править глобальный файл. Такие инструкции живут только в рамках сессии, но зато не конфликтуют с другими проектами.
Четвёртая — агентный подход с явными этапами: план → реализация → тесты → ревью. CLAUDE.md может описывать эти этапы, но реальная польза появляется, когда вы сами контролируете переход от одного к другому, а не полагаетесь на модель.
Итог простой: CLAUDE.md полезен для общих предпочтений, но не заменяет CI, линтеры и человеческую проверку. Если правило важно, оно должно быть в коде, а не только в маркдауне.
Часто задаваемые вопросы
Нужно ли отдельный CLAUDE.md для каждого репозитория?
Да, лучше заводить отдельный файл для каждого репозитория. Во-первых, CLAUDE.md описывает проектный контекст: где лежит бизнес-логика, какие тесты запускать и как называются переменные. Во-вторых, один глобальный файл не сможет учесть различия между проектами, и модель начнёт гадать.
Что писать в CLAUDE.md, если проект только начинается?
Начните с того, что уже знаете: стек технологий, правила именования, структура каталогов и команды для запуска тестов. По мере роста кодовой базы добавляйте наблюдения о повторяющихся ошибках модели. Пусть файл растёт вместе с проектом, а не пишется один раз навсегда.
Следует ли Claude Code правилам из CLAUDE.md безоговорочно?
Нет, это скорее сильная подсказка, а не жесткое ограничение. Модель может проигнорировать правило, если задача сформулирована нечётко, или предложить альтернативное решение. Поэтому вместо фразы «пишите чистый код» укажите «используйте type hints и именуйте переменные в стиле snake_case».
Как часто нужно обновлять CLAUDE.md?
Обновляйте, когда меняется архитектура, добавляются новые зависимости или появляются повторяющиеся ошибки модели. С практической точки зрения, если Claude Code начал предлагать код, который не вписывается в репозиторий, значит инструкции устарели. Рассматривайте файл как живой документ, который правится вместе с рефакторингом.
Можно ли использовать CLAUDE.md вместе с README.md и AGENTS.md?
Можно, но они решают разные задачи. README.md читают люди, AGENTS.md — другие агенты и инструменты, а CLAUDE.md — конкретно Claude Code в начале сессии. Не дублируйте одно и то же в трёх файлах, иначе модель запутается в противоречиях.