Чому налаштування монорепозиторію ламається зі зростанням команди
Перший рік єдиний репозиторій виглядає виграшем: одна гілка, спільний набір перевірок, пакети перевикористовують код одне одного без публікації в реєстр.
На момент, коли над ним працює кілька команд, випуск розтягується з хвилин до годин, а правка в одному модулі перезбирає половину репозиторію.
Справа не в інструменті збірки — зміна збирача картину не змінює. Кеш інвалідується цілком, бо ніде не зафіксовано, які пакети від чого залежать: граф збірки розростається за рахунок неявних зв'язків через спільні модулі утиліт і типів.
Відтворюваність збірки зникає — локально задача проходить, у спільному середовищі падає, і причину шукають вручну за логами.
Перевірки на кожен коміт тривають годинами, бо перераховуються всі цілі, а не зачеплені. Запускати їх частинами ніхто не ризикує: без схеми залежностей незрозуміло, що справді можна не збирати. Час випуску перестає залежати від обсягу правки і починає залежати від розміру репозиторію.
Зверху додаються ручні кроки: порядок викочування сервісів тримається в голові одного інженера, версії спільних пакетів правляться руками, а розходження середовищ ловиться вже на бою.
Розбираємо, як це закривається схемою залежностей, явними межами пакетів і точною інвалідацією кешу.
Що дає вибудувана збірка: вимірювані ефекти
Налаштування монорепозиторію вимірюється не кількістю пакетів, а часом, який команда витрачає на очікування та розбір поломок. Ефекти нижче видно в метриках уже в перші тижні роботи.
Час збірки та перевірок
Граф залежностей будується за вихідним кодом, тому перезбираються лише зачеплені проєкти, решту віддають із кешу. Частка влучань у кеш на типовій зміні виходить у межах 70–90%, і повний прогін перестає бути обов'язковим ритуалом перед комітом.
Перевірки йдуть тим самим графом: лінтери, типи й тести запускаються лише для змінених пакетів та їхніх споживачів. Час перевірок падає з десятків хвилин до хвилин, а черга на злиття перестає бути вузьким місцем.
Вартість простою та класи відмов, які зникли
Кеш прив'язаний до вмісту та середовища: змінюється конфігурація або версія інструментів — він інвалідується сам, а не віддає застарілий артефакт. Зникає клас відмов «зібралося локально, впало на спільній гілці».
Другий клас, який зник, — розсинхрон спільних бібліотек: єдиний реєстр залежностей на репозиторій не дає сервісам розійтися несумісними мажорами. Простій тут рахують у годинах заблокованого конвеєра і в кількості людей, які чекають на розбір.
Звідси п'ятий ефект: випуск стає передбачуваною подією зі зрозумілим складом змін, і частота випусків зростає без зростання ручної роботи.
Опора не на нашу оцінку: Google тримає основний код в одному репозиторії і застосовує спільний граф збірки на масштабі тисяч інженерів — та сама логіка працює і вниз, до команди з десяти людей.
Варіанти організації збірки: порівняння підходів
Оркестратор обирають за графом залежностей: скільки пакетів, скількома мовами, як часто змінюються спільні бібліотеки. Нижче — чотири робочі режими, включно з варіантом без оркестратора.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Оркестратор сімейства Turborepo | Одна мова, десятки пакетів, потрібні локальний кеш і проста конфігурація без навчання команди | Правила видимості пакетів слабші, нестандартні сценарії збірки лягають погано |
| Nx | Кілька застосунків і спільних бібліотек, потрібні строгий граф залежностей і заборона наскрізних імпортів | Вищий поріг входу: конфігурацію треба підтримувати й рев'юїти |
| Bazel | Багато мов в одному репозиторії, віддалений кеш і розподілена збірка на сотні пакетів | Налаштування вимірюється тижнями, потрібен виділений інженер |
| Без оркестратора | Один-два пакети, рідкісні релізи, короткий цикл збірки | Локального кешу немає: кожен прогін перезбирає все, час зростає лінійно |
Як обрати режим під розмір команди
Невеликій команді зазвичай достатньо оркестратора сімейства Turborepo: локальний кеш закриває повторні збірки, а конфігурація читається цілком за один вечір.
За десятків пакетів і кількох застосунків виграє Nx — правила видимості не дають застосунку потягнути нутрощі чужої бібліотеки, і граф залежностей залишається передбачуваним.
Bazel виправданий, коли пакети написані різними мовами й одиничний прогін тягнеться десятки хвилин: віддалений кеш і розподілена збірка повертають хвилини, але вимагають інженера, який живе цією збіркою.
Перехід між режимами не обов'язково має бути одноразовим: починаємо з графа й локального кешу, а віддалений кеш підключаємо тоді, коли прогін перестає вкладатися в комфортний час.
Налаштування монорепозиторію: етапи інженерної роботи
Роботу ведемо етапами: спочатку вимірюємо, потім змінюємо структуру, і тільки потім торкаємось збірки та перевірок. Переїзд в один репозиторій без замірів перетворюється на лотерею з релізами, тому кожне рішення в нас спирається на цифри до і після.
Аудит і схема залежностей
- Аудит збірки та графа залежностей. Заміряємо повну й інкрементальну збірку, дивимось, які пакети тягнуть за собою весь репозиторій, шукаємо цикли та неявні зв'язки між модулями.
- Межі пакетів. Розмічаємо, що стає самостійним пакетом, що — спільною бібліотекою, і фіксуємо правила імпорту: застосунок не лізе в нутрощі чужого модуля.
- План впровадження. Переводимо репозиторій частинами — спочатку один застосунок з його залежностями, потім решту, без зупинки релізів.
Налаштування збірки та перевірка
- Оркестратор і кеш. Налаштовуємо Turborepo, Nx або Bazel під структуру, що вийшла: кеш за вмістом входів і спільний кеш для всіх машин, щоб незмінне не перезбиралося.
- Розподіл задач за зачепленими проєктами. Конвеєр перевірок ганяє тести й збірку лише для змінених пакетів і тих, хто від них залежить, — час перевірки перестає зростати разом із репозиторієм.
- Перевірка на реальних сценаріях. Прогоняємо типові зміни: правку спільної бібліотеки, реліз, відкат, паралельні гілки. Окремо переконуємось, що кеш не віддає застарілий результат.
- Приймання результату та передача. Порівнюємо заміри до і після, віддаємо схему залежностей, правила меж, документацію зі збірки й проводимо навчання команди.
Що передаємо на руки: конфігурації, схеми, регламенти
Передача репозиторію — це стан, який інша команда відтворює без автора налаштування. Кожен артефакт нижче описує не «як вийшло», а чому зроблено саме так і що зламається при зміні.
Конфігурації та схеми
- Конфігурація збірки та кешу: явні входи й виходи задач, області впливу, правила інвалідації — повторний прогін не перезбирає незмінені пакети.
- Схема залежностей: граф пакетів, межі модулів, заборона циклічних зв'язків та імпорту в обхід публічного інтерфейсу.
- Політики доступу до кешу: права читання й запису, область видимості артефактів, ролі для релізної гілки та зовнішніх підрядників.
- Опис локального середовища: закріплені версії платформи й менеджера пакетів, підйом репозиторію однією командою, набір перевірок перед комітом.
Регламенти та документація
- Регламент додавання проєкту: критерії виділення нового пакета, шаблон заготовки, обов'язкові поля конфігурації, порядок рев'ю.
- Оновлення залежностей та інструментів: порядок перевірки сумісності, критерій відкату, періодичність міграцій.
- Відновлення збірки: точки входу, типові збої кешу, холодна збірка з нуля, перевірка цілісності артефактів.
- Онбординг розробника: маршрут першого дня, розбір частих помилок, власники сервісів і порядок ескалації.
Усі артефакти лежать у репозиторії поруч із кодом і оновлюються в тому самому рев'ю, що й зміни збірки — інакше інструкція розходиться з реальністю за пару тижнів.
Кейс: як зник клас відмов при збірці та випуску
До нас прийшла команда з шести продуктових підрозділів — понад сорок інженерів в одному монорепозиторії на кілька застосунків і спільних бібліотек.
Збірка запускалася цілком на кожну зміну: правка в пакеті зі спільними типами витягувала перезбірку всіх продуктів, повний прогін займав близько сорока хвилин, а очікування вільного складального агента додавало ще до пів години. Розробники перестали запускати перевірки локально, а випуски йшли «на удачу».
Ситуація до
Кеш збірки формально був увімкнений, але ключ рахувався за всім деревом одразу, тому влучань майже не траплялося: частка повторних збірок трималася на 12%, тобто майже кожен прогін рахувався з нуля.
Відмови приходили на випуску, а не на перевірці: приблизно третина релізів відкочувалася через падіння збірки або несумісні версії спільних пакетів. Корінь був не в інструменті, а в схемі — у пакетів не було явних меж, залежності йшли крізь застосунки.
Що змінилося після
Ми розвели монорепозиторій на шари — спільні бібліотеки, доменні пакети, застосунки — і зафіксували правила залежностей: застосунок може залежати від домену, домен від бібліотеки, зворотне заборонено й перевіряється автоматично.
Ключ кешу прив'язали до вмісту пакета та його залежностей, тому незмінені гілки графа перевикористовують готовий результат, а збирається лише зачеплена частина. Конвеєр випуску зібрали на Nx зі спільним віддаленим кешем для всіх команд.
За квартал замір повторили на другому продукті й звірилися з публічними замірами за великими монорепозиторіями — порядок цифр зійшовся, тобто результат відтворюється, а не був випадковістю однієї команди.
Час повної збірки скоротився з 40 до 7 хвилин, частка повторних збірок зросла з 12 до 78%, відмови на випуску — з 30 до 3% і залишаються на цьому рівні.
Технічні сумніви: короткі відповіді інженерам
Монорепозиторій — це не «скласти папки в один репозиторій», а зміна моделі роботи: єдиний граф залежностей, спільні правила збірки, спільний кеш результатів. Нижче — п'ять питань, які найчастіше ставлять техліди перед стартом міграції репозиторіїв, і короткі відповіді по суті.
Як переноситься історія з окремих репозиторіїв?
Історія зберігається: переносимо не лише файли, а й коміти з авторами, гілками та тегами релізів — археологія та blame залишаються робочими.
Репозиторії підключаються по одному, старі до кінця міграції живуть у режимі лише для читання, тому паралельна робота команд не зупиняється.
Чи не зросте час збірки після об'єднання?
Зростає лише за неправильно оголошених залежностей між пакетами. Коли граф описано чесно, перезбирається й перевіряється лише зачеплена частина, а решта піднімається з кешу за вмістом входів.
Якщо правило виходить занадто широким, звужуємо області впливу пакетів, а не відмовляємося від об'єднання.
Як переводити команди без зупинки релізів?
Поетапний перехід: спочатку одна команда-піонер переносить свій сервіс і обкатує правила, потім підключаються решта. Новий код іде вже в монорепозиторій, а старі гілки доживають свій цикл — перемикання не ламає графік релізів.
Як кешування впливає на повторні перевірки?
Повторні перевірки перевикористовують результат, якщо входи задачі не змінилися, — той самий набір тестів не ганяється двічі на різних гілках і агентах.
Віддалений кеш спільний для складальних машин і локальних середовищ, тому розробник бачить результат, уже порахований на сервері.
Хто відповідає за правила збірки після передачі підтримки?
Правила живуть у репозиторії як код, а не в чиїйсь голові: зміна проходить через рев'ю, власник кожної ділянки зафіксований. Під час передачі підтримки віддаємо конфігурації, схему графа залежностей, документацію з типових правок і проводимо навчання команди.
Обговорімо ваше завдання зі збірки та випуску
Щоб повернутися з оцінкою обсягу робіт, а не із загальними словами, інженерам потрібен зріз вашої поточної збірки. За ним видно, де губиться час: на встановленні залежностей, на повному перезбиранні пакетів при правці одного модуля чи на ручному узгодженні релізів між командами.
Зберемо цей зріз за одну розмову — від вас вхідні дані та цільові показники, від нас план впровадження з етапами й точками відкату.
- Поточна схема репозиторіїв: один спільний, набір окремих чи змішана; як пов'язані пакети та спільні бібліотеки.
- Вузькі місця збірки: час повної та інкрементальної збірки, частка кешованих задач, що перезбирається без змін.
- Склад команд і зони відповідальності: хто володіє пакетами, як узгоджують правки в спільних модулях.
- Обмеження за строками випуску: як часто релізите, чи є вікна, коли збірку не можна зупиняти.
- Цільові показники: час збірки та випуску, частка шляхів, покритих перевірками, клас відмов, який потрібно виключити.
- Інфраструктура збірки: де працюють агенти — власні машини, хмара чи змішано, чи є обмеження щодо доступів.
- Поточний інструмент (Turborepo, Nx чи Bazel) і що в ньому перестало влаштовувати.
Опишіть завдання в тому вигляді, у якому воно є зараз: фрагменти конфігурацій, логи збірки або просто опис процесу. Ми повернемося з оцінкою обсягу робіт, планом впровадження та варіантами переходу без зупинки випуску — обговоримо завдання й домовимось про наступний крок.
Чим єдиний репозиторій відрізняється від окремих проєктів?
Вибір моделі репозиторіїв — це не про зручність розробників, а про те, як системою поширюються наскрізні зміни.
Коли спільний модуль лежить в окремому репозиторії, правка в ньому перетворюється на ланцюжок узгоджених релізів: оновити пакет, дочекатися збірок у всіх споживачів, синхронізувати версії.
У єдиному дереві та сама правка перевіряється однією збіркою за графом залежностей, і несумісність випливає до потрапляння в основну гілку.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Єдиний репозиторій | Кілька продуктів ділять бібліотеки й контракти, наскрізні зміни йдуть постійно | Зростає час збірки та клонування; потрібні кеш задач і розмежування прав за каталогами |
| Окремі проєкти | Команди майже не перетинаються кодом і живуть у своїх циклах релізів | Кожна наскрізна зміна — серія релізів; версії спільних модулів поступово розходяться |
| Гібрид за доменами | Є ядро зі спільними модулями та незалежні продукти навколо | Межі доменів доводиться утримувати вручну, інакше виходить той самий моноліт |
Ізоляція команд вирішується не кількістю репозиторіїв, а правами доступу та межами пакетів: у єдиному дереві доступ видають на каталог, а правки в чужій області відсікають перевірками на злитті.
Інструменти на кшталт Turborepo, Nx або Bazel додають до дерева кеш задач і граф залежностей — без них збірка всього репозиторію на кожну зміну стає вузьким місцем.
Ціна помилки в спільних правилах збірки найвища: невдалий крок у єдиному дереві зупиняє одразу всі команди, тому правила тримають поруч із кодом і змінюють окремою перевірюваною зміною.
Ми починаємо з карти залежностей і частоти наскрізних правок — вона й визначає, де проходить межа між спільним деревом і окремими проєктами.







