Настройка монорепозитория: Turborepo, Nx или Bazel под ключ

Сборка и выпуск в одном репозитории ломаются, когда над продуктом работают несколько команд: правки мешают друг другу, релизы затягиваются. Мы настраиваем монорепозиторий под ключ — от структуры проектов до регламентов выпуска. Сборка перестаёт быть узким местом, а выпуск версии занимает минуты вместо часов.
Часто задаваемые вопросы

Почему настройка монорепозитория ломается при росте команды

Первый год единый репозиторий выглядит выигрышем: одна ветка, общий набор проверок, пакеты переиспользуют код друг друга без публикации в реестр. К моменту, когда над ним работает несколько команд, выпуск растягивается с минут до часов, а правка в одном модуле пересобирает половину репозитория.

Дело не в инструменте сборки — смена сборщика картину не меняет. Кэш инвалидируется целиком, потому что нигде не зафиксировано, какие пакеты от чего зависят: граф сборки разрастается за счёт неявных связей через общие модули утилит и типов. Воспроизводимость сборки уходит — локально задача проходит, в общей среде падает, и причину ищут вручную по логам.

Проверки на каждый коммит идут часами, потому что пересчитываются все цели, а не затронутые. Запускать их частями никто не рискует: без схемы зависимостей непонятно, что действительно можно не собирать. Время выпуска перестаёт зависеть от объёма правки и начинает зависеть от размера репозитория.

Сверху добавляются ручные шаги: порядок выката сервисов держится в голове у одного инженера, версии общих пакетов правятся руками, а расхождение окружений ловится уже на бою. Разбираем, как это закрывается схемой зависимостей, явными границами пакетов и точной инвалидацией кэша.

Что даёт выстроенная сборка: измеримые эффекты

Настройка монорепозитория измеряется не числом пакетов, а временем, которое команда тратит на ожидание и разбор поломок. Эффекты ниже видно в метриках уже в первые недели работы.

Время сборки и проверок

Граф зависимостей строится по исходникам, поэтому пересобираются только затронутые проекты, остальное отдаётся из кэша. Доля попаданий в кэш на типовом изменении выходит в пределах 70–90%, и полный прогон перестаёт быть обязательным ритуалом перед коммитом.

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

Стоимость простоя и ушедшие классы отказов

Кэш привязан к содержимому и окружению: меняется конфигурация или версия инструментов — он инвалидируется сам, а не отдаёт устаревший артефакт. Уходит класс отказов «собралось локально, упало на общей ветке».

Второй ушедший класс — рассинхрон общих библиотек: единый реестр зависимостей на репозиторий не даёт сервисам разойтись по несовместимым мажорам. Простой здесь считают в часах заблокированного конвейера и в числе людей, которые ждут разбора.

Отсюда пятый эффект: выпуск становится предсказуемым событием с понятным составом изменений, и частота выпусков растёт без роста ручной работы. Опора не на нашу оценку: Google держит основной код в одном репозитории и применяет общий граф сборки на масштабе тысяч инженеров — та же логика работает и вниз, до команды из десяти человек.

Варианты организации сборки: сравнение подходов

Оркестратор выбирают по графу зависимостей: сколько пакетов, на скольких языках, как часто меняются общие библиотеки. Ниже — четыре рабочих режима, включая вариант без оркестратора.

Вариант Когда подходит Ограничения
Оркестратор семейства Turborepo Один язык, десятки пакетов, нужны локальный кэш и простая конфигурация без обучения команды Правила видимости пакетов слабее, нестандартные сценарии сборки ложатся плохо
Nx Несколько приложений и общих библиотек, нужны строгий граф зависимостей и запрет сквозных импортов Выше порог входа: конфигурацию нужно поддерживать и ревьюить
Bazel Много языков в одном репозитории, удалённый кэш и распределённая сборка на сотни пакетов Настройка измеряется неделями, нужен выделенный инженер
Без оркестратора Один-два пакета, редкие релизы, короткий цикл сборки Локального кэша нет: каждый прогон пересобирает всё, время растёт линейно

Как выбрать режим под размер команды

Небольшой команде обычно достаточно оркестратора семейства Turborepo: локальный кэш закрывает повторные сборки, а конфигурация читается целиком за один вечер. При десятках пакетов и нескольких приложениях выигрывает Nx — правила видимости не дают приложению утащить внутренности чужой библиотеки, и граф зависимостей остаётся предсказуемым. Bazel оправдан, когда пакеты написаны на разных языках и одиночный прогон тянется десятки минут: удалённый кэш и распределённая сборка возвращают минуты, но требуют инженера, который живёт этой сборкой.

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

Настройка монорепозитория: этапы инженерной работы

Работу ведём этапами: сначала измеряем, потом меняем структуру, и только затем трогаем сборку и проверки. Переезд в один репозиторий без замеров превращается в лотерею с релизами, поэтому каждое решение у нас опирается на цифры до и после.

Аудит и схема зависимостей

  1. Аудит сборки и графа зависимостей. Замеряем полную и инкрементальную сборку, смотрим, какие пакеты тянут за собой весь репозиторий, ищем циклы и неявные связи между модулями.
  2. Границы пакетов. Размечаем, что становится самостоятельным пакетом, что — общей библиотекой, и фиксируем правила импорта: приложение не лезет во внутренности чужого модуля.
  3. План внедрения. Переводим репозиторий частями — сначала одно приложение с его зависимостями, затем остальные, без остановки релизов.

Настройка сборки и проверка

  • Оркестратор и кэш. Настраиваем Turborepo, Nx или Bazel под получившуюся структуру: кэш по содержимому входов и общий кэш для всех машин, чтобы неизменное не пересобиралось.
  • Разделение задач по затронутым проектам. Конвейер проверок гоняет тесты и сборку только для изменённых пакетов и тех, кто от них зависит, — время проверки перестаёт расти вместе с репозиторием.
  • Проверка на реальных сценариях. Прогоняем типовые изменения: правку общей библиотеки, релиз, откат, параллельные ветки. Отдельно убеждаемся, что кэш не отдаёт устаревший результат.
  • Приёмка результата и передача. Сравниваем замеры до и после, отдаём схему зависимостей, правила границ, документацию по сборке и проводим обучение команды.

Что передаём на руки: конфигурации, схемы, регламенты

Передача репозитория — это состояние, которое другая команда воспроизводит без автора настройки. Каждый артефакт ниже описывает не «как получилось», а почему сделано именно так и что сломается при изменении.

Конфигурации и схемы

  • Конфигурация сборки и кэша: явные входы и выходы задач, области влияния, правила инвалидации — повторный прогон не пересобирает неизменённые пакеты.
  • Схема зависимостей: граф пакетов, границы модулей, запрет циклических связей и импорта в обход публичного интерфейса.
  • Политики доступа к кэшу: права чтения и записи, область видимости артефактов, роли для релизной ветки и внешних подрядчиков.
  • Описание локального окружения: закреплённые версии платформы и менеджера пакетов, подъём репозитория одной командой, набор проверок перед коммитом.

Регламенты и документация

  • Регламент добавления проекта: критерии выделения нового пакета, шаблон заготовки, обязательные поля конфигурации, порядок ревью.
  • Обновление зависимостей и инструментов: порядок проверки совместимости, критерий отката, периодичность миграций.
  • Восстановление сборки: точки входа, типовые сбои кэша, холодная сборка с нуля, проверка целостности артефактов.
  • Онбординг разработчика: маршрут первого дня, разбор частых ошибок, владельцы сервисов и порядок эскалации.

Все артефакты лежат в репозитории рядом с кодом и обновляются в том же ревью, что и изменения сборки — иначе инструкция расходится с реальностью за пару недель.

Кейс: как ушёл класс отказов при сборке и выпуске

К нам пришла команда из шести продуктовых подразделений — больше сорока инженеров в одном монорепозитории на несколько приложений и общих библиотек. Сборка запускалась целиком на каждое изменение: правка в пакете с общими типами вытягивала пересборку всех продуктов, полный прогон занимал около сорока минут, а ожидание свободного сборочного агента добавляло ещё до получаса. Разработчики перестали запускать проверки локально, а выпуски шли «на удачу».

Ситуация до

Кэш сборки формально был включён, но ключ считался по всему дереву сразу, поэтому попаданий почти не случалось: доля повторных сборок держалась на 12 %, то есть почти каждый прогон считался с нуля. Отказы приходили на выпуске, а не на проверке: примерно треть релизов откатывалась из-за падения сборки или несовместимых версий общих пакетов. Корень был не в инструменте, а в схеме — у пакетов не было явных границ, зависимости шли сквозь приложения.

Что изменилось после

Мы развели монорепозиторий на слои — общие библиотеки, доменные пакеты, приложения — и зафиксировали правила зависимостей: приложение может зависеть от домена, домен от библиотеки, обратное запрещено и проверяется автоматически. Ключ кэша привязали к содержимому пакета и его зависимостей, поэтому неизменённые ветки графа переиспользуют готовый результат, а собирается только затронутая часть. Конвейер выпуска собрали на Nx с общим удалённым кэшем для всех команд.

Через квартал замер повторили на втором продукте и сверились с публичными замерами по крупным монорепозиториям — порядок цифр сошёлся, то есть результат воспроизводится, а не был случайностью одной команды. Время полной сборки сократилось с 40 до 7 минут, доля повторных сборок выросла с 12 до 78 %, отказы на выпуске — с 30 до 3 % и остаются на этом уровне.

Технические сомнения: короткие ответы инженерам

Монорепозиторий — это не «сложить папки в один репозиторий», а смена модели работы: единый граф зависимостей, общие правила сборки, общий кэш результатов. Ниже — пять вопросов, которые чаще всего задают техлиды перед стартом миграции репозиториев, и короткие ответы по существу.

Как переносится история из раздельных репозиториев?

История сохраняется: переносим не только файлы, но и коммиты с авторами, ветками и тегами релизов — археология и blame остаются рабочими. Репозитории подключаются по одному, старые до конца миграции живут в режиме только чтения, поэтому параллельная работа команд не останавливается.

Не вырастет ли время сборки после объединения?

Растёт только при неверно объявленных зависимостях между пакетами. Когда граф описан честно, пересобирается и проверяется лишь затронутая часть, а остальное поднимается из кэша по содержимому входов. Если правило получается слишком широким, сужаем области влияния пакетов, а не отказываемся от объединения.

Как переводить команды без остановки релизов?

Поэтапный переход: сначала одна команда-пионер переносит свой сервис и обкатывает правила, затем подключаются остальные. Новый код идёт уже в монорепозиторий, а старые ветки доживают свой цикл — переключение не ломает график релизов.

Как кэширование влияет на повторные проверки?

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

Кто отвечает за правила сборки после передачи поддержки?

Правила живут в репозитории как код, а не в чьей-то голове: изменение проходит через ревью, владелец каждого участка зафиксирован. При передаче поддержки отдаём конфигурации, схему графа зависимостей, документацию по типовым правкам и проводим обучение команды.

Обсудим вашу задачу сборки и выпуска

Чтобы вернуться с оценкой объёма работ, а не с общими словами, инженерам нужен срез вашей текущей сборки. По нему видно, где теряется время: на установке зависимостей, на полном пересборе пакетов при правке одного модуля или на ручном согласовании релизов между командами. Соберём этот срез за один разговор — от вас входные данные и целевые показатели, от нас план внедрения с этапами и точками отката.

  • Текущая схема репозиториев: один общий, набор отдельных или смешанная; как связаны пакеты и общие библиотеки.
  • Узкие места сборки: время полной и инкрементальной сборки, доля кэшируемых задач, что пересобирается без изменений.
  • Состав команд и зоны ответственности: кто владеет пакетами, как согласуют правки в общих модулях.
  • Ограничения по срокам выпуска: как часто релизите, есть ли окна, когда сборку нельзя останавливать.
  • Целевые показатели: время сборки и выпуска, доля покрытых проверками путей, класс отказов, который нужно исключить.
  • Инфраструктура сборки: где работают агенты — свои машины, облако или смешанно, есть ли ограничения по доступам.
  • Текущий инструмент (Turborepo, Nx или Bazel) и что в нём перестало устраивать.

Опишите задачу в том виде, в каком она есть сейчас: фрагменты конфигураций, логи сборки или просто описание процесса. Мы вернёмся с оценкой объёма работ, планом внедрения и вариантами перехода без остановки выпуска — обсудим задачу и договоримся о следующем шаге.

Чем единый репозиторий отличается от раздельных проектов?

Выбор модели репозиториев — это не про удобство разработчиков, а про то, как по системе распространяются сквозные изменения. Когда общий модуль лежит в отдельном репозитории, правка в нём превращается в цепочку согласованных релизов: обновить пакет, дождаться сборок у всех потребителей, синхронизировать версии. В едином дереве та же правка проверяется одной сборкой по графу зависимостей, и несовместимость всплывает до попадания в основную ветку.

Вариант Когда подходит Ограничения
Единый репозиторий Несколько продуктов делят библиотеки и контракты, сквозные изменения идут постоянно Растёт время сборки и клонирования; нужны кэш задач и разграничение прав по каталогам
Раздельные проекты Команды почти не пересекаются по коду и живут в своих циклах релизов Каждое сквозное изменение — серия релизов; версии общих модулей постепенно расходятся
Гибрид по доменам Есть ядро с общими модулями и независимые продукты вокруг Границы доменов приходится удерживать вручную, иначе получается тот же монолит

Изоляция команд решается не числом репозиториев, а правами доступа и границами пакетов: в едином дереве доступ выдают на каталог, а правки в чужой области отсекают проверками на слиянии. Инструменты вроде Turborepo, Nx или Bazel добавляют к дереву кэш задач и граф зависимостей — без них сборка всего репозитория на каждое изменение становится узким местом.

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

  • Складывают в один реестр всё подряд — приложения, библиотеки, служебные скрипты, черновики — и не задают границы владения. Любая правка запускает пересборку целого набора, и время сборки растёт линейно вместе с командой.
  • Настраивают кэш «чтобы был», но не следят за достоверностью его ключей: задача помечается выполненной, хотя входные данные изменились. Ложное попадание возвращает старый результат — со стороны это выглядит как загадочный откат.
  • Описывают связи между пакетами на глаз. Появляются круговые зависимости и обращения к внутренним частям чужого пакета: локально всё сходится, а на общей машине порядок сборки рассыпается.
  • Держат общий код внутри приложения, а потом копируют его между пакетами. Правка в одном месте не доезжает до второго, и поведение продуктов расходится.
  • Не разводят окружения: на машине инженера одни настройки, на сборочном сервере другие. Класс отказов «у меня работает» не исчезает, а маскируется до выпуска.
  • Переносят всё одним заходом и останавливают выпуск продукта на время переезда. Если что-то идёт не так, откатываться некуда — прежний контур уже тронут.
  • Не закрепляют регламенты: порядок добавления пакетов, зоны ответственности, требования к именованию живут в голове одного инженера. После его ухода сборка снова разъезжается.
  • Не ограничивают срок жизни собранных результатов и рабочих веток — реестр разрастается, а нужный результат теряется среди устаревших.

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

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142