Хороший CLAUDE.md — не README. В нём инварианты, которые Claude не выведет из кода. 6 обязательных пунктов, 4 ненужных, 5 вопросов.
В моём проекте Pickful есть децентрализованная система сообществ-жюри, крипто-платежи x402, Sign-In with Ethereum, multi-database, push в реальном времени — сплошь стеки, появившиеся за последние пару лет. Claude пишет эти фичи быстро и чисто.
Но откройте CLAUDE.md проекта и увидите: система жюри и x402 — этих слов нет ни разу.
Это не упущение. Роль CLAUDE.md никогда не была «описывать функции». Она в том, чтобы фиксировать то, что Claude никогда не выведет, просто читая код.
Тот, кто пишет CLAUDE.md впервые, часто относится к нему как к README — описывая каждую ключевую функцию:
Такое содержимое Claude, открыв topic_review_service.rb / x402.rb / like_points_service.rb, читает точнее, чем вы это опишете. Тысячу слов описания бизнес-логики Claude считывает из кода за пару сотен токенов — без интерпретационного сдвига. Код — это факт. Описание — сведения из вторых рук.
По-настоящему Claude спотыкается о следующие 6 категорий.
В CLAUDE.md Pickful есть такие строки:
Propshaft (not Sprockets)
ImportMap (no JavaScript bundler)
Hotwire: Turbo Frames, Turbo Streams, Stimulus
Lexxy gem overrides ActionText:
config.lexxy.override_action_text_defaults = false
Каждая строка идёт против догадки по умолчанию. Увидев Rails-проект, Claude по умолчанию предполагает:
Без этих строк в CLAUDE.md, попросив Claude добавить новую JS-фичу, с высокой вероятностью получите установку Webpacker, правки package.json, конфиг бандлера — всё неверно, и неверно тихо (приложение работает, но пайплайн ассетов загрязнён).
Эти строки в CLAUDE.md говорят Claude: не гадай, уже решено.
PostgreSQL with 4 separate databases:
- primary - Main application data
- cache - Solid Cache storage
- queue - Solid Queue jobs
- cable - Action Cable subscriptions
Просто сформулировано — но может спасти целую ночь. Мульти-DB по умолчанию в Rails 8 — новое поведение, Claude по своей воле не проверит, сколько у вас баз. Безобидно выглядящая миграция, уехавшая не в ту базу, в dev не даёт ошибки (все четыре — PostgreSQL, schema мигрирует где угодно). Но в проде таблица job из Solid Queue попадает в бэкап primary, либо модель primary запрашивает cache-базу — баги, которые неделями всплывают.
Две строки в CLAUDE.md против целого дня debugging прода.
/p-{slug} - Short post URLs (4-5 char alphanumeric)
/t-{slug} - Topic URLs (3-4 char alphanumeric)
/s-{code} - Short URL redirects (3-4 char alphanumeric)
/r-{referral} - Referral links
Маршруты Claude видит в routes.rb, но соглашения о длине (4-5 символов, 3-4 символа) зарыты в slug-генерации моделей или сервисов. Попросите Claude добавить новый тип короткой ссылки — скорее всего он сгенерирует 6-символьный slug, UUID-подобный или чисто цифровой — вне визуального языка всей системы.
Характер этих «соглашений»: их нарушение не даёт ошибку, но следующий читатель кода почувствует — что-то не так. Писать обязательно.
VIP status at 400+ points
Posts with 15+ likes are "hot" posts
Оба числа живут где-то в коде (User#vip?, scope Post#hot?). Проблема: когда Claude трогает что-то соседнее — правит награды в очках, добавляет уведомление «почти VIP», пишет cron для закрепления hot-постов — он не выровняет пороги в других местах автоматически.
Итог: за задачу вы даёте 500 очков, а копия говорит «можешь стать VIP» (400 достаточно); или сидируете данные для новой фичи с малым числом лайков и порог 15 не пересекается никогда.
Кодовые способности Claude сильны, но системного числового чутья у него нет. Помещение ключевых порогов в CLAUDE.md приводит к тому, что каждая беседа стартует со знанием: «400 и 15 — особые числа».
- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules
Задача этой строки — навигация, не описание.
Без неё, когда Claude надо добавить новый контроль прав, три возможности:
unless current_user.admin? прямо в контроллереauthorize? в моделиСо строкой «Pundit policies in app/policies/» Claude каждый раз идёт прямо в app/policies/ добавлять policy-файл — стиль единообразен.
Одна строка избавляет Claude от «детективной работы» каждый раз.
Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers
Добавляя новую фичу, defaults Claude такие:
А вашему проекту реально нужно:
Нарушение этих «внешних ограничений уровня проекта» плодит мелкую доделку — переводы добирать, тесты переписывать. Запись в CLAUDE.md прибивает раз и навсегда «что надо делать каждый раз».
Столь же важно, как «надо написать» — «не писать». Категории ниже — увидели = удалили:
1. Описание функций
«Система жюри: пользователи могут жаловаться на контент-нарушение, пожалованный идёт на публичное голосование, присяжные выбираются из...»
→ Claude откроет topic_review_service.rb и прочтёт точнее, чем вы написали. Засовывать это в каждую новую беседу — чистая трата.
2. То, что мгновенно читается из дерева каталогов / Gemfile
«app/models/ содержит модели ActiveRecord», «Rails 8», «БД — PostgreSQL»
→ Claude бросит взгляд на корень проекта и Gemfile — и знает.
3. Общие программистские знания
«Controllers should be thin, delegate to services», «Избегайте N+1», «Пишите тесты на основные фичи»
→ Это уже в тренировочных данных Claude. Пишите это, только если ваш проект необычен — например «мы принципиально не используем service layer; логика живёт в контроллерах».
4. Контекст текущей задачи
«Сейчас мы рефакторим платёжную систему; фокус на...»
→ Это контекст беседы, не факт проекта. Сунув в CLAUDE.md, загрязните все остальные беседы.
Доказательство «я могу это сделать» — перед проповедью. После написания секции выше я прогнал CLAUDE.md Pickful — 238 строк — через 5 вопросов. Результат: примерно половина — трата.
Что резать (~120 строк):
Большая часть блока dev-команд (70 строк → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman — всё стандартные Rails-команды, уже в тренировке Claude. Оставить три специфичных для проекта: bin/jobs (worker Solid Queue), bin/importmap pin (специфичен для ImportMap), bin/kamal deploy.
Список Core Domain Models (35 строк → убрать целиком): перечислить 20 моделей с ролями — классический образец «CLAUDE.md в стиле README» — Claude запустит ls app/models/ или прочтёт один файл модели и узнает. Совать в каждую беседу — чистая трата.
Стандартные пункты Tech Stack (28 строк → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search — всё мгновенно читается из Gemfile. Оставить только контр-интуитивные: Propshaft / ImportMap / Lexxy / x402-rails / Grover.
Разбросанные общие программистские банальности: «Controllers should be thin», «Use app/jobs/ for async processing», что тестируют request specs vs. model specs — Claude делает это по умолчанию. Лишнее жжение токенов.
Остаётся ~100 строк: соглашения URL-роутинга, разделение 4 DB, пороги VIP 400 / Hot 15, override Lexxy, анти-default архитектурные решения, указатель Pundit, список locale, FactoryBot (не fixtures).
Но важнее: каких инвариантов ещё нет?
Во время аудита я осознал несколько пунктов, которые надо было добавить и не добавил:
238 строк сжать до 100–120, плюс 5–10 строк ранее упущенных инвариантов — это ближе к «правильной плотности» CLAUDE.md.
Это не туториал. Это аудит моего собственного проекта — и я тоже написал неправильно. Правильная форма CLAUDE.md — постоянно удалять и постоянно добавлять — любой проект, чуть подзревший, должен иметь всё более короткий и плотный CLAUDE.md.
Каждый раз, прежде чем что-то добавить в CLAUDE.md, прогоняю этот чек-лист:
Правила, прошедшие все 5, остаются; на одно-без-ответа — удалить или переписать.
Правильное использование CLAUDE.md — это не «презентация проекта», а сжатие скрытого знания между вами и кодовой базой, которое Claude никогда не восполнит чтением кода — необычные решения, невидимые пороги, соглашения против defaults, внешние ограничения уровня проекта.
Каждая добавленная строка должна отвечать на вопрос: «Это то, что Claude не прочитает из кода?» Не прочитает — оставить. Прочитает — удалить.
CLAUDE.md, написанный так, обычно меньше половины черновика, но на каждую беседу ценнее, чем тысячи слов описания функций. И он всегда «ещё не закончен» — каждая новая выпущенная фича подсвечивает очередной инвариант, который должен был быть внутри, и очередной абзац, который теперь можно вырезать. Удалять и добавлять, удалять и добавлять — это ежедневное обслуживание CLAUDE.md.