Налаштування Git Submodules для спільних бібліотек
Та сама бібліотека — парсер конфігів, клієнт внутрішнього API, набір компонентів інтерфейсу — живе копіями в кількох репозиторіях. Поки копії розходяться повільно, цього ніхто не помічає.
Варто випустити виправлення в одній із них, і версії перестають збігатися: у сервісі авторизації вже нова сигнатура виклику, у білінгу — стара.
Далі спрацьовує ефект «у кого свіжіше». Інженер, який першим підтягнув зміни, отримує падіння збірки на чужому коді, а решта продовжують збиратися на застарілій копії й проблеми не бачать.
Розсинхрон версій спливає на стенді або, що гірше, на проді — і шукати його починають не там, де він виник: спочатку у своєму коді, потім у залежностях, потім у середовищі збірки.
Налаштування Git Submodules прибирає саму причину. Спільна бібліотека залишається окремим репозиторієм, а проєкт зберігає не копію її коду, а посилання на конкретний коміт.
У батьківському репозиторії видно, яка версія бібліотеки зафіксована, оновлення стає усвідомленою дією з окремим записом в історії, а не побічним наслідком клонування. Одна бібліотека в репозиторіях перестає бути кількома різними бібліотеками.
Нижче розбираємо, як це виглядає на практиці: що потрапляє в .gitmodules, як вибудувати порядок оновлення, щоб підняття версії в одному проєкті не ламало збірку в сусідньому, і які перевірки поставити в автоматичну збірку, щоб розходження ловилося до злиття гілки.
Що змінюється у збірці, коли версії спільного коду під контролем
Підмодуль зберігає в батьківському репозиторії не «останню версію» бібліотеки, а вказівник на конкретну ревізію. Клонування з ініціалізацією підмодулів дає рівно той код, який проходив приймальні перевірки, — і на машині розробника, і на складальному агенті.
Тому відтворювана збірка перестає залежати від того, коли і в якому порядку хтось синхронізував залежності вручну.
Раніше спільний код жив копіями: бібліотеку переносили в кожен проєкт, правили прямо на місці й забували повернути виправлення назад. Дрейф накопичувався, і однакові на вигляд збірки відрізнялися поведінкою.
Git з підмодулями прибирає саму можливість такої різниці: у батьківському репозиторії лежить посилання, а тіло бібліотеки — лише в її власному репозиторії.
Фіксація версії бібліотеки перетворює оновлення на явну дію: змінюється вказівник, зміна потрапляє в історію й проходить рев'ю як звичайна правка. Відкат — повернення вказівника на попередню ревізію, без перезбирання інших частин проєкту.
Єдине джерело коду прибирає ручні правки чужого коду: виправлення вноситься в репозиторій бібліотеки й приходить у всі проєкти під час оновлення підмодуля.
Час збірки проєкту скорочується завдяки тому, що зникає ручна синхронізація версій і повторні розбори розходжень. Крок ініціалізації підмодулів на агенті займає секунди, а результат детермінований: однаковий вхід — однаковий артефакт.
Заодно зникає цілий клас відмов на кшталт «на моїй машині працює» — розходженню просто нізвідки взятися.
Три способи повторного використання спільного коду: порівняння підходів
Спосіб повторного використання спільного коду — це передусім питання про те, хто і як часто змінює бібліотеку.
Якщо правки йдуть з одного проєкту й релізяться разом із ним, розносити код по репозиторіях сенсу немає; якщо бібліотеку споживають п'ять команд із різним темпом релізів — потрібен контракт версії, а не спільна папка.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Підмодулі або єдиний репозиторій | Кілька репозиторіїв, бібліотека живе окремо, потрібна жорстка прив'язка проєкту до конкретної ревізії | Посилання на коміт замість гілки: оновлення лише явною командою, застаріле посилання не ламає збірку, а тихо тягне старий код |
| Реєстр пакетів | Бібліотеку споживають різні проєкти, у них свій цикл релізів і потрібні версії з діапазонами сумісності | Потребує реєстру, правил публікації та семантичного версіонування; правка стає доступною лише після випуску нової версії |
| Копія залежності в дереві | Рідкісні оновлення й вимога збиратися без звернення до зовнішніх джерел | Та сама бібліотека розходиться за версіями між проєктами, локальні патчі губляться під час наступного оновлення |
Окремо про обмеження підмодулів, які найчастіше спливають уже в процесі. Робоча копія після клонування порожня, доки не виконано рекурсивний обхід — це регулярно ламає збірку в системах безперервної інтеграції, якщо крок не прописаний явно.
Усередині підмодуля репозиторій перебуває в стані відокремленої гілки, і правка, зроблена «на місці», виїжджає комітом у гілку, про яку інженер не думав. Тому вибір варіанта варто закріпити в правилах: де припустимо змінювати версію посилання і хто робить рев'ю цієї зміни.
Порядок налаштування й перевірки підмодулів у проєкті
Підмодуль фіксує не гілку, а конкретний коміт зовнішнього репозиторію, тому «просто додати» його недостатньо. Без аудиту залежностей і єдиної схеми гілок збірка відтворюється лише в автора правок: решта отримують іншу ревізію спільної бібліотеки.
Шість кроків: від інвентаризації до передачі
-
Аудит залежностей. Інвентаризуємо спільні бібліотеки: що підключено копіюванням, що вкладеним репозиторієм, що вручну. Для кожної фіксуємо власника, частоту правок і кількість проєктів-споживачів.
-
Схема гілок. Домовляємося, звідки береться ревізія: релізні споживачі дивляться на гілку супроводу, активна розробка — на основну. У підмодулі закріплено тег або коміт, а не кінець гілки.
-
Підключення. Прописуємо шлях і спосіб отримання кожного підмодуля: чиста копія репозиторію має розгортатися однією командою, без ручних доустановлень.
-
Регламент оновлення підмодулів. Оновлення йде окремою зміною із зазначенням причини, проходить рев'ю й потрапляє в основну гілку разом із правками коду. Автоматичне «підтягнути останнє» виключено.
-
Перевірка відтворюваності. На чистому середовищі збірка йде лише із зафіксованих ревізій, нічого не підтягується повз оголошені джерела; розходження видно до випуску, а не після.
-
Перевірка перед релізом і передача. Перед випуском звіряємо ревізії з реєстром залежностей і проганяємо збірку з чистої копії — розходження блокує випуск. Віддаємо карту залежностей, правила оновлення й порядок відкатів.
Що передаємо команді після впровадження
Після впровадження в команди залишається не «налаштований репозиторій», а комплект, за яким новий інженер піднімає середовище за один прохід, а оновлення спільної бібліотеки розходиться по проєктах без ручних правок у кожному.
Нижче — склад цього комплекту й те, що в ньому перевірено на живих збірках, а не описано «з пам'яті».
- Конфігурації підмодулів — файл зі шляхами, адресами й гілкою для кожного підключення: зафіксовано тег або гілку, увімкнено часткове клонування, задано правила ігнорування локальних змін. Команда бачить, яке підключення публічне, а яке закрите.
- Карта залежностей — таблиця «проєкт → бібліотека → зафіксована версія → власник». За нею за хвилину знаходиться, які проєкти зачепить правка в спільній бібліотеці й кого потрібно попередити.
- Політика оновлення — хто рухає вказівник версії, за якою подією (випуск бібліотеки, закриття уразливості) і що має позеленіти в перевірках до злиття.
- Права доступу — матриця «роль → читання/запис» для кожного сховища: підряднику видається читання бібліотеки без права змінювати версії в основному проєкті.
- Інструкція для розробників — короткі сценарії: первинне розгортання, додавання підключення, переведення проєкту на нову версію, типові помилки та їхні ознаки.
- Порядок відкату — як повернути попередню версію бібліотеки в одному проєкті й як відкотити масове оновлення, якщо перевірки виявили регрес.
- Регламент перевірок у збірці — правила, які не дають злити зміну з «плаваючою» версією або незакритими конфліктами в підключеннях.
Комплект лежить поруч із кодом і оновлюється разом із ним: конфігурації — у репозиторії, карта й політика — у спільній документації команди. Опишіть вашу схему репозиторіїв — повернемося з оцінкою та планом впровадження.
Кейс: прибрали розсинхрон версій у трьох командах
Як було: спільна бібліотека в трьох копіях
Три продуктові команди тримали спільний модуль — типи, клієнт зовнішнього сервісу, шар логування. Кожна копіювала його у свій репозиторій і правила під себе, тому до релізу версії розходилися, і падало вже в конвеєрі.
Кожна четверта збірка зупинялася через незбіг версії бібліотеки, а на ручне зведення правок ішло до шести годин на тиждень на команду: порівняти три копії, перенести чужу правку, не зламати локальні доопрацювання.
Що змінили
Бібліотеку винесли в окремий репозиторій, підключили як Git Submodules і зафіксували в кожному продукті не гілку, а конкретний коміт — збірка стала відтворюваною.
Оновлення йде окремою задачею: інженер піднімає вказівник на нову ревізію, проганяє перевірки, і лише потім це йде в основну гілку. Ламкі зміни публікуються в мажорній гілці, продукти переїжджають на неї по черзі, а не всі одразу.
Метрики після
Частка збірок, що падали через версію бібліотеки, знизилася з чверті до поодиноких випадків — зазвичай це явний конфлікт, видимий на кроці перевірки. Ручне зведення правок зникло: правка живе в одному місці й приїжджає в продукти через оновлення вказівника.
Час збірки продукту скоротився, бо спільна частина більше не перезбирається всередині кожного репозиторію.
Чим підмодуль відрізняється від пакета і що робити при розростанні?
Підмодуль — це жорстке посилання на конкретний коміт чужого репозиторію, записане в дереві проєкту. Git не вирішує за вас, чи сумісні версії: вказівник оновлюють вручну й фіксують окремим комітом, а транзитивні залежності бібліотеки в дерево не приїжджають.
Пакет влаштований інакше — це опублікований артефакт із версією, і менеджер залежностей сам збирає узгоджений набір.
Межа проходить за ритмом випуску й кількістю споживачів. Поки бібліотека змінюється разом із застосунком, а споживач один, підмодуль дешевший: вихідні видно в дереві, правки йдуть в одному середовищі, проміжні стани не потрібно версіонувати.
Як тільки з'являється другий-третій споживач із незалежним графіком, зворотна сумісність і нумерація версій стають обов'язковими — це і є сигнал, що потрібен пакет.
Репозиторій, що розрісся, видає себе часом збірки й чергами на злиття. Повний перенос за один крок майже завжди ламає збірку: спочатку бібліотеку виносять в окремий репозиторій і публікують як пакет, а підмодуль залишають перехідним мостом для тих, хто ще не перейшов.
Споживачів переводять по одному, звіряючи контракт, і лише після цього видаляють посилання й файл .gitmodules.
Окремо про межі відповідальності: підмодуль дає всім однакові права на один репозиторій, тому команди блокують одна одну. Пакет розділяє ролі — одна команда випускає версії, решта лише підключає їх.
Перед міграцією фіксують політику версіонування й стабільний контракт, інакше часткова міграція розтягнеться на невизначений строк.
Технічні сумніви: гілки, права, відкати й оновлення
Підмодуль фіксується в батьківському репозиторії посиланням на конкретний коміт спільної бібліотеки, а не на гілку.
Саме ця властивість визначає, як поводиться проєкт під час перемикання гілок, відкатів і розмежування доступу, — і саме навколо неї виникає більшість сумнівів перед тим, як погодитися на таку схему.
Загальний принцип: ініціалізація підмодулів — явна операція (submodule update --init --recursive), а не побічний ефект клонування. Поки її не виконали, каталоги бібліотек залишаються порожніми, і збірка падає одразу й зрозуміло, а не «тихо» виїжджає на застарілому коді.
Що станеться під час перемикання гілки?
Робоче дерево підмодуля саме не переміщується — після перемикання гілки батьківського проєкту потрібно синхронізувати посилання, інакше зберете стару бібліотеку з новим кодом.
Ми закриваємо це хук-скриптом: синхронізація виконується автоматично, а розходження фіксованого посилання й фактичного стану ловиться перевіркою в безперервній інтеграції до збірки.
Як обмежити права підрядників на спільний код?
Доступ до репозиторію бібліотеки видається окремо від доступу до проєкту й лише на читання — через окремий ключ або токен обмеженої дії.
Підрядник збирає й налагоджує зі спільною бібліотекою, але не може переписати її для себе; зміни проходять через власника репозиторію й обов'язкову перевірку.
Як відкотити версію бібліотеки без перезбирання всього?
Посилання на підмодуль — це один рядок в історії батьківського проєкту. Повернення до попереднього коміту бібліотеки робиться правкою цього посилання й фіксується окремою зміною: решта коду не чіпається.
Ми тримаємо політику так, щоб відкат займав хвилини, а не перезбирання всіх сервісів, і перевіряємо його тестами перед викаткою.
Обговоримо задачу: розберемо схему й повернемося з планом
Щоб повернутися з оцінкою та планом впровадження, нам потрібна не абстрактна постановка, а фактична схема репозиторіїв: де лежать спільні бібліотеки, хто і як підключає їх зараз, які гілки вважаються стабільними.
За цими вхідними даними ми рахуємо обсяг робіт і пропонуємо порядок переведення на Git Submodules — із точками відкату й без зупинки поточних випусків.
Що корисно описати на дзвінку або надіслати заздалегідь:
- список репозиторіїв та їхніх власників: застосунки, сервіси, спільні бібліотеки, допоміжні утиліти;
- поточний спосіб підключення спільних модулів: копіювання коду, окремий пакет, вкладені репозиторії, скрипти синхронізації;
- схему розгалуження й правила випуску: які гілки довгоживучі, як проходять правки й як фіксуються версії;
- вимоги до доступів: яким командам потрібне читання бібліотек, кому дозволено змінювати вказівник на конкретний коміт;
- правила оновлення: як часто підтягувати зміни спільної бібліотеки в сервіси й хто ухвалює рішення про відкат;
- набір перевірок на збірці: що запускається під час зміни вкладеного модуля і що блокує влиття;
- обмеження середовищ: закриті мережі, дзеркала репозиторіїв, особливості збірки на боці замовника.
З цим набором ми фіксуємо схему репозиторіїв, розбиваємо роботи за етапами й надсилаємо план впровадження з критеріями готовності на кожному кроці. Опишіть задачу — повернемося з оцінкою та пропозицією щодо порядку дій.







