Монорепозиторий
Стратегия хранения нескольких проектов и сервисов в одном 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 проще и честнее.
Связанные термины
- Git-репозиторий — единица хранения, о которой идёт речь
- Git — sparse-checkout и работа с большими репозиториями
- Микросервисы и монолит — почему это не одно и то же
- CI/CD pipeline — где монорепо создаёт главную сложность
- Git Flow — trunk-based как типичная модель для монорепо
- Системы контроля версий — общий контекст
Готовы запустить GPU-задачу?
Запустить GPU-сервер