Понятные коммиты Claude Code: соглашения, шаблоны сообщений, атомарные изменения и история репозитория, по которой легко сделать быстрый rollback.

ИИ часто пишет коммиты "на глаз": видит пачку изменений и пытается описать ее одной фразой. Сообщение получается размытым, потому что модель не чувствует командный контекст: что у вас считается важным, как вы называете сущности, какие правки принято фиксировать отдельно.
Беда не в том, что текст "некрасивый". Беда в том, что история превращается в кашу. А Git ценен именно историей: по ней вы понимаете, что менялось, почему и где искать причину бага.
Плохие сообщения ломают ревью. Ревьюеру приходится читать дифф целиком, потому что по заголовку непонятно, что вы хотели сделать. Потом ломается поиск: когда через месяц вы ищете, где "сломалась авторизация", коммиты типа update и fix не дают никакой зацепки.
Типичные симптомы таких коммитов:
update, fix, wipПредставьте: вы правили форму логина, заодно переименовали пару файлов и обновили зависимости. ИИ пишет: "fix login". Через неделю падает сборка, и непонятно, связано ли это с логином, переименованием или зависимостями. Такой коммит сложно откатить безопасно, потому что он тянет за собой лишнее.
Проблема обычно не в ИИ, а в отсутствии правил. Если не задать рамки, модель будет объединять изменения и писать универсальные слова, которые не помогают ни ревью, ни откату.
Понятный коммит читается как короткая, честная записка: что изменилось и зачем. Если человек открывает историю через неделю (или вы сами через месяц), ему не нужно гадать, почему тронули файл и что это ломает.
Практическая проверка простая: по одному коммиту должно быть ясно, какое поведение стало другим. Не только "что сделал", но и "какую проблему это решает". Хорошо, когда по сообщению видно, стоит ли брать этот коммит в hotfix или лучше не трогать.
Почти всегда помогают четыре вещи:
Коммит не должен заменять задачу в трекере. В задаче часто есть обсуждения и контекст бизнеса. В коммите нужен итог: конкретное изменение и мотив.
Детализация полезна, когда она влияет на работу других: поменялись публичные поля, формат данных, поведение по умолчанию, добавилась миграция или фича-флаг. А перечисление всех файлов, переименование переменных и "передвинул строку" лучше оставить диффу.
Ориентир простой: достаточно конкретно, чтобы понять смысл без открытия диффа, но достаточно коротко, чтобы git log оставался читаемым.
Соглашение по коммитам нужно не ради красоты. Оно помогает быстро понять, что менялось, кто и зачем это делал, и можно ли откатить часть изменений без сюрпризов. Если вы просите ИИ писать коммиты, единый стиль особенно важен: без него история превращается в набор похожих фраз.
Когда команда небольшая (или вы один), часто хватает формата "Область: действие": "Auth: исправил проверку токена", "UI: добавил состояние загрузки". Важно договориться о списке областей (Auth, Payments, Admin, Mobile) и не переименовывать их каждую неделю.
Conventional Commits полезен, когда вы хотите меньше споров и больше автоматизации: понятные релизы, генерацию changelog, быстрый поиск по типам изменений. Формат простой: type(scope): short summary. Пример: fix(auth): handle expired token.
Чтобы выбрать стиль и не спорить, ответьте на два вопроса: вам важнее читаемость для людей или будущая автоматизация? Если сейчас важнее читаемость, начните с "Область: действие" и закрепите примеры в README. Если есть регулярные релизы и несколько команд, берите Conventional Commits, но без фанатизма.
Минимальный набор типов, который закрывает большинство случаев:
Остальное добавляйте только если чувствуете реальную боль. Если тип не помогает принять решение про откат или поиск изменений, он лишний.
Чтобы сообщения получались стабильно, дайте модели жесткую форму. Тогда она не будет каждый раз придумывать стиль заново, а история останется предсказуемой.
Хорошая заготовка включает заголовок, короткое тело и (иногда) примечания. Заголовок отвечает на "что изменилось", тело на "почему сделали" и "какие есть последствия". Именно "почему" чаще всего спасает через месяц.
Есть слова, которые почти всегда портят заголовок, потому что ничего не объясняют. Их проще прямо запретить в инструкции:
\u003ctype\u003e(\u003cscope\u003e): \u003cкратко, в настоящем времени\u003e
Почему:
- \u003cпричина 1\u003e
- \u003cпричина 2\u003e
Что важно:
- \u003cпобочные эффекты/миграции/заметки для ревью\u003e
Для функциональности:
feat(\u003cscope\u003e): \u003cдобавить/изменить поведение\u003e
Почему:
- \u003cкакую боль пользователя решаем\u003e
Что важно:
- \u003cчто проверить руками\u003e
Для исправления:
fix(\u003cscope\u003e): \u003cисправить конкретную ошибку\u003e
Почему:
- \u003cкак проявлялась проблема\u003e
Что важно:
- \u003cриск, обратная совместимость, флаги\u003e
Для рефакторинга без изменения поведения:
refactor(\u003cscope\u003e): \u003cперестроить код без изменения логики\u003e
Почему:
- \u003cупростить поддержку/убрать дубли\u003e
Что важно:
- \u003cкак убедились, что поведение прежнее\u003e
Простой пример "почему, а не что": вместо "обновил валидацию формы" написать "пользователи отправляли форму без телефона, из-за этого заявки терялись".
Если вы хотите, чтобы ИИ писал нормально, не оставляйте ему свободу. Хороший запрос для коммита - это короткое ТЗ: формат, длина, и что именно считать одной темой.
Рабочий прием: просить 3 варианта сообщения и выбирать один. Так вы быстрее отсекаете слишком общие формулировки, а стиль выравнивается.
Задайте ограничение на заголовок: не больше 72 символов. Это заставляет выделять суть, а не пересказывать дифф.
Полезно отдельным блоком просить перечислить файлы и объяснить смысл изменений простыми словами. Так проще заметить лишнее: форматирование, случайные правки конфигов, промежуточные логи.
Если видно, что в коммите две темы (например, "поправил валидацию" и "обновил зависимости"), лучше сразу разделить изменения и пересобрать сообщения под каждую тему.
Шаблон запроса, который обычно дает предсказуемый результат:
Сгенерируй сообщение коммита по этим изменениям.
Требования:
- Дай 3 варианта заголовка (до 72 символов), выберу один.
- После заголовка: 1-2 строки тела, только смысл, без воды.
- Отдельным блоком: список затронутых файлов и что в каждом изменилось.
- Если больше одной темы, предложи, как разбить на 2-3 атомарных коммита.
Контекст задачи: \u003c1-2 предложения\u003e
Мини-пример контекста: "исправить ошибку 500 при сохранении профиля, когда поле phone пустое". Он помогает модели назвать причину и эффект, а не писать "misc fixes".
Хорошая история в Git рождается не в момент, когда вы придумываете красивое сообщение, а раньше: когда вы намеренно делаете маленький кусок работы и вовремя останавливаетесь.
Начните с цели в 1-2 предложениях: что меняете и зачем. Пример: "Добавить валидацию телефона в форме регистрации, чтобы не принимать пустые и слишком короткие значения". Эта фраза потом станет темой коммита.
Дальше делайте правки маленькой порцией. Если вы уже затронули несколько разных смыслов (валидацию, верстку, тексты ошибок), это сигнал остановиться и разделить.
Не просите ИИ "сделай коммит", пока сами не посмотрели изменения. Сначала убедитесь, что в индекс попало ровно то, что вы хотите.
Повторяемая последовательность, которая хорошо работает:
Небольшой прием: после просмотра diff задайте себе вопрос "что я буду откатывать, если завтра вылезет баг?". Если ответ звучит как "все сразу", коммит стоит разбить.
Если вы собираете приложение в TakProsto (takprosto.ai) и затем экспортируете исходники в репозиторий, этот же цикл помогает не потерять управляемость: каждое изменение фиксируется отдельной идеей, а при необходимости можно быстро вернуться к снимку и сделать откат.
Атомарный коммит - это одно понятное изменение, которое можно откатить без сюрпризов. Атомарность решает половину проблем: история становится читаемой, а rollback быстрым.
Проверка простая: если внутри коммита есть два разных "почему", он уже не атомарный. "Почему меняем UI" и "почему обновляем схему БД" - разные причины, даже если делались в одном заходе.
Признаки неатомарного коммита:
Самое полезное правило разделения: функционал отдельно, форматирование отдельно. Если formatter перелопатил 30 файлов, вынесите это в отдельный коммит с честным сообщением.
Хороший трюк - разделять тесты и реализацию: один шаг, один коммит. С автогенерацией (клиенты, миграции, шаблонные файлы) тоже проще жить в два шага: сначала "генерация как есть" без ручных правок, потом "ручные правки поверх генерации" с точной причиной.
Пример: добавляете поле в форму. Один коммит - миграция и модель, второй - отображение в интерфейсе, третий - переименование или форматирование. История получается короткой, но управляемой.
История, удобная для быстрого отката, выглядит скучно: много маленьких понятных коммитов, каждый про одну причину изменения. Зато в момент аварии вы не гадаете, что именно сломалось, и не откатываете лишнее.
Правило, которое хорошо работает: один риск - один коммит. Если вы одновременно правите валидацию формы и обновляете зависимости, при откате вы потеряете и полезные изменения.
Часто историю собирают так: сначала инфраструктура (фича-флаг, конфиг, подготовка интерфейсов), затем минимальная рабочая логика, потом тесты и проверки, и в конце рефакторинг или форматирование отдельным коммитом.
Revert хорош, когда нужно быстро вернуть прод в рабочее состояние и коммит изолирован. Если проблема размазана, иногда проще сделать новый fix-forward коммит, который исправляет баг и не трогает остальную историю. Главное - чтобы это было понятно по сообщению.
Задача: в интерфейсе нужна новая кнопка "Повторить". Для нее требуется новый эндпоинт, по пути вы нашли баг в обработке ошибок, а заодно обновили текст подсказки. Если закоммитить все разом, потом трудно понять, что именно сломало релиз и что откатывать.
Один из рабочих вариантов - разнести это на несколько коммитов с четкими границами:
Такую историю легко откатывать. Допустим, после релиза выяснилось: при ошибке 500 приложение зависает. Тогда вы откатываете только fix(client): handle 500 error on retry (или временно еще и refactor, если подозрение падает на него), а кнопка и новый эндпоинт остаются.
Главная ловушка в том, что модель пишет коммиты быстро, но не думает о том, как вы будете читать историю через месяц. Без правил история превращается в "что-то поменяли где-то", и откат становится нервным.
Ошибки, которые чаще всего ломают читаемость:
fix, wip, finalЭто лечится простыми привычками:
Если форматировали код или прогнали линтер, отделите это в отдельный коммит.
Пишите в сообщении причину и эффект: что меняется для пользователя или системы. "fix(auth): не сбрасывать сессию при обновлении токена" лучше, чем "auth fixes".
Не прячьте смысл под chore. Если это багфикс, пишите fix. Если меняете поведение, пишите feat. Если только перестановка файлов, делайте отдельный коммит с понятным "refactor: переименовать X в Y".
Остановитесь на 30 секунд и проверьте, что запись будет понятна вам через месяц и любому человеку в команде. Цель простая: один коммит - одна причина изменения, и по истории можно безопасно откатиться.
Посмотрите на заголовок: он должен быть коротким и конкретным, в одном стиле с остальными коммитами. Избегайте общих слов вроде "правки", "обновления", "фикс", если не ясно, что именно изменилось.
Если используете тело сообщения, хватит 1-3 строк: что поменяли и почему. Например: "Убрали повторный запрос профиля, чтобы не дергать API дважды при ререндере".
Перед коммитом проверьте:
Если сомневаетесь, представьте сценарий: "Прод упал, нужно откатить только эту правку". По сообщению должно быть ясно, что вы откатываете и какие побочные эффекты возможны.
Работает не "идеальная формулировка", а повторяемый ритуал. Закрепите стиль прямо в репозитории: короткий документ на одну страницу и несколько примеров удачных сообщений. Примеры обычно полезнее правил.
Сделайте 2-3 шаблона и используйте их всегда: для фич, для исправлений, для техдолга. Когда шаблон выбран заранее, модель реже уходит в общие слова и чаще пишет по делу.
И главное: сначала маленький diff, потом коммит. Если правка разрослась, остановитесь и разделите ее, пока контекст свежий.
Если команда собирает продукт через чат-платформу TakProsto, имеет смысл использовать planning mode, чтобы заранее разбить задачу на шаги. А снимки и rollback помогают безопасно откатываться, когда эксперимент пошел не туда, еще до того, как изменения попадут в Git.
Договоритесь в команде об одном стиле и соблюдайте его без исключений хотя бы неделю. Потом коротко разберите примеры: какие сообщения стали эталонными и что стоит убрать из правил.
Потому что модель видит только дифф и пытается «сжать» его в одну универсальную фразу. Без правил и контекста команды она часто пишет общие слова вроде fix/update, из-за чего по истории непонятно, что изменилось и зачем.
Откройте git log и спросите себя: «Пойму ли я через месяц, какое поведение стало другим, не читая дифф?» Хороший коммит обычно содержит:
Плохой заголовок не помогает ни ревью, ни поиску, ни откату. Чаще всего это:
update, fix, wipНачните с простого формата «Область: действие» — он читается людьми и быстро приживается.
Если вам важны релизы, changelog и фильтрация по типам — берите Conventional Commits: type(scope): summary.
Главное — закрепить один стиль и примеры в репозитории, чтобы ИИ не «изобретал» формат каждый раз.
Обычно хватает пяти:
feat — новое поведение для пользователяfix — исправление ошибкиrefactor — перестройка кода без изменения поведенияdocs — тексты и документацияДайте жесткий шаблон и запретите «пустые» слова. Удобный минимум:
Так сообщения получаются предсказуемыми и сравнимыми между собой.
Попросите модель не «один лучший вариант», а несколько и с ограничениями:
И добавьте 1–2 предложения контекста задачи (как проявлялась проблема или что меняется для пользователя).
Если в коммите больше одного «почему», его уже сложно откатывать. Типичные причины разделить:
Практика: «один риск — один коммит» и «форматирование — отдельно».
Сделайте ритуал на несколько минут:
Это быстрее, чем потом разбираться в истории и делать нервный rollback.
Экспорт кода — это момент, когда удобно «разложить» изменения на понятные идеи и собрать чистую историю. Помогает тот же подход:
Если вы используете снимки и rollback в платформе, это дополняет Git: можно откатиться до экспорта, а в репозитории оставить аккуратную историю.
chore — техработы: конфиги, зависимости, инфраструктурные мелочиЕсли тип не помогает понять смысл или решение про откат — скорее всего, он лишний.