Автоматичне форматування коду в CI/CD: Prettier, Black, gofmt

Ручні правки стилю відволікають розробників і гальмують випуск змін. Ми налаштовуємо автоматичне форматування коду під ключ: перевірка стилю стає частиною конвеєра, а не окремим завданням. Команда отримує єдиний стиль і повертає собі години роботи без ручних суперечок.
Часті запитання

Автоматичне форматування коду: де ламається процес

Автоматичне форматування коду майже завжди відкладають: репозиторій зростає швидше, ніж доходить черга до налаштування правил. Поки команда невелика, стиль тримається на домовленостях і уважності рев’юера — відступи, лапки, порядок імпортів, довжина рядка.

Але варто з’явитися другій мові й третьому репозиторію, як стиль роз’їжджається: кожен редактор налаштований по-своєму, а єдиного джерела правди немає.

Ціна проявляється не в суперечках про смаки, а у вимірюваних речах. Ручні правки в рев’ю — найчастіша причина зауважень: цикл обговорення витрачається на пробіли й перенесення рядків замість логіки зміни.

Шум у діфах зростає разом із кількістю зачеплених файлів — реальну зміну доводиться шукати серед переформатованих сусідніх рядків, час до злиття збільшується, а ймовірність пропустити дефект зростає.

Окрема категорія — зриви збірки через стиль. Prettier, Black і gofmt запускають на одному кроці конвеєра, перевірку стилю — на іншому, і правила на машині розробника розходяться з правилами на сервері збірки.

Підсумок — червона збірка на рядку, який автор не чіпав, і звичка перезапускати її замість розбору причини. Нижче розбираємо, як це закривається і чому у форматера має залишитися один режим запуску.

Що змінюється в команді та конвеєрі після впровадження автоформату

Форматер запускається до рев’ю, а не після нього: хук на коміт переписує файл, а перевірка в конвеєрі падає, якщо стиль розійшовся з конфігом. Рев’юер бачить лише смислові зміни — перестановку пробілів і перенесення рядків за нього вже зробив інструмент. Це змінює не швидкість окремих людей, а структуру самого процесу.

Ефект вимірний. До впровадження на правки відступів і лапок ішло від чверті до половини часу рев’ю, а діфи додавали сотні рядків шуму.

Після — частка рядків, змінених зі стилю, падає майже до нуля, а спірні правки перестають бути предметом обговорення: про них домовляються один раз у конфізі.

Єдиний стиль без ручних правок

Конфіг версіонується разом із проєктом, тому правило однаково працює в редакторі, на локальній машині та в конвеєрі. Prettier із зафіксованим набором опцій прибирає варіативність: файл приводиться до одного вигляду незалежно від того, хто його писав. Новому інженеру не потрібно вивчати чужу манеру оформлення — форматер застосовує її сам.

Менше шуму в рев’ю та правок у діфах

Менше шуму в діфі — швидше сходиться перевірка: зауваження зміщуються з оформлення на логіку та граничні випадки. Стабільність збірки зростає з тієї ж причини — розбіжності в стилі зникають зі списку причин відмови.

Перед масовим прогоном форматування виносять в окремий коміт, щоб в історії не змішувалися смислові та косметичні зміни.

Режими перевірки та правки: від локального хука до кроку збірки

Режими відрізняються тим, хто і коли платить за виправлення: локально правка коштує секунди, у конвеєрі — цикл збірки, рев’ю та повторний запуск. Тому формати зазвичай поєднують, а не вибирають один. Нижче — коли який режим доречний і де він перестає працювати.

Локальна правка до коміту

Форматування під час збереження в редакторі тримає робочу копію в узгодженому вигляді: автор не думає про стиль, діфи не засмічуються перестановкою пробілів.

Правка перед комітом через передкомітний хук проганяє форматер по змінених файлах і зупиняє коміт, якщо щось залишилося невиправленим. Вибірковий прогін по файлах важливий у великих репозиторіях: повний обхід усіх вихідних кодів на кожен коміт помітно гальмує роботу.

Перевірка як обов’язковий крок конвеєра

Тут форматер запускається в режимі перевірки, без запису, і падає з ненульовим кодом повернення. Це єдиний режим, який не можна обійти локальними налаштуваннями, — він і слугує спільним бар’єром для всіх гілок. Плата за це — правку доводиться вносити після пушу, а отже, запускати збірку заново.

Варіант Коли підходить Обмеження
Форматування під час збереження Щоденна робота, потрібен миттєвий відгук Залежить від налаштувань кожного розробника, не діє під час правок у веб-інтерфейсі
Правка перед комітом Потрібно не пускати неформатований код в історію Вимагає встановлення хуків у кожного, обходиться прапорцем пропуску перевірок
Крок конвеєра Спільний бар’єр для всіх гілок і зовнішніх учасників Помилка видна лише після пушу, потрібен повторний прогін збірки
Вибірковий прогін по змінених файлах Великі репозиторії з довгим повним обходом Потрібна коректна база порівняння гілки, інакше частина файлів випадає

Як ми вмикаємо перевірки стилю в конвеєр: етапи робіт

Форматування, увімкнене одним комітом у всі репозиторії, обнуляє історію правок: перестає бути зрозуміло, хто і навіщо змінював рядок, а огляд змін тоне в перестановці пробілів. Тому впровадження форматування ведемо окремим проєктом з оборотними кроками, що не блокують релізи.

Інвентаризація репозиторіїв і винятків

Спочатку збираємо карту: мови та їхні частки, активність, згенерований код, вендорні каталоги, файли з навмисним вирівнюванням — таблиці, схеми міграцій, розмітка. Усе, що створюється збіркою, потрапляє у винятки, інакше форматер і генератор правлять одне одного нескінченно.

  1. Інвентаризація репозиторіїв: мови, активність, згенеровані та вендорні каталоги.
  2. Спільний профіль правил — єдиний набір налаштувань на організацію, відмінності за мовами задані перевизначеннями, а не копіями.
  3. Пілот на одному репозиторії в режимі перевірки без правок — оцінюємо частку файлів, які будуть зачеплені.
  4. Разовий прогін по репозиторію окремим комітом: лише форматування, без смислових змін.
  5. Підключення перевірки на гілці — спочатку попередження, потім обов’язкова умова злиття.
  6. Поетапне включення решти репозиторіїв партіями, від найактивніших до архівних.
  7. Передача команді: профіль, команди запуску, автоправка перед комітом у локальному середовищі.

Поетапне посилення перевірок

Обов’язковість вводимо не одразу: репозиторій живе в режимі попередження, поки перевірка не перестане спрацьовувати на згенерованих файлах. Потім вона стає блокувальною — не пройдене форматування не потрапляє в основну гілку, і стиль перестає бути предметом суперечок в оглядах.

Що залишається в команди: конфігурації, правила, регламенти

Форматування в конвеєрі — це не одна команда, а набір артефактів у репозиторії. Їхнє завдання — щоб новий інженер за пів року отримав ті самі правила однією командою, без листування з автором налаштування.

Файли конфігурації та спільний профіль правил

  • Єдиний профіль правил у корені репозиторію: один конфіг на всі мови, без локальних варіацій у підкаталогах — розходження налаштувань між модулями зникає як клас.
  • Узгодження з редактором: .editorconfig повторює ті самі відступи та перенесення, щоб правки в IDE не конфліктували з форматером під час злиття.
  • Списки винятків: згенерований код, вендорні каталоги, міграції — форматується лише те, що команда править руками.
  • Крок перевірки в конвеєрі: злиття блокується, якщо файл не відформатовано; автовиправлення приходить окремим комітом.
  • Хук на машині розробника: передкомітна перевірка не пускає невідформатований код і економить прогони конвеєра.

Документація та порядок винятків

  • Коротка інструкція в репозиторії: як прогнати форматування локально, де лежить конфіг і що робити при конфлікті з лінтером.
  • Порядок відступів від правил: виняток оформлюється з причиною та власником, інакше список зростає і правила перестають діяти.
  • Регламент змін профілю: правка правил іде окремою зміною з прогоном по всьому репозиторію, а не «попутно» в робочій задачі.

Підсумок — форматування перестає залежати від домовленостей: діфи в рев’ю містять сенс, а перевірка відтворювана на будь-якій машині.

Кейс: монорепозиторій без ручних правок стилю

Замовник прийшов із монорепозиторієм: десяток сервісів кількома мовами, а єдиний стиль існував лише як усна домовленість.

Форматування кожен запускав вручну, не завжди перед комітом, і перевірка коду перетворювалася на суперечку про відступи — близько третини зауважень стосувалися вигляду файлу, а не логіки.

Було

Перевірка коду на середній гілці займала близько 35 хвилин, і майже кожен третій коментар був про форматування.

Приблизно кожен п’ятий коміт не змінював поведінку системи, а лише правив відступи та перенесення: такі правки осідали в спільній історії й заважали розбирати зміни під час відкату.

Конфлікти злиття у файлах, які ведуть кілька команд, виникали саме через різний формат — розбір злиття займав більше часу, ніж сама доробка.

Стало

Форматування перенесли в конвеєр: перевірка йде до обговорення змін, при розходженні збірка падає й додає готовий виправлений варіант. Історичні файли прогнали окремим комітом через Prettier — без змін логіки, щоб правки залишалися читабельними.

Час перевірки коду скоротився з 35 до 12 хвилин. Частка комітів лише зі стилем — менше 2%. Конфлікти злиття через форматування майже зникли: кількість випадків упала приблизно в п’ять разів, а однаковий вміст у двох гілках тепер дає ідентичний файл. Обговорення знову про логіку, а не про пробіли.

Форматування та лінтинг: у чому різниця і що робити з легасі

Форматер і лінтер відповідають на різні питання, і спроба закрити обидва однією перевіркою породжує нескінченні суперечки в рев’ю. Форматер працює із зовнішнім виглядом: відступи, довжина рядка, лапки, порядок імпортів — він перезаписує файл і завжди дає однаковий результат.

Лінтер дивиться на сенс: невикористані змінні, недосяжні гілки, підозрілі порівняння — і править їх лише частково.

Критерій Форматер Лінтер
Предмет зовнішній вигляд коду логіка та потенційні помилки
Результат детермінований, однаковий у всіх зауваження з рівнями важливості
Автовиправлення повне, файл перезаписується часткове, частина рішень за інженером
Успадкований код вмикається одним масовим проходом вмикається поступово, за правилами
Джерело налаштувань один конфіг у репозиторії набір правил і поріг спрацьовування

Розділення перевірок у конвеєрі роблять за кроками: форматування — окремий крок з автовиправленням, лінт — окремий, зі своїм кодом повернення. Падіння одного не маскує інше, і розробник одразу бачить, що саме потрібно виправити.

На давно живучому коді порядок зворотний до звичного. Спочатку один масовий прохід форматера, винесений в окремий коміт, — далі будь-яка правка йде по вирівняному файлу, а історія змін не засмічується стильовими правками.

Лінтер вмикають поступово: правило за правилом, зауваження в старих модулях переводять у попередження, блокують лише нові.

Правила без конфліктів виходять, коли істина одна: стиль задає форматер, а з набору лінтера прибирають усе, що дублює оформлення. Інакше інструменти тягнуть один і той самий код у різні боки, і команда вимикає перевірки цілком — разом із корисними.

Технічні сумніви: шум в історії, чужі мови, винятки

Разове переформатування — це коміт на сотні файлів, де логіка правок не читається, а git blame після нього показує автором того, хто запустив форматер. Обидві проблеми знімаються процедурою, а не умовляннями.

Чотири сумніви, які звучать перед увімкненням обов’язкової перевірки, і те, як кожен закривається конфігурацією та процесом.

Разовий коміт «переформатувати все» зламає історію

Прохід роблять однією окремою ревізією, а її хеш вносять у файл ігнорування — історія рядків зберігається, і звичайний коментар до правки залишається осмисленим. Далі формат змінюється лише в тих рядках, які й так правили.

У нас змішаний стек

Перевірка розбивається за масками: у кожної мови свій форматер і конфіг, які запускаються лише на її файлах. Що не потрапило під жодну маску — не перевіряється, тому нова мова не вимагає переписувати правило цілком.

Є згенероване та вендорні каталоги

Ігноруються збірки, міграції, схеми та сторонні бібліотеки: згенероване не форматуємо, інакше наступна генерація поверне правки й перевірка впаде знову. Прикордонний випадок закривається точковим вимкненням на файл або блок із коментарем про причину.

У команд різні стилі

Стиль переїжджає з листування в один спільний конфіг — суперечка стає правкою одного файлу, видимою на рев’ю. Перевірка в конвеєрі робить підсумок необговорюваним: стилі команд сходяться до зафіксованого набору правил, а не до чийогось смаку.

Обговоримо задачу та повернемося з оцінкою й планом робіт

Щоб розмова була предметною, а не загальною, нам достатньо кількох вступних. Автоматичне форматування майже цілком залежить від вашого репозиторію: один він чи десяток пов’язаних, які мови в ньому сусідять, що вже зафіксовано в правилах і що заважає увімкнути перевірку сьогодні. Нижче — те, що допомагає повернутися з оцінкою та планом, а не з уточнювальними питаннями.

  • Склад репозиторіїв: де монорепозиторій, де окремі сервіси, які мови і в якому співвідношенні, чи є спільні каталоги з бібліотеками.
  • Поточні правила оформлення: конфігурації форматерів, налаштування редактора, винятки для згенерованого та вендорного коду, спірні файли.
  • Вимоги до перевірок: що має блокувати злиття, чого достатньо попередити, які гілки захищені й чи потрібен автофікс замість відмови.
  • Частка успадкованого коду: обсяг файлів, які не можна форматувати одним комітом, і готовність команди до великої разової правки.
  • Порядок викату: чи плануєте вмикати одразу на всі гілки або поетапно — спочатку звіт, потім автофікс, потім блокування.
  • Середовище виконання: де запускаються завдання збірки, чи є обмеження щодо доступів та ізольовані виконавці.

Опишіть задачу у вільній формі — склад репозиторіїв, мови та поточні правила. Повернемося з оцінкою та планом робіт, де порядок викату розписаний за кроками і зрозуміло, що перевірити до увімкнення блокування.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1393
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1368
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    815
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1139