Збірка монорепозиторію: де втрачається час команди
Монорепозиторій починається як зручне рішення: спільні бібліотеки лежать поруч із сервісами, правила перевірки коду єдині, наскрізна правка їде одним набором змін.
Проблема проявляється зі зростанням — десятки сервісів і пакетів зв'язуються спільним графом залежностей, і зміна в одному модулі формально зачіпає сусідів через спільні бібліотеки, контракти та схеми даних.
Далі вмикається ефект «збірки всього». Система безперервної інтеграції бачить нову зміну в репозиторії та запускає перевірку всіх модулів: лінтери, тести, збірку образів.
Прогін займає десятки хвилин, а паралельні гілки вишиковують чергу збіркових задач — агенти зайняті чужими прогонами, свіжий коміт чекає на звільнення ресурсів, хоча його правки стосуються одного пакета.
Тут і проявляється вузьке місце конвеєра збірки.
Тривала збірка проєкту перестає бути незручністю та стає гальмом для всієї команди: зворотний зв'язок щодо коду надходить із затримкою в години, розробник перемикається на іншу задачу, конфлікти злиття накопичуються, а зламана зміна виявляється тоді, коли контекст уже втрачено.
Інкрементальні прогони та кеш проміжних результатів згладжують картину, але не усувають причину: конвеєр не розрізняє зачеплені та незачеплені модулі, тому платить за все.
Розбираємо, як це закривається: побудовою графа залежностей між модулями, відбором лише зачеплених, кешуванням за вмістом файлів і розділенням черги за пріоритетом гілок.
Що дає збірка лише зачеплених модулів
Збірка зачеплених модулів спирається на граф залежностей репозиторію: за кожної зміни система обчислює, які проєкти зачеплені — сам модуль і все, що залежить від нього транзитивно.
Інструменти на кшталт Nx тримають такий граф в актуальному стані та перераховують його за вихідними кодами, а не за ручними списками. Усе, що залишилося за межами зачепленого набору, до перевірки не потрапляє.
Скільки часу економить перевірка за змінами
Час перевірки змін перестає залежати від розміру репозиторію та починає залежати від розміру правки: зміна в листовому модулі тягне 3-5 задач замість сотень.
Кеш результатів збірки підсилює ефект — якщо входи задачі не змінювалися (вихідні коди, конфігурація, оточення), результат підставляється з кешу і задача не виконується взагалі. Гілка з точковою правкою проходить перевірку за одиниці хвилин там, де раніше йшли десятки.
Як падає навантаження на збіркові машини
Паралельні задачі збірки розбираються за графом: агенти отримують незалежні вітки та не перезбирають спільний префікс залежностей заново.
Відмова від повного перезбирання прибирає пікову чергу — замість сотні задач на кожне злиття приходить 5-10, а звільнені слоти йдуть на одночасну перевірку кількох гілок.
Кеш спільний для всіх агентів: повторна задача забирає готовий артефакт, а не запускає компіляцію заново, тому збіркові машини перестають бути вузьким місцем зі зростанням кількості команд.
Режими збірки в монорепозиторії: порівняння варіантів
Вибір режиму збірки визначає, скільки часу чекає інженер після надсилання змін і скільки ресурсів з'їдає збірковий конвеєр. Нижче — реальні альтернативи, між якими обирають команди, та їхні обмеження.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Повна збірка репозиторію | Невеликий монорепозиторій, рідкісні коміти | Час зростає лінійно з кількістю модулів |
| Вибіркова збірка модулів | Десятки сервісів, часта інтеграція | Потрібен точний граф залежностей: помилка в ньому ламає реліз |
| Кешування артефактів збірки | Стабільний стек, рідко змінювані залежності | Промах кешу повертає повний час збірки |
| Розподілена збірка | Багато незалежних модулів, тривалі тести | Складніше налагодження, потрібні ізольовані агенти |
| Розділення на кілька репозиторіїв | Слабко пов'язані домени, різні команди | Втрачається атомарність змін, контракти версіонуються вручну |
Спільний кеш і граф залежностей
Граф будується за маніфестами модулів, і від його точності залежить, які задачі взагалі потраплять до збірки. Кеш артефактів живе на спільному сховищі та перевикористовується між гілками: повторна збірка незміненого модуля займає секунди замість хвилин.
Якщо граф описує неявні зв'язки неповно, частина залежних модулів не перезбирається — і дефект випливає вже в робочому середовищі.
Коли вигідніше розділити репозиторії
Розділення виправдане, коли домени майже не ділять код: тоді вибіркова збірка поступається місцем незалежним конвеєрам в окремих репозиторіях. Ціна — спільні бібліотеки доводиться версіонувати й оновлювати вручну, а наскрізна зміна проходить через кілька погоджень. Поки частка спільних правок висока, монорепозиторій з affected-збіркою дешевший в експлуатації.
Як ми впроваджуємо збірку за зачепленими модулями
Конвеєр у монорепозиторії за замовчуванням перезбирає все. Поки модулів десяток, це терпимо, але на сотні пакетів збірка перестає встигати за темпом правок: черга зростає, а тест, що впав у чужому модулі, блокує випуск. Порядок робіт нижче вибудований так, щоб запускалися лише зачеплені модулі та їхні споживачі.
Від аудиту до вибіркової збірки
- Аудит конвеєра збірки. Знімаємо часи етапів, відзначаємо, що кешується, і фіксуємо метрики до змін — інакше ефект потім не буде з чим порівняти.
- Карта залежностей модулів. Розбираємо зв'язки між пакетами, спільні бібліотеки та циклічні зав'язки, через які вибірковий запуск дає пропуски.
- Налаштування вибіркової збірки. Прив'язуємо запуск до змінених шляхів і правил: правка в спільній бібліотеці тягне за собою всіх її споживачів, готові артефакти перевикористовуються.
- Налагодження хибних запусків. Проходимо пограничні випадки — зміни конфігурації, схем даних, транзитивних залежностей, коли збирається зайве або пропускається потрібне.
Пілот і критерії приймання
- Пілот на одному напрямі. Вмикаємо режим для однієї команди та звіряємо результат із повною збіркою: пропущені збірки видно до розкочування на весь репозиторій.
- Передання регламентів команді. Описуємо, як додавати модуль і оголошувати залежності; далі конвеєр супроводжує команда без нашої участі.
Критерій приймання — жодного пропущеного модуля, який реально зачеплений, і поодинокі зайві запуски. Далі час очікування в черзі перестає залежати від розміру репозиторію.
Артефакти та регламенти, що залишаються в репозиторії
Граф залежностей і правила запуску збірок працюють, поки зафіксовані в репозиторії, а не в листуванні й не в голові одного інженера.
Тому все, що описує збірку, лежить поруч із кодом і проходить той самий регламент перевірок: правку в правилах видно в історії, її можна обговорити й відкотити разом із релізом.
- Конфігурації збіркових задач — декларативний опис кроків і умов запуску: за якою гілкою збираємо, які перевірки обов'язкові, що пропускається для правок у документації.
- Схема залежностей — граф пакетів і зв'язків між ними; за ним обчислюється, які збірки та тести зачеплені зміною, а які запускати не потрібно.
- Регламент перевірок коду — обов'язковий набір статичного аналізу та тестів із порогами покриття до злиття, порядок винятків і те, хто їх затверджує.
- Політики доступу до збіркових агентів — хто має право запускати збірку, змінювати правила, читати секрети; де зберігаються ключі, як видаються та як відкликаються при виході людини.
- Документація зі збірки — устрій конвеєра, де дивитися журнали та метрики, які відмови типові та що робити при падінні на кожному етапі.
- Інструкція для розробників — як відтворити збірку локально, як додати пакет і не зламати граф, які угоди про найменування гілок і комітів прийняті.
- Регламент чергування — хто реагує на збірку, що впала, за який час, коли конвеєр зупиняють і як про це повідомляють команді.
Цей набір залишається в команди після запуску: його не відновлюють із пам'яті, а нового інженера вводять у процес за дні, а не тижні.
Кейс: монорепозиторій на десятки модулів і час перевірки
Замовник прийшов із монорепозиторієм на кілька десятків модулів, де кожен коміт запускав збірку та перевірку всього дерева.
Медіана перевірки трималася близько години, черга на збіркові машини зростала швидше, ніж команда, і розробники почали накопичувати правки у великі пакети — аби лише не чекати конвеєр зайвий раз.
Заміри знімали двічі та незалежно. Спершу за журналами GitLab CI — час кожної стадії на тому самому наборі змін до й після перебудови. Потім прогнали ті самі коміти на копії репозиторію та окремому виконавці, щоб виключити вплив кешу та сусідніх задач. Обидва заміри зійшлися в межах похибки — результат відтворюваний, а не разовий вдалий прогін.
Що саме змінилося в конвеєрі
- Зібрали граф залежностей модулів і тримаємо його в репозиторії поруч із кодом.
- Перевірка йде лише за зачепленими модулями та тими, хто від них залежить; решта позначається пропуском.
- Кеш артефактів прив'язали до вмісту модуля, а не до номера збірки, — він перевикористовується між гілками.
- Швидкі перевірки відсікають наперед непрохідні зміни до важких задач.
- Паралельність обмежили за кількістю збіркових машин, щоб черга не з'їдала виграш.
Після впровадження медіана перевірки впала з години до лічених хвилин, а збірки, що зачіпають менше десятої частини модулів, стали переважними. Навантаження на збіркові машини знизилося настільки, що звільнені потужності віддали під нічні прогони.
Окремо пішов клас відмов, який списували на «нестабільні тести»: падіння йшли зі спільного стану та кешу між модулями, тепер у кожної задачі ізольований робочий каталог.
Що робити, якщо збірка вже розрослася до десятків модулів
Так, перехід на вибіркову збірку в монорепозиторії робиться без зупинки розробки — за однієї умови: вибірковий контур спочатку вважається паралельно з повним прогоном і не впливає на статус основної гілки.
Порівняння двох прогонів на тій самій зміні показує, які модулі система вважає зачепленими і де граф залежностей бреше.
Застарілі модулі виносять окремим кроком: виводять з обов'язкової збірки, фіксують останню робочу точку та тримають архівною гілкою. Спершу перевіряють, що на них не посилається живий код — прихована залежність випливає саме після виключення.
| Крок | Що зачіпає | Чим ризикуємо |
|---|---|---|
| Інвентаризація залежностей | граф модулів, спільні бібліотеки | неповний граф: правка бібліотеки не тягне споживачів, гілка виглядає зеленою |
| Спостереження | лише звіти, контур не змінюється | розбіжність у правилах визначення зачеплених модулів |
| Переведення частини модулів | правила за шляхами, кеш проміжних артефактів | спільна бібліотека змінюється — перезбирати доводиться всіх споживачів |
| Заморожування застарілих модулів | виключення з обов'язкових перевірок | приховане посилання живого коду на заморожений |
| Повне переведення | основна гілка, обов'язкові перевірки | втрата страховки: потрібен швидкий відкат до повного прогону |
Кожен крок відкочується однією правкою правил, а повний прогін знімається останнім — коли вибіркова збірка сходиться з ним за результатами. Підсумок: типова зміна перезбирає одиниці модулів замість десятків, основна гілка не простоює.
Часті технічні питання перед стартом робіт
Перед стартом інженерні команди ставлять приблизно той самий набір питань: наскільки повні перевірки за вибіркової збірки, що відбувається зі спільними бібліотеками та як конвеєр живе після передання. Відповідаємо коротко — по суті.
Як переконатися, що вибіркова збірка не пропускає потрібні перевірки?
Граф залежностей будується за маніфестами пакетів і імпортами в коді, а не за структурою каталогів. Змінені шляхи зіставляються з пакетами, і до збірки потрапляють усі, хто залежить від них транзитивно.
Поверх цього на захищених гілках завжди йде повний прогін — він і слугує еталоном, з яким звіряємо вибірковий результат.
Що буде, якщо змінилася спільна бібліотека?
Зміна в спільному пакеті позначає всіх його споживачів, тому вони перезбираються разом із ним. Окремо обробляємо «глобальні» правки — файли фіксації версій, конфігурацію збірки та кореневі правила: вони запускають повний прогін, бо впливають на весь репозиторій.
А якщо перевірка все-таки пропуститься?
Хибні пропуски ловимо порівнянням списку зачеплених пакетів із повним прогоном на релізних гілках. Кожну розбіжність розбираємо та закриваємо правилом мапінгу або явним винятком. Такі розбіжності фіксуються в журналі — видно, що причина пішла, а не повторилася за місяць.
Хто підтримує конвеєр після запуску?
Конфігурація лежить у репозиторії поруч із кодом і змінюється через звичайне рев'ю. Правила зіставлення, схема кешу та порядок додавання нового пакета описані в документації, тому команда заводить сервіси за шаблоном без нас.
Ролі розділені: розробник запускає збірки та читає логи, правка спільних правил і секретів — через окремий доступ і погодження.
Обговоримо задачу: що уточнити до старту
Щоб оцінка робіт зі збірки спиралася на факти, а не на припущення, нам потрібні вихідні дані щодо репозиторію: скільки в ньому пакетів і застосунків, де лежать спільні бібліотеки та хто їх перевикористовує.
За цими відомостями ми рахуємо, які перевірки запускати лише для зачеплених частин коду, а які — завжди, і готуємо план впровадження з реалістичними строками.
Нижче — те, що варто уточнити до старту. Якщо частина даних поки недоступна, обговоримо задачу за тим, що є: пробний прогін на одному пакеті зазвичай показує реальну картину швидше, ніж повний опис словами.
- Устрій репозиторію: кількість пакетів, межі між ними, спільні модулі та зв'язки між збірками.
- Схема гілок: які гілки вважаються основними, як оформлюються та зливаються зміни.
- Поточний час перевірки: скільки йде повний прогін і скільки — перевірка однієї правки.
- Найдовші та найкрихкіші перевірки: що найчастіше падає і чому.
- Обмеження щодо доступів: права до репозиторію, збіркових вузлів і сховища артефактів.
- Очікування щодо строків впровадження: що критично отримати першим, а що можна відкласти.
Опишіть задачу у вільній формі — повернемося з оцінкою та планом робіт, без загальних слів.







