Монорепозиторий: сравниваем npm, pnpm, Nx и Turborepo
Монорепозиторий не сводится к одной папке с несколькими проектами. Разбираем, чем отличаются npm workspaces, pnpm, Nx и Turborepo, какую цену платит команда и с чего начать без лишней инфраструктуры.

Монорепозиторий помогает хранить веб-приложение, общие пакеты и инфраструктурный код в одном Git-репозитории, но сам по себе не ускоряет сборку и не решает проблему зависимостей. Если выбрать только менеджер пакетов, команда получит workspaces без кеша задач; если сразу поставить тяжелый оркестратор, усложнит локальную разработку ради проекта из двух пакетов. Ниже — сравнение четырех вариантов и критерии, по которым можно принять решение для конкретного репозитория.
Сначала разделите две разные задачи
В монорепозитории обычно путают два уровня.
- Workspace-слой связывает пакеты, устанавливает зависимости и позволяет обращаться к локальному пакету по имени. Его дают npm workspaces и pnpm workspaces.
- Оркестрация задач определяет порядок сборки, запускает тесты только там, где это нужно, и кеширует результаты. Для этого используют Turborepo, Nx или собственные скрипты.
Например, pnpm может понять, что apps/store зависит от packages/ui, но не обязан сам строить граф задач, распределять сборку по машинам и восстанавливать кеш CI. Это уже отдельная ответственность.
Главный выбор в монорепозитории — не между «модным» и «простым» инструментом. Вы выбираете, где будет жить сложность: в правилах команды, менеджере пакетов или отдельном слое сборки.
Термин: граф зависимостей — модель, в которой пакеты и задачи представлены узлами, а связи между ними показывают, что нужно выполнить раньше. По этому графу оркестратор понимает, какие задачи можно пропустить или запустить параллельно.
Четыре варианта для одного репозитория
Синтаксис ниже ориентирован на npm 10, pnpm 9 и Turborepo 2.x. У конкретной версии CLI могут меняться детали команд, поэтому перед внедрением стоит проверить документацию и зафиксировать версии в проекте.
| Вариант | Что получает команда | Цена | Когда выбирать |
|---|---|---|---|
| npm workspaces | Связь локальных пакетов, единый lock-файл, запуск скриптов из корня | Меньше возможностей для кеша и графа задач | Небольшой репозиторий, уже использующий npm |
| pnpm workspaces | Жесткая изоляция зависимостей, экономное хранение пакетов, удобные фильтры | Нужно объяснить команде отличия от плоского node_modules | Несколько приложений и общих пакетов без сложной CI-оркестрации |
| Turborepo | Граф задач, локальный и удаленный кеш, параллельный запуск | Появляется отдельная конфигурация и требования к повторяемости задач | Много повторяющихся build, lint и test в JavaScript/TypeScript-проекте |
| Nx | Граф проекта, генераторы, правила зависимостей, плагины и кеш | Более заметная платформа и собственные соглашения | Большая команда, много приложений, строгие архитектурные границы |
npm workspaces: минимальный порог входа
npm подходит, если главная проблема — не оркестрация, а единая установка зависимостей и импорт локального пакета. Корневой package.json может выглядеть так:
{
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build": "npm run build --workspaces --if-present",
"test": "npm run test --workspaces --if-present"
}
}
Пакет из packages/ui получает собственное имя в package.json, а приложение указывает его как зависимость. npm создаст связь внутри workspace, если версия локального пакета подходит под диапазон.
Преимущество — предсказуемая привычка для команды, уже работающей с npm. Ограничение проявляется, когда в репозитории десятки пакетов: общий скрипт запускает задачи шире необходимого, а ускорение приходится собирать самостоятельно.
pnpm workspaces: строгие зависимости и фильтры
pnpm хранит скачанные пакеты в общем хранилище и связывает их с проектами, поэтому установка часто не дублирует одни и те же файлы в каждом пакете. В корне нужен файл pnpm-workspace.yaml:
packages:
- "apps/*"
- "packages/*"
Важная особенность pnpm — пакет не может случайно импортировать зависимость, которой нет в его собственном package.json. Код, который «случайно работал» в плоском node_modules, после миграции может завершиться ошибкой вида ERR_PNPM_MISSING_DEPENDENCY или обычным Cannot find module. Это не недостаток инструмента, а обнаруженная скрытая связь.
Сильная сторона pnpm — фильтрация. Можно запустить скрипт только для приложения и его локальных зависимостей, но точный синтаксис фильтров нужно сверять с документацией вашей версии pnpm. Это снижает время локальной проверки без перехода на Nx или Turborepo.
Turborepo: оркестратор поверх workspace
Turborepo не заменяет npm или pnpm. Он запускает задачи, описанные в пакетах, и использует граф зависимостей. Минимальный turbo.json для Turborepo 2.x:
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"lint": {},
"test": {}
}
}
Запись ^build означает: перед сборкой пакета выполните build у его зависимостей. Поле outputs сообщает, какие файлы можно восстановить из кеша. Если сборка записывает результат в другую папку, например .next, её нужно указать явно, иначе кеш будет неполным.
Turborepo особенно полезен, когда сборки повторяются в CI и между запусками меняются только отдельные пакеты. Но кеш требует детерминированных задач: тест не должен зависеть от случайного времени, локального файла разработчика или незафиксированной переменной окружения.
Nx: платформа с архитектурными правилами
Nx стоит рассматривать не как «еще один запускатель скриптов», а как платформу для большого workspace. Он строит граф проектов, умеет анализировать затронутые изменениями узлы и предлагает генераторы для типовой структуры. Это полезно, когда в репозитории несколько приложений, библиотеки разных типов и разные команды-владельцы.
Цена — больше соглашений. Команде придется договориться о структуре проектов, именах задач, генераторах и границах импортов. Если у вас два пакета и один pipeline, часть возможностей Nx останется невостребованной, а новые правила будут мешать быстрее, чем помогать.
Что выбрать по размеру и боли проекта
Выбор лучше привязывать не к числу папок, а к повторяемости задач и стоимости CI.
- Два–пять пакетов, один фронтенд и общая библиотека: начните с npm workspaces или pnpm. Сначала зафиксируйте границы пакетов и отдельные команды
build,lintиtest. - Несколько приложений, сборка долго повторяется: используйте pnpm workspaces вместе с Turborepo. Так workspace отвечает за зависимости, а оркестратор — за порядок и кеш.
- Большая команда и риск хаотичных импортов: смотрите в сторону Nx. Его ценность не только в скорости, но и в правилах архитектуры.
- Смешанный репозиторий с нестандартными задачами: не переносите всё в генераторы сразу. Оставьте обычные скрипты там, где они прозрачнее, и добавляйте оркестратор по измеренной потребности.
Что проверить до миграции
Не начинайте с перемещения папок. Сначала снимите карту текущей системы:
- Выпишите приложения, библиотеки и инфраструктурные пакеты. Для каждого укажите владельца и команду запуска.
- Проверьте скрытые зависимости: импорты пакетов без записи в их
package.json, чтение файлов из соседнего проекта, общие переменные окружения. - Разделите артефакты сборки. У каждого пакета должна быть понятная папка результата, которую можно включить в кеш или удалить без вреда для соседей.
- Замерьте CI до изменений: полное время pipeline, время установки зависимостей, build, тестов и lint. Без исходной точки кеш легко принять за ускорение, хотя изменился только набор шагов.
- Перенесите один пакет и прогоните чистую установку, локальную сборку и CI. Только после этого расширяйте workspace.
Что ломается чаще всего
Плоские зависимости маскируют ошибку
После перехода на pnpm модуль перестает находиться, хотя он установлен в корне. Исправление — добавить пакет в зависимости именно того workspace, который его импортирует. Не лечите проблему ручной установкой модуля в корень: ошибка вернется в CI.
Кеш объявлен, но результат неполный
Сборка проходит локально, а восстановленный кеш отдает старый или пустой каталог. Проверьте outputs, каталоги, которые удаляются перед сборкой, и переменные окружения, влияющие на результат. Для задач с внешними сервисами кеш часто нужно отключать или ограничивать.
Скрипт зависит от текущей папки
Команда работает из пакета, но падает при запуске из корня с ошибкой вроде ENOENT: no such file or directory. Скрипт должен использовать пути относительно самого пакета или явно задавать рабочий каталог средствами выбранного инструмента.
Версия Node.js расходится между разработчиком и CI
Одинаковый lock-файл не гарантирует одинаковый результат при разных версиях Node.js. Зафиксируйте поддерживаемый диапазон в документации проекта и настройках CI, а версию менеджера пакетов закрепите через механизм, который поддерживает ваш runtime и процесс сборки.
Когда подход ломается
Монорепозиторий не всегда лучше нескольких репозиториев. Он усложняет права доступа, release-процесс и историю изменений: маленькая правка общей библиотеки может запустить проверки нескольких приложений. Если команды выпускают продукты независимо, репозитории имеют разные циклы безопасности или в них почти нет общего кода, единый workspace может увеличить координационные расходы.
Отдельно стоит вынести публикацию пакетов и инвалидацию кеша. Это связанные, но самостоятельные темы: здесь важны только структура workspace и запуск задач.
Что делать дальше
Для нового JavaScript/TypeScript-монорепозитория начните с pnpm workspaces, если вам нужна строгая изоляция зависимостей и фильтрация. Добавляйте Turborepo после замера CI и локальных сборок, когда повторяющиеся задачи действительно стали узким местом. Nx выбирайте, если вместе со скоростью нужны генераторы, граф проектов и формальные архитектурные ограничения.
Следующий практический шаг — собрать маленький пилот из одного приложения и одной общей библиотеки. Измерьте время чистой установки, повторной сборки, тестов и pipeline после изменения только одного пакета. Именно эти четыре показателя покажут, окупилась ли выбранная схема, а не количество файлов в корне репозитория.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: npm workspaces — официальная документация, Turborepo — официальная документация по задачам и конфигурации
Частые вопросы
Можно ли сделать монорепозиторий только на npm?
Чем pnpm принципиально отличается от npm workspaces?
Нужно ли использовать Turborepo вместе с Nx?
Что делать, если кеш сборки возвращает неправильный результат?
Насколько большой репозиторий нужен для Nx или Turborepo?
Что будет, если команды используют разные версии Node.js?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.