
Привет, Хабр! На связи Никита Ли, frontend-разработчик в Рунити.
За последние годы мы в Рунити пришли к довольно привычной для крупных frontend-команд ситуации: проектов становилось больше, кодовая база разрасталась, а количество переиспользуемых пакетов и микрофронтендов росло слишком быстро. Поддерживать зоопарк репозиториев становилось всё сложнее — и по времени, и по нервам.
В этой статье расскажу, с какими проблемами мы столкнулись, зачем вернулись к одному репозиторию и что Nx реально изменил в нашей работе.
Навигация по тексту:
С чем мы столкнулись при внедрении микрофронтендов
Наш рабочий флоу стал выглядеть примерно так, и для того чтобы обновить размер шрифта или иконку, приходилось тратить до трёх дней — просто в ожидании апрувов от всех причастных.

Были и другие, не менее значимые проблемы, с которыми мы с командой разработки сталкивались практически ежедневно:
Кошмарный клубок зависимостей. Половина микрофронтендов сидит на устаревшей бета-версии пакета, в котором уже произошло несколько breaking changes, но обновлять и актуализировать код никто не спешит.
Конфиги, конфиги, конфиги. Они заполоняют корни репозиториев дублями: webpack.config.js, eslint.config.js, tsconfig.json, gitlab-ci.yml и так далее. Попробуйте добавить новое правило в ESLint без танцев с бубном.
Пик DX. Чтобы разрабатывать микрофронтенд, его нужно пересобрать локально и залинковать. После — дождаться CI, который вне зависимости от характера изменений прогоняется везде и всегда.
Когда проекты живут в отдельных репозиториях, любая общая логика довольно быстро превращается в источник постоянной боли. Общие пакеты приходится обновлять вручную, следить за версиями и цепочкой зависимостей, синхронизировать изменения. Чем теснее связаны проекты между собой, тем дороже становится любое изменение.
Особенно это чувствуется в микрофронтенд-архитектуре, когда пакетов много, а изменений требуется еще больше.
В какой-то момент стало понятно, что проблема не в конкретных инструментах или людях, а в самой модели хранения и обновления кода. Так мы и пришли к решению переехать в монорепозиторий на Nx.
Почему именно монорепозиторий
Важно учесть, что монорепозиторий — это не монолит. Мы распилили наш frontend-монолит на микрофронтенды, уйдя в отдельные репозитории для каждого проекта, но пришли опять к единственному репозиторию. Однако подходы отличаются: монорепозиторий — просто хранилище, по сути, изолированных пакетов и приложений с отдельными независимыми деплоями.
Монорепозиторий — это не серебряная пуля и подойдёт не всем. Он не делает систему автоматически проще и точно не подходит для любого масштаба. Но в нашем случае он позволял решить сразу несколько ключевых проблем:
Один MR на все зависимости без дополнительных бампов с ревью одного места;
Единый источник правды, где все приложения используют ui-kit из packages/ui-kit, а не из npm-реестра с рандомными версиями;
Shared-конфиги, и все приложения линтятся, тестируются и собираются по одним и тем же правилам;
Легче вносить изменения, когда не надо искать все места, где используется функционал, а IDE сама подсказывает возможные места ошибок.
Почему Nx
С тем, что монорепозиторий мы хотим, разобрались, но просто объединить код в один репозиторий не получится: пакеты и приложения надо разграничивать, а в идеале ещё и управлять кодом, сборкой и предоставлять удобные инструменты для генерации. Предлагаю кратко рассмотреть некоторые доступные альтернативы:
Пакетные менеджеры (workspaces npm, pnpm, yarn...) — простые инструменты для небольших монорепозиториев с базовыми кейсами отделения пакетов от приложений в рамках одного репозитория и управления их зависимостями отдельно;
Lerna — один из первых инструментов для работы с JS-монорепозиториями, используется чаще как более интересная версия workspaces для публикации пакетов и параллельных тасков. Поддерживается командой Nx;
Turborepo — более продвинутый инструмент для работы с задачами параллельно и с кэшем, имеет невысокую сложность настройки и хорошую производительность благодаря Rust. Является отличным выбором начиная от малых до крупных проектов, особенно актуален для Next.js, так как поддерживается инструмент командой Vercel;
Nx — наиболее популярный инструмент, выросший из экосистемы Angular, доступный на сегодняшний день практически любому frontend и не только стеку, с самым широким спектром предлагаемых возможностей: от распределенного выполнения и cloud-кэширования до генераторов и affected-команд с расширенным графом зависимостей.
По данным профильного опроса state of JavaScript 2025 большинству хватает встроенных возможностей пакетных менеджеров для управления зависимостями и построения монорпеозиториев.

Мы в свою очередь для своих целей выбрали Nx и вот почему:
Поддержка module federation для наших микрофронтендов с конфигурацией host/remote и shared-пакетами, которая работает из коробки;
Пайплайны выполнения задач при наличии зависимостей выручают, так как для сборки одного приложения автоматически затрагиваются все его зависимости;
Отслеживание графа зависимостей пакетов и приложений для проектирования и устранения циклических импортов реально облегчает жизнь;
Affected — запускает только те задачи, которые действительно нужны. То есть при сборке или тестах в CI будут выполняться только затронутые проекты, а не весь репозиторий;
Кэширование задач, которое может работать локально и в облаке (в том числе и своем). Сборки пакетов или тесты без изменений при необходимости будут браться из кэша Nx менее чем за одну секунду;
Генераторы, позволяющие развернуть новое уже настроенное приложение или библиотеку одной командой.
Цена переезда: долго и затратно
Важно сразу сказать: переезд в монорепозиторий — это не быстрый и не дешевый процесс. Это действительно долго и требует вложений времени команды. В нашем случае переезд занял около 12 недель.
Мы изначально понимали, что если делать это «в лоб», то пострадает продуктовая разработка. Поэтому переезд шёл параллельно с основной работой: мы закладывали время на погашение технического долга и постепенно переносили проекты в новую структуру. Это позволило не останавливать развитие продукта и не создавать критичных рисков для бизнеса.

Где монорепозиторий может не взлететь
Хочется сказать, что монорепозиторий и Nx — не универсальное решение. Если ваша команда и проекты небольшие (<5 проектов), то использование Nx может быть неоправданно сложным и дорогим. Или наоборот, если у вас гигантский продукт с разнообразным технологическим стеком и необходимостью использования большого количества установленных пакетов, то выгода от использования Nx снижается, а нагрузка при работе с подобным репозиторием будет высокой — как когнитивно, так и для локальных машин или CI.
Командные нюансы также имеют место при обсуждении: командам с большими MR и крупными релизами слишком часто придется актуализировать ветки и решать конфликты в монорепозитории, а при слишком частых деплоях будет затрагиваться общий код, увеличивая время ожидания пайплайнов и задевая все команды.
Ну и вишенкой на торте станет необходимость тонкой настройки инструмента, управляющего монорепозиторием. В случае с Nx это сотни строк конфига переплетающихся плагинов и задач. Настраивать так же тонко нужно и CI/CD, так как монорепозиторий, в котором на каждый коммит в MR будут проходить условные тесты, линтер и сборка на все затронутые проекты, будет потреблять достаточно много ресурсов. Если ваш GitLab с его раннерами или другая система не будут к этому готовы, то уже 2–3 одновременных MR с изменениями shared-пакета, который стриггерит еще с десяток приложений для тестирования, просто могут положить вашу инфраструктуру.
Что мы получили в итоге
После переезда стало заметно проще выносить общие пакеты и переиспользовать их между проектами без лишней головной боли. Архитектура стала более прозрачной, а изменения — более предсказуемыми. Мы не избавились от всех проблем разом, но существенно снизили операционные издержки на поддержку frontend-экосистемы.
Помимо сокращения времени работы над задачами по обновлению версий в нескольких репозиториях, мы получили кое-что более важное — выстроенный процесс, который оказался более оптимальным, чем был до этого. Теперь у команды разработки есть понимание, когда и куда можно выносить переиспользуемые вещи, мы смогли избавиться от циклических зависимостей (смело рекомендую eslint-правило @nx/enforce-module-boundaries) и оптимизировать архитектуру, разделив зоны ответственности.
Главный вывод здесь простой: монорепозиторий — это инвестиция. В начале действительно кажется, что вы просто создали себе проблемы на пустом месте, но если масштаб и связность проектов это оправдывают, в конце пути становится проще жить и развивать систему дальше.
