
Вы почти наверняка уже встречались с Conventional Commits. Их уродливые лица можно заметить в changelog опенсорсного проекта, которым вы пользовались. Возможно, это был обязательный формат коммитов опенсорсного проекта, в котором вы были контрибьютором. Многие люди безгранично им верят. Я им безгранично не верю.
Хоть он и применяется в большом количестве разных популярных опенсорсных проектов, Conventional Commits — это глубоко порочный стандарт, стимулирующий фокусироваться не на том и не оправдывающий свои ожидания.
Ошибочная фокусировка
Conventional Commits должен добавлять семантическую значимость сообщениям коммитов, позволяя разработчикам и конечным пользователям понимать внесённые коммитом изменения. Однако Conventional Commits справляется с этим просто ужасно. Для демонстрации этого давайте разберём анатомию conventional commit. Согласно веб-сайту Conventional Commit, сообщения коммитов должны форматироваться так:
<тип>[опциональное указание границ]: <описание>[опциональное тело][опциональное примечание(-я)]
В строке темы коммита есть <тип> (что-то наподобие fix, feat, chore, docs или refactor1), описывающий тип изменения. Затем идёт опциональное указание границ коммита и описание.
У этого формат есть один фатальный недостаток: тип имеет больший приоритет, чем границы. На самом деле, всё должно быть наоборот.
Границы > тип
Границы изменения (тема изменения) — самая важная часть коммита. Чтобы показать это, давайте подумаем, почему каждой из категорий ответственных лиц важнее границы, чем тип изменения:
-
Контрибьюторы: когда вы контрибьютор проекта, то вам часто нужно читать лог коммитов, чтобы находить в кодовой базе изменения, связанные с определённой областью кода. На то есть множество причин, и вот некоторые из них:
-
Желание войти в курс дела произошедшего со времени вашего последнего коммита.
-
Попытка понять, куда в целом движется проект.
-
Поиск коммитов, которые могут конфликтовать с вашей текущей работой при выполнении pull или rebase.
В процессе чтения лога коммитов вы ищете, какие области были затронуты. Вам не важен тип произошедших изменений, вас интересуют их границы.
-
-
Отладчики: при изучении бага часто приходится читать лог коммитов, чтобы понять, какие изменения могли затронуть области, связанные с компонентом, в котором проявился баг. В этом случае границы тоже остаются самой важной информацией. Тип изменения совершенно бесполезен, потому что баги могут быть добавлены в любом изменении, вне зависимости от типа. (Я уверен, что все мы писали фикс, который вызывал ещё один баг.)
-
Реагирующие на инциденты: когда продакшен лежит, эффективным способом выявления областей, потенциально вызвавших проблемы, становится просмотр лога коммитов на предмет изменений, внесённых примерно во время аварии. Границы здесь опять оказываются самой важной информацией. Например, если вы видите коммит, связанный с областью кода
authи внесённый на самом пике появления входящих ошибок API, то, вероятно, это и есть виновник проблемы. И в этом случае тип снова бесполезен, потому что баги могут быть добавлены в любом изменении.
Что же делают Conventional Commits? Они настолько снижают приоритет границ, что их указание становится опциональным! Какого чёрта границы опциональны? Коммит без указания его границ — это как предложение без темы! Более того, Conventional Commits ставят тип в самое начало сообщения коммита, полностью переворачивая приоритет границ и типов.
Тип избыточен и он ограничивает
Возможно, вы думаете: «Может быть, всё действительно наоборот, но тип коммита чем-то важен?», на что я отвечу «нет». Тип изменения почти всегда должен указываться в описании коммита! Возьмём для примера одно сообщение коммита:
fix(компилятор): предотвращает вырезание элементов SVG <style> в пространстве имён
Даже если бы оставить только описание, будет понять, что это фикс! Пространство в строке коммита драгоценно, пустая трата символов на тип не нужна! Но чаще всего она даже хуже, чем бесполезна; часто она ограничивает. Возьмём для примера ещё одно сообщение коммита:
refactor(core): поддержка webmcp дополнена использованием document.modelContext
Этот коммит расширил функциональность webmcp в компоненте core, чтобы она поддерживала и document.modelContext, и navigator.modelContext. Так это устранение бага, рефакторинг или новая фича? Я бы сказал, что всё сразу! Но здесь по-прежнему важно только то, что это изменение в компоненте core/webmcp.
Conventional Commits изначально фокусируются не на том (на типе коммита), обесценивая границы (которые и нужны людям на самом деле).
Нарушенные обещания
Итак, мы уже поняли, что формат Conventional Commits ужасен, но должен же он приносить хоть какую-то пользу? Давайте прочитаем раздел Why Use Conventional Commits, что бы понять, имеют ли его обоснования смысл.
-
Автоматическая генерация CHANGELOG.
Это самое важное обещание Conventional Commits: можно запустить инструмент наподобие git-cliff или conventional-changelog, чтобы сгенерировать changelog из коммитов с момента предыдущего релиза. Хорошая ли это мысль? Нет! Аудитория changelog совершенно отличается от аудитории лога коммитов!
Changelog предназначен для пользователей, а пользователю важно понимать функциональные различия между версиями. Ему нужно знать, что поменялось с точки зрения бизнеса/функциональности.
Лог коммитов предназначен для разработчиков, а разработчикам важно прочитать историю изменения кодовой базы во времени. Им важны изменения с точки зрения границ.
Как видите, это две абсолютно разные грани, и любые попытки совместить их приведут к посредственным результатам. И на то есть несколько причин:
-
В любом достаточно сложном проекте для реализации немаленькой фичи требуется множество коммитов. Процесс реализации фичи (задокументированный в логе коммитов) ценен для разработчиков и контрибьюторов, но бесполезен для конечного пользователя. Его волнует только новая фича, а не то, как её делали!
-
Как отметил Рич ван дер Хофф, при работе с Conventional Commits проблематично выполнять откаты. Коммиты откатов важны для разработчиков с точки зрения истории лога коммитов, но для конечного пользователя откаченное изменение эквивалентно полному отсутствию изменения.
-
-
Автоматическое определение семантического инкремента версии (на основании типов внесённых коммитов).
Звучит здорово, но реалии разработки ПО часто препятствуют точному выполнению этой задачи. Возьмём для примера следующие ситуации:
-
Откаты: представьте ситуацию, когда добавленное вами изменение настолько критическое, что приходится его откатить. Инструментарий обнаружит важное изменение и увеличит номер основной версии, хотя поломка обратной совместимости была откачена и важного изменения на самом деле не было.
-
Случайные поломки: допустим, поломка оказалась незамеченной и вы не осознали, что изменение критично, когда вносили его. Только позже вы понимаете, что оно оказалось критическим. Произойдёт некорректный инкремент младшей версии/патча, хотя на самом деле нужно увеличение старшей версии.
-
Ретроактивный откат поломок: допустим, позже вы добавили коммит, который в сочетании с ранее ломавшим код коммитом приводит к diff, не ломающему код. Аналогично первому случаю, инструментарий ошибочно идентифицирует это, как ломающее изменение.
В таких ситуациях можно переписать историю с помощью rebase, но это часто ломается или не допускается рабочими процессами. Кроме того, после этого контрибьюторы видят пересмотренную историю, что снижает достоверность истории, которую рассказывает лог коммитов.
-
-
Донесение природы изменений до коллег, сообщества и других ответственных лиц.
Как мы уже разобрались, коллегам и сообществу нужно от changelog и лога коммитов совершенно разное. Conventional Commits умудряются не решить ни одну из этих проблем.
-
Запуск процессов сборки и публикации.
А это попросту дурная идея. Допустим, вы выполняете автоматизированные проверки безопасности коммитов, касающихся кода, но кто-то создаёт троянский коммит с заголовком
docs: fix typos, который на самом деле добавляет уязвимости в подсистему аутентификации. Очевидно, что подобная зловредная активность может быть отловлена на этапе ревью кода, но она минует автоматизированный инструментарий, из-за чего бремя выявления проблемы возляжет на человека.Вычислительные ресурсы дёшевы, просто пользуйтесь
git diffдля выявления изменившихся файлов (здесь снова нужны границы) и выполняйте процессы сборки/публикации на основе этого. -
Упрощение участия контрибьюторов в проектах, обеспечивая им возможность изучения более структурированной истории коммитов.
Ну да, более структурированной. Упрощает работу контрибьюторов? Да ни капли (и мы уже это подробно показали).
Ни один из этих ключевых пунктов Conventional Commits на самом деле не проходит проверку реальностью.
Кроме того, Conventional Commits крайне сложно применять в проектах. Предполагается, что вы должны сами определить собственный набор «типов», но практически все просто берут стандартные из commitlint, которые часто плохо подходят под особенности конкретных проектов. Эта проблема проявляется особенно остро в корпоративных средах, в которых требования управления изменениями и аудита часто предписывают указывать номер тикета в каждом сообщении коммита. Очевидным местом для этого становится поле <scope>, но в результате бесполезный номер тикета заменяет единственные полезные метаданные Conventional Commit.
Лучший выбор
Что же выбрать вместо этого? Идите по стопам реально успешных программных проектов наподобие Linux, FreeBSD, Git, Go и NixOS! Что у этих проектов общего? Во всех них используются сообщения коммитов с префиксом, указывающим на границы (под «границами», scope, подразумевается то, что важно для конкретного проекта). Обычно границы каждого проекта самоочевидны. Для ядра Linux естественной областью стала подсистема. Для проектов Go — путь пакета. В проекте с микросервисной архитектурой естественной границей станет имя микросервиса.
Вот несколько проектов и их инструкций по формату коммитов.
|
Проект |
Формат |
Пример |
|---|---|---|
|
|
|
|
|
|
||
|
|
||
|
|
||
|
|
||
|
|
К сожалению, несмотря на использование в самых успешны опенсорсных проектах, этот стиль коммитов, похоже, проиграл войну брендингов. Я намерен это изменить: представляю вашему вниманию scopedcommits.com. Этот веб-сайт посвящён пропаганде возврата к логичным сообщениям коммитов и к отделению генерации changelog от управления логами коммитов.
Заключение
Предполагаемые преимущества Conventional Commits на самом деле иллюзорны, и наша отрасль не получает никакой осязаемой выгоды от применения них в качестве стандарта. Однако Conventional Commits, к сожалению, стали довольно популярны в опенсорсных проектах и наверно поэтому ИИ имеют привычку по умолчанию использовать их в сообщениях коммитов. Это привело к распространению в проектах сообщений коммитов, состоящих из антипаттернов.
Цель этой статьи — борьба против доминирования Conventional Commits и демонстрация того, что есть более совершенные способы структурирования сообщений коммитов.
-
Строго говоря, спецификация Conventional Commits определяет только
fixиfeat, оставляя определение дополнительных типов конкретным проектам, однако в большинстве проектов просто используются типы, определённые commitlint, поэтому я добавил некоторые из них в этот список.
ссылка на оригинал статьи https://habr.com/ru/articles/1044970/