Free

Хороший CLAUDE.md не описывает функции — он фиксирует только то, что Claude не видит при чтении кода

Хороший 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 — описывая каждую ключевую функцию:

  • «Система жюри позволяет пользователям жаловаться на контент; пожалованный идёт на публичное голосование, присяжные выбираются из...»
  • «Платежи x402 запускают on-chain переводы через HTTP-статус 402...»
  • «Лайки дают очки; при 400 очках пользователь становится VIP...»

Такое содержимое Claude, открыв topic_review_service.rb / x402.rb / like_points_service.rb, читает точнее, чем вы это опишете. Тысячу слов описания бизнес-логики Claude считывает из кода за пару сотен токенов — без интерпретационного сдвига. Код — это факт. Описание — сведения из вторых рук.

По-настоящему Claude спотыкается о следующие 6 категорий.

6 категорий, которые действительно спасают

1. Контр-интуитивные архитектурные решения

В 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 по умолчанию предполагает:

  • Статические ассеты идут через Sprockets (инерция старых проектов)
  • JS через Webpacker или esbuild
  • Фронт — React или смесь Stimulus + Turbo
  • Rich text — родной ActionText

Без этих строк в CLAUDE.md, попросив Claude добавить новую JS-фичу, с высокой вероятностью получите установку Webpacker, правки package.json, конфиг бандлера — всё неверно, и неверно тихо (приложение работает, но пайплайн ассетов загрязнён).

Эти строки в CLAUDE.md говорят Claude: не гадай, уже решено.

2. Разделение multi-database

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 прода.

3. «Невидимые соглашения» URL-роутинга

/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-подобный или чисто цифровой — вне визуального языка всей системы.

Характер этих «соглашений»: их нарушение не даёт ошибку, но следующий читатель кода почувствует — что-то не так. Писать обязательно.

4. Захардкоженные бизнес-пороги

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 — особые числа».

5. Указатель auth/authz стека

- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules

Задача этой строки — навигация, не описание.

Без неё, когда Claude надо добавить новый контроль прав, три возможности:

  • Пишет unless current_user.admin? прямо в контроллере
  • Раскапывает остатки CanCan, уже не используемые
  • Придумывает свой метод authorize? в модели

Со строкой «Pundit policies in app/policies/» Claude каждый раз идёт прямо в app/policies/ добавлять policy-файл — стиль единообразен.

Одна строка избавляет Claude от «детективной работы» каждый раз.

6. Внешние ограничения уровня проекта

Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers

Добавляя новую фичу, defaults Claude такие:

  • Только английские строки
  • Тесты на Minitest + fixtures (default Rails)

А вашему проекту реально нужно:

  • Переводы на 3 locale
  • RSpec + FactoryBot, не fixtures

Нарушение этих «внешних ограничений уровня проекта» плодит мелкую доделку — переводы добирать, тесты переписывать. Запись в 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 тоже можно сократить вдвое

Доказательство «я могу это сделать» — перед проповедью. После написания секции выше я прогнал 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).

Но важнее: каких инвариантов ещё нет?

Во время аудита я осознал несколько пунктов, которые надо было добавить и не добавил:

  • Числа, спрятанные в системе жюри (порог голосования, критерии пригодности присяжного) — в CLAUDE.md не засвечены вовсе
  • Если у x402 есть chain id, адрес контракта, обязательные env var — без этого Claude не пойдёт grep-ать конфиг и придумает значения
  • Особые ограничительные правила в сервисах point-trading / Referral

238 строк сжать до 100–120, плюс 5–10 строк ранее упущенных инвариантов — это ближе к «правильной плотности» CLAUDE.md.

Это не туториал. Это аудит моего собственного проекта — и я тоже написал неправильно. Правильная форма CLAUDE.md — постоянно удалять и постоянно добавлять — любой проект, чуть подзревший, должен иметь всё более короткий и плотный CLAUDE.md.

5 вопросов перед внесением правила

Каждый раз, прежде чем что-то добавить в CLAUDE.md, прогоняю этот чек-лист:

  1. Может ли Claude вывести правило, прочитав 3 файла? Может — не пишите, пусть читает.
  2. Правило контр-интуитивно? (Необычные пороги, немейнстримный выбор библиотеки, конфиг против upstream-defaults.) Да — обязательно писать.
  3. Это инвариант уровня codebase, или влияет на один файл? Один файл — в комментарий к коду, не в CLAUDE.md.
  4. Нарушение приведёт к тихой ошибке Claude? (Без ошибки, но семантика неверна — не та БД, пропущенный перевод, обойдённая policy.) Да — обязательно.
  5. Можно объяснить в 3 строках? Нет — вы сами ещё не оформили мысль. Пока не пишите.

Правила, прошедшие все 5, остаются; на одно-без-ответа — удалить или переписать.

Итог в одну строку

Правильное использование CLAUDE.md — это не «презентация проекта», а сжатие скрытого знания между вами и кодовой базой, которое Claude никогда не восполнит чтением кода — необычные решения, невидимые пороги, соглашения против defaults, внешние ограничения уровня проекта.

Каждая добавленная строка должна отвечать на вопрос: «Это то, что Claude не прочитает из кода?» Не прочитает — оставить. Прочитает — удалить.

CLAUDE.md, написанный так, обычно меньше половины черновика, но на каждую беседу ценнее, чем тысячи слов описания функций. И он всегда «ещё не закончен» — каждая новая выпущенная фича подсвечивает очередной инвариант, который должен был быть внутри, и очередной абзац, который теперь можно вырезать. Удалять и добавлять, удалять и добавлять — это ежедневное обслуживание CLAUDE.md.