Налаштування Code Coverage у CI/CD: пороги, тренди та алерти

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

Чому метрика code coverage бреше в CI/CD і ламає релізи

Метрика code coverage виглядає об'єктивною рівно до моменту, коли її зіставляють з історією падінь. Команда тримає поріг покриття вище 80 %, збірка зелена, а регресії в релізі приходять стабільно.

Це не парадокс: звіт про покриття показує, які рядки виконалися хоча б раз, але мовчить про те, чи перевіряли їх на різних даних і з різними станами системи.

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

Реліз іде далі з упевненим «покриття зросло», а падає на першому нештатному сценарії — тому самому, заради якого цю вітку й написано.

Другий — тести заради числа. Коли поріг покриття перетворюється на звітний показник, з'являються перевірки, які викликають функцію і переконуються у факті виклику, а не в поведінці.

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

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

Четвертий — відсутність історії прогонів. Поріг перевіряється на кожному коміті, але тренду немає: покриття тихо сповзає з 84 до 76 % за десяток змін, кожна з яких формально проходить допуск.

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

Що дає керування покриттям: вимірювані ефекти

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

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

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

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

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

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

Підходи до контролю покриття: порівняння й обмеження

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

Рахувати всі метрики з одного прогону вміє SonarQube, тож вибір не в тому, що вимірювати, а в тому, яке правило робить блокувальну перевірку.

Підхід Коли підходить Слабкі місця
Загальний поріг покриття Рівна база, метрика змінюється повільно Новий файл без тестів розбавляє відсоток; поріг доводиться знижувати
Поріг за зміненими рядками Активна розробка, часті правки Не бачить деградацію в старому коді, який не чіпають
Трендовий контроль Великі репозиторії, де поріг уперся в стелю Потрібна накопичена історія замірів, реагує із затримкою
Режим спостереження Впровадження перевірки на наявному коді Нічого не блокує: регрес проходить, поки правило не перемкнули

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

Впровадження: від аудиту конвеєра до алертів за покриттям

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

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

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

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

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

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

  • Файл правил покриття — де оголошені охоплення, формат звіту й точки злиття даних із модульних та інтеграційних прогонів.
  • Політика винятків — перелік шляхів і типів коду, виведених із підрахунку, з обов'язковим обґрунтуванням і датою перегляду в кожного запису.
  • Шаблон звіту — зрізи за модулями, порівняння з попереднім релізом і тренд за останні прогони, а не єдина цифра.
  • Схема оповіщень — кому й куди йде сигнал при просадці, з розділенням на попередження й блокування злиття.
  • Інструкція черговому — порядок розбору падіння: перевірити нові винятки, тести, що впали, гілку з незакритими ділянками.
  • Регламент перегляду порогів — хто і за якою подією піднімає планку, щоб вона не залишилася на рівні першого запуску.
  • Карта володіння — яка команда відповідає за покриття якого модуля, щоб питання щодо винятку йшло за адресою, а не до нас.

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

Кейс: пороги й тренди прибрали регресії з релізів

До нас прийшла продуктова команда: десяток сервісів в одному репозиторії, релізи двічі на тиждень, загальне покриття близько 70% і повна довіра до цієї цифри.

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

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

Ми перенесли акцент із загального відсотка на покриття змінених рядків: у кожному запиті на злиття окремо рахувалося, яка частка доданих і змінених рядків закрита тестами.

Порогове правило у SonarQube пов'язали з обов'язковим статусом перевірки — для платіжного й білінгового модулів збірка падає, якщо змінений рядок не покритий, для інших гілок поріг м'якший.

Поруч побудували тижневий тренд і алерти на просадку, щоб якість було видно до релізу, а не після інциденту.

Метрики до і після

За три місяці покриття змінених рядків зросло з 46% до 84%, при цьому загальний відсоток майже не рухався — він і не був метою.

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

Чим відрізняється code coverage за рядками й за вітками?

Покриття рядків відповідає на питання «чи дійде виконання до цього рядка», покриття віток — «чи перевірені всі результати кожного рішення». Рядкове покриття закриває рядок, якщо виконалася хоча б одна його інструкція.

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

Розрив між метриками і є джерелом хибного покриття. Рядок із if (a && b) стає «зеленим», щойно a виявиться хибним: права частина не виконається жодного разу, а звіт покаже 100%.

Так само поводять себе ранні повернення й обробники винятків — рядки закриті, а сценарій помилки не перевірений.

Метрика Що показує Де бреше Роль у гейті
Рядки частку виконаних рядків коротке замикання, ранні повернення тренд за проєктом
Вітки усі результати рішень складні булеві вирази поріг на зміненому коді
Умови кожен операнд окремо рідко підтримується повністю точково, критичні модулі
Функції сам факт виклику якості перевірок не видно сигнал «модуль забутий»

Основною метрикою беремо покриття віток: інструменти на кшталт JaCoCo рахують його з того самого прогону, що й рядки, без додаткових тестів. Рядкове покриття залишаємо як індикатор тренду й невикористаного коду.

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

Часті сумніви інженерів перед налаштуванням покриття

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

Прогін стане вдвічі довшим — збір покриття гальмує тести?

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

Покриття малюється порожніми тестами — навіщо тоді поріг?

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

У нас моноліт і старий код — репозиторій не дотягне до загального порогу.

Загальний поріг і не потрібен. Базовий рівень фіксує поточний стан, а правило «не гірше, ніж було» застосовується до змінених файлів. Звіти зводять в одне сховище з прив'язкою до коміту, тренд будується за гілками, алерти спрацьовують на падіння щодо базової гілки.

Під час розпилювання моноліту планку піднімають за модулями, а винятки для успадкованого коду задають за шляхами з датою перегляду.

Обговорімо задачу: як налаштувати пороги й оповіщення

Налаштувати пороги й оповіщення «за замовчуванням» не можна: той самий відсоток покриття означає різне для модуля розрахунків і для коду, який просто проксіює зовнішній виклик.

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

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

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

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

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

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

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