Продвинутый

CLAUDE.md по Карпаthy: как заставить ИИ писать код без ко...

Алексей Кузнецов
Алексей Кузнецов
Системный администратор7 сентября 2026 г.13 мин чтения

Разбираем репозиторий multica-ai/andrej-karpathy-skills — один файл конфигурации, который меняет поведение Claude Code, устраняя типичные ошибки LLM при разр...

CLAUDE.md по Карпаthy: как заставить ИИ писать код без костылей

Разбираем репозиторий multica-ai/andrej-karpathy-skills — один файл конфигурации, который меняет поведение Claude Code, устраняя типичные ошибки LLM при разработке.

Почему стандартный Claude Code проигрывает в продакшене

Почему стандартный Claude Code проигрывает в продакшене

Во-первых, стандартный Claude Code часто демонстрирует «среднего разработчика» в весах модели, что приводит к тому, что он генерирует код, который выглядит правильно на первый взгляд, но не соответствует реальным требованиям продакшена. Например, при работе с Dockerfile он может предложить:

dockerfile
FROM python:3.9-slim
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

Это выглядит аккуратно, но в продакшене такие решения часто приводят к проблемам с предсказуемостью. Модель не учитывает, что в реальных условиях:

  • Часто используются конкретные версии базовых образов
  • Требуется многоступенчатая сборка для уменьшения размера образа
  • Необходимы специфические параметры сборки (например, --no-cache-dir для pip)

Конкретный пример из практики: один из моих коллег попросил Claude сгенерировать Dockerfile для микросервиса. Модель предложила базовый python:3.9-slim, но не учла, что в нашей инфраструктуре все сервисы должны использовать Alpine для экономии ресурсов. Результат — после деплоя сервис начал потреблять в 3 раза больше памяти, чем ожидалось. Чтобы исправить это, пришлось вручную править Dockerfile, добавив:

dockerfile
FROM python:3.9-alpine
RUN apk add --no-cache gcc musl-dev

Во-вторых, контекстное окно Claude Code ограничено (обычно 32K токенов), и это съедает architectural решения при работе с крупными проектами. Я помню случай, когда нужно было добавить новый сервис в существующую систему из 15 микросервисов. Claude пытался понять всю архитектуру, но context window закончился на 5-6 сервисах, и он начал предлагать решения, которые нарушали принципы модульности.

Пример из реальной практики: при попытке рефакторинга монолитного приложения в несколько сервисов Claude предложил вынести всю бизнес-логику в отдельный сервис, но не учёл, что:

  1. Логика была тесно coupled с инфраструктурными деталями (например, использование конкретных очередей RabbitMQ)
  2. Контекст window не позволял ему увидеть все зависимости между модулями
  3. Он не знал о наших общих паттернах, которые мы документировали в внутреннем wiki

Чтобы обойти это ограничение, я начал разбивать запрос на части, сначала анализируя только один модуль, а потом постепенно расширяя контекст. Но это требует дополнительного времени и усилий, которые в продакшене часто не хватает.

На практике лучше использовать Claude Code для конкретных, локализованных задач, чем пытаться решить глобальные архитектурные вопросы за один запрос. Для этого:

  • Разбивайте большие задачи на маленькие, чётко определённые
  • Используйте конфигурационные файлы для передачи контекста (например, .cursorrules)
  • Всегда проверяйте предложенные решения в контексте полной системы

Ключевой принцип: Claude Code должен дополнять ваш процесс, а не заменять системный подход. Если вы хотите сохранить предсказуемость в коде, не полагайтесь на «магические» генерации — всегда проверяйте, как решение вписывается в общую архитектуру.

Что внутри CLAUDE.md от Карпаthy

Что внутри CLAUDE.md от Карпаthy

Правила генерации: от именования до обработки ошибок

Во-первых, имена должны отражать смысл сущности. Используйте snake_case для переменных и функции, избегайте сокращений и аббревиатур. Пример плохого именования:

bash
let userId = 42

А правильное:

bash
let user_id = 42

С практической точки зрения, функции следует называть по шаблону «verb_noun», например create_user, read_config. Это упрощает чтение кода и делает его более предсказуемым.

Если мы посмотрим на обработку ошибок, то важно не игнорировать сообщения об ошибках. Вместо того чтобы просто комментировать строку с ошибкой, изучите её полностью. Пример корректного подхода в Bash‑скрипте:

bash
if ! command -v jq >/dev/null 2>&1; then
    echo "jq не установлен, установка требуется" >&2
    exit 1
fi

Во-вторых, при работе с конфигурационными файлами проверяйте их наличие и корректность перед чтением. Используйте проверку наличия файла и проверку прав доступа:

bash
if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then
    echo "CA bundle найден"
else
    echo "CA bundle отсутствует" >&2
    exit 1
fi

Если мы посмотрим на пример из реальной практики, то часто встречается ошибка — hard‑coded пути к файлам. Вместо этого используйте переменные, которые можно переопределять через окружение:

yaml
# config.yaml
data_path: /var/lib/app/data

Таким образом, имена, проверки и обработка ошибок становятся частью предсказуемой и надёжной конфигурации.

Запрещённые паттерны и обязательные проверки

Запрещённым паттерном является использование комментариев TODO как окончательного решения. Такие комментарии часто остаются в продакшн‑коде и приводят к непредвиденным сбоям. Вместо этого фиксируйтеissue в трекере и рерайте код.

Ещё один запрещённый паттерн — hard‑coding значений, включая пути, порты и учётные данные. Пример плохого кода:

python
DB_HOST = "localhost"
DB_PORT = 5432
DB_USER = "admin"
DB_PASS = "secret"

Правильнее использовать переменные окружения или файлы конфигурации:

python
import os

DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = int(os.getenv("DB_PORT", "5432"))
DB_USER = os.getenv("DB_USER", "admin")
DB_PASS = os.getenv("DB_PASS", "secret")

С практической точки зрения, обязательной проверкой является возврат кода возврата команд. Игнорирование exit code может привести к дальнейшим ошибкам. Пример проверки:

bash
if ! curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health; then
    echo "API недоступен" >&2
    exit 1
fi

Если мы посмотрим на обязательные проверки, то следует убедиться в наличии необходимых утилит, в работоспособности сетевых портов и в корректности конфигурационных файлов. Например, проверка порта 443:

bash
if ! ss -tlnp | grep -q ':443'; then
    echo "Порт 443 не слушает ни один процесс" >&2
    exit 1
fi

Эти запрещённые паттерны и обязательные проверки помогают избежать типичных ловушек при работе с Claude Code и делают процесс генерации кода более предсказуемым и надёжным.

Внедряем в рабочий процесс: от pet-проектов до Kubernetes

Локальный запуск: где положить файл и как проверить

Файл CLAUDE.md нужно разместить в корне вашего проекта, рядом с README.md или pyproject.toml. Это стандартное место, где LLM-инструменты ищут конфигурации. Например, для Python-проекта:

bash
my-project/
├── CLAUDE.md      # ← сюда кладём файл
├── README.md
├── pyproject.toml
└── src/
    └── main.py

Чтобы проверить, что файл работает, запустите Claude Code с флагом --config:

bash
claude code --config CLAUDE.md

Если вы видите в логах сообщение Loaded configuration from CLAUDE.md, значит всё правильно. Если нет — проверьте путь к файлу и синтаксис YAML/Markdown (в зависимости от формата, который использует ваш вариант Claude Code).

В реальной практике я видел, как разработчики добавляли в CLAUDE.md запрет на использование eval() и exec() в Python-коде — это устраняет класс уязвимостей, которые LLM часто предлагает "для удобства". Пример такого правила:

yaml
rules:
  - id: no-eval-exec
    description: "Запрещать использование eval() и exec()"
    severity: error
    pattern: "eval\(|exec\(
    message: "eval/exec запрещены  используйте безопасные альтернативы"

Интеграция с CI/CD и код-ревью командой

Для интеграции с CI/CD файл CLAUDE.md нужно сделать доступным агенту Claude Code в пайплайне. В GitLab CI, например, это делается через монтирование файла в контейнер:

yaml
stages:
  - test

claude_check:
  stage: test
  image: python:3.11
  script:
    - pip install claude-code  # если нужно
    - claude code --config CLAUDE.md --check
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

В GitHub Actions аналогично:

yaml
jobs:
  claude-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Claude Code with CLAUDE.md
        run: claude code --config CLAUDE.md --check

Для code review командами CLAUDE.md помогает стандартизировать ожидания. Например, если в файле прописано:

markdown
## Код-ревью
- Все функции должны иметь docstring в Google стиле
- Запрещён use of `list` как переменная имени
- Проверка на null перед использованием Optional

То при ревью PR команда будет фокусироваться именно на этих пунктах, а не на общих "это выглядит странно" замечаниях. Это уменьшает субъективность и ускоряет процесс.

Во-первых, CLAUDE.md — это не просто набор правил, а инструмент для создания предсказуемости. Без него LLM будет "гадать", как вы хотите, чтобы код выглядел, а с ним вы задаёте чёткие границы. Это как IKEA для мебели: всё собирается по инструкции, и не нужно думать, где что прикрутить.

Во-вторых, в k8s-проектах я видел, как CLAUDE.md с настройками "no root containers" и "use init containers" помогал избежать частых ошибок при деплое. Например, запрет на privileged: true в поды — это спасает от уязвимостей, которые LLM может предлагать "для простоты".

Ограничения и когда не стоит использовать

Ограничения и когда не стоит использовать

Случаи, когда жесткие правила мешают прототипированию

CLAUDE.md от Карпаthy — это, по сути, контракт. И как любой контракт, он работает хорошо, когда стороны знают, чего хотят. Но на стадии «набросаем за вечер, посмотрим, полетит ли вообще» — этот файл становится тормозом.

Представьте: вы садитесь писать proof-of-concept для новой фичи. Не знаете, подойдёт ли подход, выберете ли библиотеку, сойдутся ли типы. А CLAUDE.md требует: «сначала напишите тесты», «используйте только типизированные интерфейсы», «никаких any», «все функции — pure». Вы тратите 40 минут на удовлетворение линтера и удовлетворение правил, а не на проверку гипотезы.

С практики: в прошлом году прототипировал векторный поиск для RAG-пайплайна. Понадобилось три варианта эмбеддингов, два способа чанкинга, четыре индекса. Если бы следовал CLAUDE.md дословно — ушёл бы в рефакторинг интерфейсов к полночи. Сделал грязно в одном ноутбуке, выбрал рабочий вариант, а на следующий день переписал «по-памятке». Экономия — порядка 6 часов.

Когда не включать строгий режим:

  • Спеки нет, требования плавающие
  • Исследуете чужой код или новую библиотеку
  • Хакертон / spike / proof-of-concept
  • Команда из 1–2 человек, контекст в голове, а не в документах

Мой подход: держу два файла. CLAUDE.md — для продакшн-кода, CLAUDE.prototype.md — для всего остального. Переключаю флагом при запуске:

bash
# Прототипирование — минимальные ограничения
claude --config CLAUDE.prototype.md

# Продакшн — полный набор правил
claude --config CLAUDE.md

Содержимое CLAUDE.prototype.md у меня занимает 30 строк против 200+ в основном:

markdown
# CLAUDE.prototype.md — режим быстрой проверки гипотез

## Разрешено
- any / unknown / @ts-ignore — если ускоряет проверку идеи
- Консольные логи вместо структурированного логирования
- Хардкод конфигов, отсутствие тестов
- Дублирование кода (DRY можно отложить)
- Импорт всего подряд для быстрой проверки API

## Запрещено
- Коммитить в main без ревью
- Пушить в прод
- Оставлять TODO дольше 48 часов без тикета

## Переход в продакшн
Перед MR: запустить `claude --config CLAUDE.md --check` и закрыть все предупреждения.

Как адаптировать под стек команды без потери сути

Оригинальный CLAUDE.md заточен под TypeScript/React/Node. Если ваш стек — Go + gRPC, Python + FastAPI или Rust + Axum — слепое копирование даст ложные срабатывания и раздражение.

Принцип: сохраняйте инварианты, меняйте реализацию.

Инварианты Карпаthy (моя интерпретация):

  1. Явность лучше неявности — никакой магии, всё читаемо в коде
  2. Типы на границах — вход/выход функции типизированы, внутри — по обстоятельствам
  3. Тестируемость по дизайну — чистые функции, инъекция зависимостей, никаких синглтонов
  4. Ошибки — значения — Result/Option/Union вместо исключений там, где это уместно
  5. Документируйте «почему», а не «что» — код сам говорит что делает

Вот как это выглядит для трёх стеков:

Go + gRPC — заменяем интерфейсы на интерфейсы Go, ошибки — на возвращаемые error:

markdown
## Go-специфичные правила (дополняют базовые)

### Обработка ошибок
- Все публичные функции возвращают (T, error) — никаких panic в библиотечном коде
- Ошибки оборачиваем через fmt.Errorf с %w для сохранения цепочки
- Sentinel errors только для ожидаемых случаев: ErrNotFound, ErrUnauthorized
- В gRPC-хендлерах: логируем с контекстом, возвращаем status.Error(codes.Internal, ...)

### Интерфейсы
- Интерфейсы определяются в пакете-потребителе, не в пакете-реализации
- Размер интерфейса — 3–5 методов макс. Если больше — разбиваем
- Моки генерируем через go:generate + mockgen, не пишем руками

### Тестирование
- Unit-тесты: табличные, параллельные (t.Parallel())
- Интеграционные: testcontainers для Postgres/Redis/Kafka
- Бенчмарки для горячих путей — обязательно в CI

Python + FastAPI — типы через Pydantic, зависимости через Depends:

markdown
## Python-специфичные правила

### Типизация
- Все публичные функции: type hints + docstring (Google style)
- Pydantic v2 модели для request/response — единственный источник истины
- mypy --strict в CI, игноры только с комментарием # type: ignore[code] + ссылка на тикет

### Зависимости
- Внедрение через FastAPI Depends — никаких глобальных переменных
- Репозитории/сервисы — протоколы (Protocol), реализации подменяются в тестах
- Настройки: pydantic-settings, .env только для локали, прод — через секреты

### Асинхронность
- async def везде, где есть I/O (БД, HTTP, Kafka)
- Синхронные CPU-bound задачи — в run_in_executor или отдельный процесс
- Никаких asyncio.create_task без сохранения ссылки (утечки задач)

Rust + Axum — используем систему типов максимально:

markdown
## Rust-специфичные правила

### Ошибки
- thiserror для библиотечных ошибок, anyhow для бинарников
- IntoResponse для ошибок хендлеров — единая точка маппинга в HTTP коды
- Result<T, AppError> везде, unwrap/expect только в тестах и main()

### Архитектура
- Трейты для портов (Repository, Cache, Queue), структуры для адаптеров
- Axum extractors для зависимостей — State<AppState> + Extension<T>
- Tower middleware для cross-cutting concerns (логирование, метрики, трассировка)

### Тестирование
- mockall для моков трейтов (только где неизбежно)
- sqlx::test для БД-тестов с реальной Postgres
- property-based testing (proptest) для сериализации/парсинга

Общий паттерн адаптации:

markdown
# CLAUDE.md — базовый слой (общий для всех стеков)

## Принципы (неизменные)
1. Явность > неявность
2. Типы на границах
3. Тестируемость по дизайну
4. Ошибки — значения
5. Документируйте «почему»

## Универсальные запреты
- any/anyhow/unwrap в продакшн-коде без обоснования
- Глобальное изменяемое состояние
- Имплементация в интерфейсе (кроме Rust trait impl)
- Коммиты без прохождения линтера/тайпчекера
markdown
# CLAUDE.go.md / CLAUDE.py.md / CLAUDE.rs.md — слой стека

## Стек-специфичные правила (см. примеры выше)
## Инструментарий: линтеры, форматтеры, генераторы
## CI-гейты для этого стека

Подключение в проекте:

markdown
# CLAUDE.md в корне проекта (коммитится в репо)

@import "CLAUDE.base.md"
@import "CLAUDE.go.md"     # или .py.md / .rs.md
@import "CLAUDE.team.md"   # локальные соглашения команды

CLAUDE.team.md — единственное, что пишут разработчики конкретной команды. Там: нейминг веток, префиксы коммитов, соглашения по миграциям БД, внутренние библиотеки. Остальное — наследуется.

Так вы не теряете суть (инварианты), но не боретесь с инструментом за каждую языковую идиому.

Часто задаваемые вопросы

Почему стандартный Claude Code не подходит для продакшена

Стандартная модель Claude Code часто имеет вес «среднего разработчика», что приводит к генерации кода, который выглядит правильно на первый взгляд, но не соответствует реальным требованиям продакшена. Например, при создании Dockerfile модель может предложить базовый образ python:3.9-slim, не учитывая, что в инфраструктуре требуется Alpine для экономии ресурсов, что приводит к увеличению потребления памяти в три раза. Для устранения таких проблем необходимо явно задать конфигурацию через CLAUDE.md и использовать конкретные версии образов, а также добавлять необходимые зависимости в RUN директива.

Ограничения контекстного окна Claude Code

Контекстное окно стандартной версии Claude Code ограничено примерно 32K токенов, что существенно ограничивает способность модели понимать архитектуру больших проектов. При попытке добавить новый сервис в существующую систему из 15 микросервисов контекст быстро исчерпывается, и модель начинает предлагать решения, нарушающие принципы модульности и архитектурных решений. Это приводит к ошибкам в интеграции и неправильной структурированию кода, особенно в случаях, когда изменения затрагивают множество связанных компонентов.

Как настроить Claude Code для воспроизводимости

Для повышения надежности работы Claude Code следует явно указать в CLAUDE.md конкретные параметры: использовать Alpine-образы, включать опции компиляции без кэша (--no-cache-dir), задавать версии библиотек и жестко фиксировать пути. Также важно регулярно проверять сгенерированный код на соответствие архитектурным требованиям и проводить автотестирование, чтобы выявить ошибки, которые модель могла пропустить из-за ограничений контекста. Такая настройка позволяет получить предсказуемый и контролируемый результат при работе с ИИ-генератором кода.

Что делать при изменении архитектуры существующего проекта

При масштабировании системы или внесении значительных изменений в архитектуру лучше разбить работу на небольшие шаги, сохраняя контекст в отдельных файлах. Использование промптов с чёткими инструкциями и валидация каждого шага позволяет избежать накопления ошибок и сохранить предсказуемость работы Claude Code. Регулярная проверка сгенерированных компонентов перед их интеграцией помогает поддерживать качество кода на высоком уровне и минимизировать влияние «костылей» от автоматической генерации.

Поделиться:TelegramX / TwitterVK