Короткий ответ
Монорепозиторий упрощает атомарные изменения сразу в нескольких сервисах и единую версию общих библиотек, но требует продуманной стратегии ветвления (обычно trunk-based с короткоживущими ветками), избирательного CI (сборка и тесты только затронутых путей) и более гранулярных прав доступа (CODEOWNERS по директориям вместо прав на весь репозиторий); однозначно лучшего варианта между моно- и мульти-репозиторием нет — выбор зависит от размера организации, степени связности сервисов и зрелости инструментов CI.
Как это работает подробнее
в монорепозитории один коммит может одновременно менять несколько сервисов и их общие зависимости, что устраняет проблему "рассинхронизации версий" между репозиториями, но создаёт нагрузку на CI (нужно понимать, какие именно сервисы затронуты изменением) и на модель доступа (не все команды должны иметь право мержить в чужие директории).
монорепозиторий снижает издержки координации между командами на общих библиотеках и упрощает атомарные кросс-сервисные изменения, но увеличивает сложность CI-инфраструктуры и требует дисциплины в правах доступа; полирепо даёт изоляцию команд "из коробки" и более простой CI на репозиторий, но порождает проблему синхронизации версий общих зависимостей и более сложные кросс-репозиторные изменения. Решение стоит принимать по данным — сколько реально происходит кросс-сервисных изменений и насколько команды готовы к общим процессам ревью.
Пример кода
# пример selective CI: запускаем тесты только для изменённых пакетов
name: ci
on: [pull_request]
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
services: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
billing: 'services/billing/**'
auth: 'services/auth/**'Что использовать на практике
- Рекомендация. типичные решения — selective/affected-based CI (запуск тестов и сборки только для изменённых пакетов на основе графа зависимостей, например через Bazel, Nx, Turborepo или кастомный скрипт на
git diff), CODEOWNERS-файлы с обязательным ревью от владельцев конкретных директорий, и trunk-based ветвление с короткоживущими фиче-ветками, так как долгоживущие ветки в монорепо с десятками сервисов быстро превращаются в болезненные мерджи.
Подводные камни
- Ошибка. переносить в монорепо подход "гонять весь CI на каждый коммит", что на масштабе десятков сервисов делает пайплайн неприемлемо медленным; не продумать права доступа заранее, из-за чего разработчик одной команды может случайно (или намеренно) смержить изменения в код чужого сервиса без ревью его владельцев; недооценить миграционные издержки — историю коммитов, open PR, интеграции с внешними системами, завязанными на старые repo URL.