Автоматическое форматирование кода: где ломается процесс
Автоматическое форматирование кода почти всегда откладывают: репозиторий растёт быстрее, чем доходят руки до настройки правил. Пока команда небольшая, стиль держится на договорённостях и внимательности ревьюера — отступы, кавычки, порядок импортов, длина строки. Но стоит появиться второму языку и третьему репозиторию, как стиль разъезжается: каждый редактор настроен по-своему, а единого источника правды нет.
Цена проявляется не в спорах о вкусах, а в измеримых вещах. Ручные правки в ревью — самая частая причина замечаний: цикл обсуждения уходит на пробелы и переводы строк вместо логики изменения. Шум в диффах растёт вместе с числом затронутых файлов — реальное изменение приходится искать среди переформатированных соседних строк, время до слияния увеличивается, а вероятность пропустить дефект растёт.
Отдельная категория — срывы сборки из-за стиля. Prettier, Black и gofmt запускают на одном шаге конвейера, проверку стиля — на другом, и правила на машине разработчика расходятся с правилами на сервере сборки. Итог — красная сборка на строке, которую автор не трогал, и привычка перезапускать её вместо разбора причины. Ниже разбираем, как это закрывается и почему у форматера должен остаться один режим запуска.
Что меняется в команде и конвейере после внедрения автоформата
Форматтер запускается до ревью, а не после него: хук на коммит переписывает файл, а проверка в конвейере падает, если стиль разошёлся с конфигом. Ревьюер видит только смысловые изменения — перестановку пробелов и переводы строк за него уже сделал инструмент. Это меняет не скорость отдельных людей, а структуру самого процесса.
Эффект измерим. До внедрения на правки отступов и кавычек уходило от четверти до половины времени ревью, а диффы прибавляли сотни строк шума. После — доля строк, изменённых по стилю, падает почти до нуля, а спорные правки перестают быть предметом обсуждения: о них договариваются один раз в конфиге.
Единый стиль без ручных правок
Конфиг версионируется вместе с проектом, поэтому правило одинаково работает в редакторе, на локальной машине и в конвейере. Prettier с зафиксированным набором опций убирает вариативность: файл приводится к одному виду независимо от того, кто его писал. Новому инженеру не нужно изучать чужую манеру оформления — форматтер применяет её сам.
Меньше шума в ревью и правок в диффах
Меньше шума в диффе — быстрее сходится проверка: замечания смещаются с оформления на логику и граничные случаи. Стабильность сборки растёт по той же причине — расхождения в стиле уходят из списка причин отказа. Перед массовым прогоном форматирование выносят в отдельный коммит, чтобы в истории не смешивались смысловые и косметические изменения.
Режимы проверки и правки: от локального хука до шага сборки
Режимы отличаются тем, кто и когда платит за исправление: локально правка стоит секунды, в конвейере — цикл сборки, ревью и повторный запуск. Поэтому форматы обычно сочетают, а не выбирают один. Ниже — когда какой режим уместен и где он перестаёт работать.
Локальная правка до коммита
Форматирование при сохранении в редакторе держит рабочую копию в согласованном виде: автор не думает о стиле, диффы не засоряются перестановкой пробелов. Правка перед коммитом через предкоммитный хук прогоняет форматтер по изменённым файлам и останавливает коммит, если что-то осталось неисправленным. Выборочный прогон по файлам важен на больших репозиториях: полный обход всех исходников на каждый коммит заметно тормозит работу.
Проверка как обязательный шаг конвейера
Здесь форматтер запускается в режиме проверки, без записи, и падает с ненулевым кодом возврата. Это единственный режим, который нельзя обойти локальными настройками, — он и служит общим барьером для всех веток. Плата за это — правку приходится вносить после пуша, а значит, запускать сборку заново.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Форматирование при сохранении | Ежедневная работа, нужен мгновенный отклик | Зависит от настроек каждого разработчика, не действует при правках в веб-интерфейсе |
| Правка перед коммитом | Нужно не пускать неформатированный код в историю | Требует установки хуков у каждого, обходится флагом пропуска проверок |
| Шаг конвейера | Общий барьер для всех веток и внешних участников | Ошибка видна только после пуша, нужен повторный прогон сборки |
| Выборочный прогон по изменённым файлам | Крупные репозитории с долгим полным обходом | Нужна корректная база сравнения ветки, иначе часть файлов выпадает |
Как мы включаем проверки стиля в конвейер: этапы работ
Форматирование, включённое одним коммитом во все репозитории, обнуляет историю правок: перестаёт быть понятно, кто и зачем менял строку, а обзор изменений тонет в перестановке пробелов. Поэтому внедрение форматирования ведём отдельным проектом с обратимыми шагами, не блокирующими релизы.
Инвентаризация репозиториев и исключений
Сначала собираем карту: языки и их доли, активность, сгенерированный код, вендорные каталоги, файлы с намеренным выравниванием — таблицы, схемы миграций, разметка. Всё, что создаётся сборкой, уходит в исключения, иначе форматтер и генератор правят друг друга бесконечно.
- Инвентаризация репозиториев: языки, активность, сгенерированные и вендорные каталоги.
- Общий профиль правил — единый набор настроек на организацию, различия по языкам заданы переопределениями, а не копиями.
- Пилот на одном репозитории в режиме проверки без правок — оцениваем долю файлов, которые будут затронуты.
- Разовый прогон по репозиторию отдельным коммитом: только форматирование, без смысловых изменений.
- Подключение проверки на ветке — сначала предупреждение, затем обязательное условие слияния.
- Поэтапное включение остальных репозиториев партиями, от самых активных к архивным.
- Передача команде: профиль, команды запуска, автоправка перед коммитом в локальной среде.
Поэтапное ужесточение проверок
Обязательность вводим не сразу: репозиторий живёт в режиме предупреждения, пока проверка не перестанет срабатывать на сгенерированных файлах. Затем она становится блокирующей — непрошедшее форматирование не попадает в основную ветку, и стиль перестаёт быть предметом споров в обзорах.
Что остаётся у команды: конфигурации, правила, регламенты
Форматирование в конвейере — это не одна команда, а набор артефактов в репозитории. Их задача — чтобы новый инженер через полгода получил те же правила одной командой, без переписки с автором настройки.
Файлы конфигурации и общий профиль правил
- Единый профиль правил в корне репозитория: один конфиг на все языки, без локальных вариаций в подкаталогах — расхождение настроек между модулями уходит как класс.
- Согласование с редактором:
.editorconfigповторяет те же отступы и переносы, чтобы правки в IDE не конфликтовали с форматтером при слиянии. - Списки исключений: сгенерированный код, вендорные каталоги, миграции — форматируется только то, что команда правит руками.
- Шаг проверки в конвейере: слияние блокируется, если файл не отформатирован; автоисправление приходит отдельным коммитом.
- Хук на машине разработчика: предкоммит-проверка не пускает неотформатированный код и экономит прогоны конвейера.
Документация и порядок исключений
- Короткая инструкция в репозитории: как прогнать форматирование локально, где лежит конфиг и что делать при конфликте с линтером.
- Порядок отступлений от правил: исключение оформляется с причиной и владельцем, иначе список растёт и правила перестают действовать.
- Регламент изменений профиля: правка правил идёт отдельным изменением с прогоном по всему репозиторию, а не «по пути» в рабочей задаче.
Итог — форматирование перестаёт зависеть от договорённостей: диффы в ревью содержат смысл, а проверка воспроизводима на любой машине.
Кейс: монорепозиторий без ручных правок стиля
Заказчик пришёл с монорепозиторием: десяток сервисов на нескольких языках, а единый стиль существовал только как устная договорённость. Форматирование каждый запускал вручную, не всегда перед коммитом, и проверка кода превращалась в спор об отступах — около трети замечаний касались вида файла, а не логики.
Было
Проверка кода на средней ветке занимала около 35 минут, и почти каждый третий комментарий был про форматирование. Примерно каждый пятый коммит не менял поведение системы, а только правил отступы и переносы: такие правки оседали в общей истории и мешали разбирать изменения при откате. Конфликты слияния в файлах, которые ведут несколько команд, возникали именно из-за разного формата — разбор слияния занимал больше времени, чем сама доработка.
Стало
Форматирование перенесли в конвейер: проверка идёт до обсуждения изменений, при расхождении сборка падает и прикладывает готовый исправленный вариант. Исторические файлы прогнали отдельным коммитом через Prettier — без изменений логики, чтобы правки оставались читаемыми.
Время проверки кода сократилось с 35 до 12 минут. Доля коммитов только со стилем — меньше 2%. Конфликты слияния по форматированию почти исчезли: число случаев упало примерно в пять раз, а одинаковое содержимое в двух ветках теперь даёт идентичный файл. Обсуждение снова про логику, а не про пробелы.
Форматирование и линтинг: в чём разница и что делать с легаси
Форматтер и линтер отвечают на разные вопросы, и попытка закрыть оба одной проверкой порождает бесконечные споры в ревью. Форматтер работает с внешним видом: отступы, длина строки, кавычки, порядок импортов — он перезаписывает файл и всегда даёт одинаковый результат. Линтер смотрит на смысл: неиспользуемые переменные, недостижимые ветки, подозрительные сравнения — и правит их лишь частично.
| Критерий | Форматтер | Линтер |
|---|---|---|
| Предмет | внешний вид кода | логика и потенциальные ошибки |
| Результат | детерминированный, одинаков у всех | замечания с уровнями важности |
| Автоисправление | полное, файл перезаписывается | частичное, часть решений за инженером |
| Унаследованный код | включается одним массовым проходом | включается постепенно, по правилам |
| Источник настроек | один конфиг в репозитории | набор правил и порог срабатывания |
Разделение проверок в конвейере делают по шагам: форматирование — отдельный шаг с автоисправлением, линт — отдельный, со своим кодом возврата. Падение одного не маскирует другое, и разработчик сразу видит, что именно требуется поправить.
На давно живущем коде порядок обратный привычному. Сначала один массовый проход форматтера, вынесенный в обособленный коммит, — дальше любая правка идёт по выровненному файлу, а история изменений не засоряется стилевыми правками. Линтер включают постепенно: правило за правилом, замечания в старых модулях переводят в предупреждения, блокируют только новые.
Правила без конфликтов получаются, когда истина одна: стиль задаёт форматтер, а из набора линтера убирают всё, что дублирует оформление. Иначе инструменты тянут один и тот же код в разные стороны, и команда отключает проверки целиком — вместе с полезными.
Технические сомнения: шум в истории, чужие языки, исключения
Разовое переформатирование — это коммит на сотни файлов, где логика правок не читается, а git blame после него показывает автором того, кто запустил форматтер. Обе проблемы снимаются процедурой, а не уговорами.
Четыре сомнения, которые звучат перед включением обязательной проверки, и то, как каждое закрывается конфигурацией и процессом.
Разовый коммит «переформатировать всё» сломает историю
Проход делают одной отдельной ревизией, а её хеш вносят в файл игнорирования — история строк сохраняется, и обычный комментарий к правке остаётся осмысленным. Дальше формат меняется только в тех строках, которые и так правились.
У нас смешанный стек
Проверка разбивается по маскам: у каждого языка свой форматтер и конфиг, запускаемые только на его файлах. Что не попало ни под одну маску — не проверяется, поэтому новый язык не требует переписывать правило целиком.
Есть сгенерированное и вендорные каталоги
Игнорируются сборки, миграции, схемы и сторонние библиотеки: генерируемое не форматируем, иначе следующая генерация вернёт правки и проверка упадёт снова. Пограничный случай закрывается точечным отключением на файл или блок с комментарием о причине.
У команд разные стили
Стиль переезжает из переписки в один общий конфиг — спор становится правкой одного файла, видимой на ревью. Проверка в конвейере делает итог необсуждаемым: стили команд сходятся к зафиксированному набору правил, а не к чьему-то вкусу.
Обсудим задачу и вернёмся с оценкой и планом работ
Чтобы разговор был предметным, а не общим, нам достаточно нескольких вводных. Автоматическое форматирование почти целиком зависит от вашего репозитория: один он или десяток связанных, какие языки в нём соседствуют, что уже зафиксировано в правилах и что мешает включить проверку сегодня. Ниже — то, что помогает вернуться с оценкой и планом, а не с уточняющими вопросами.
- Состав репозиториев: где монорепозиторий, где отдельные сервисы, какие языки и в каком соотношении, есть ли общие каталоги с библиотеками.
- Текущие правила оформления: конфигурации форматтеров, настройки редактора, исключения для сгенерированного и вендорного кода, спорные файлы.
- Требования к проверкам: что должно блокировать слияние, что достаточно предупредить, какие ветки защищены и нужен ли автофикс вместо отказа.
- Доля унаследованного кода: объём файлов, которые нельзя форматировать одним коммитом, и готовность команды к крупной разовой правке.
- Порядок выката: планируете ли включать сразу на все ветки или поэтапно — сначала отчёт, затем автофикс, затем блокировка.
- Среда исполнения: где запускаются задачи сборки, есть ли ограничения по доступам и изолированные исполнители.
Опишите задачу в свободной форме — состав репозиториев, языки и текущие правила. Вернёмся с оценкой и планом работ, где порядок выката расписан по шагам и понятно, что проверить до включения блокировки.







