DevOps & CI/CD

Монорепозиторий

Стратегия хранения нескольких проектов и сервисов в одном Git-репозитории с общей историей и единой версией зависимостей.

Что такое монорепозиторий

Монорепозиторий (monorepo) — подход, при котором код нескольких проектов, сервисов и библиотек живёт в одном Git-репозитории с общей историей и единым набором версий зависимостей. Противоположность — polyrepo, когда у каждого сервиса свой репозиторий.

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

monorepo/
├── apps/
│   ├── api/            # сервис на Python
│   ├── web/            # фронтенд
│   └── worker/         # обработчик очереди
├── libs/
│   ├── auth/           # общая библиотека, используется всеми
│   └── models/         # схемы данных — единый источник истины
├── infra/              # манифесты Kubernetes, Terraform
└── ci/

Зачем это нужно

  • Атомарные изменения. Правка API и всех его потребителей — один коммит и один pull request. В polyrepo это цепочка из четырёх PR в четырёх репозиториях с ручной координацией порядка мержа.
  • Одна версия зависимости. Нельзя оказаться в ситуации, где три сервиса используют три несовместимые версии общей библиотеки.
  • Переиспользование кода. Общая библиотека подключается напрямую, без публикации пакета в registry ради каждой правки.
  • Видимость. Поиск по всей кодовой базе, единый стиль, общий code review, рефакторинг через все сервисы разом.
  • Одна настройка. Линтеры, CI, pre-commit-хуки настраиваются один раз, а не копируются в сорок репозиториев с последующим расхождением.

Чем приходится платить

  • Медленный CI. Наивный подход прогоняет все тесты на любой коммит. Лечится сборкой только затронутых проектов.
  • Размер репозитория. git clone на гигабайты истории становится ощутимым.
  • Права доступа.Git выдаёт доступ к репозиторию целиком; закрыть одну папку от части команды штатными средствами нельзя — нужны CODEOWNERS и правила на уровне платформы.
  • Дисциплина границ. Ничто технически не мешает импортировать внутренности чужого сервиса напрямую — и связность растёт незаметно.

Ключевая техника: собирать только изменённое

Единственное, что делает монорепо жизнеспособным, — CI, который понимает, что именно затронул коммит. Простой вариант — фильтры по путям:

# .gitlab-ci.yml
api:test:
  script: [pytest apps/api]
  rules:
    - changes:
        - apps/api/**/*
        - libs/models/**/*      # api зависит от этой библиотеки

web:test:
  script: [npm test --prefix apps/web]
  rules:
    - changes: [apps/web/**/*]

Способ работает, но зависимости приходится перечислять руками. На масштабе переходят на инструменты, которые строят граф зависимостей сами и кешируют результаты сборки: Bazel (языко-агностичный, самый мощный и самый сложный), Nx и Turborepo (экосистема Node.js), Pants (Python).

Полезные приёмы для больших репозиториев:

git clone --filter=blob:none <url>    # без истории блобов, докачает по требованию
git sparse-checkout set apps/api      # выкачать только нужную папку

Как деплоят из монорепо

Общий репозиторий не означает общий релиз. Каждый сервис собирается в свой Docker-образ, тегируется хешем коммита, попадает в registry и выкатывается независимо. Пайплайн просто определяет, какие сервисы затронуты, и запускает сборку только для них.

Ветвление в монорепо почти всегда trunk-based: долгоживущие ветки в общем репозитории создают конфликты между командами, которые к их работе отношения не имеют.

Когда что выбирать

Монорепозиторий Polyrepo
Общий код между сервисами Много Мало или нет
Команды Работают над общим продуктом Автономны, разные релизные циклы
Атомарные кросс-сервисные правки Часто нужны Редки
Требования к CI Нужен умный, инкрементальный Простой на каждый репозиторий
Разграничение доступа Проблема Из коробки

Практический ориентир: монорепо оправдан, когда сервисы делают один продукт и постоянно тянут друг у друга общий код. Если команды автономны, релизятся по своему графику и общего кода почти нет — polyrepo проще и честнее.

Связанные термины

Готовы запустить GPU-задачу?

Запустить GPU-сервер