Bazel, Nx, Turborepo: сравнение
Три подхода к сборке в монорепозиториях — от Google-scale до стартапа
Зачем монорепозиторию система сборки
Когда в одном репозитории лежат десятки пакетов (о том, почему команды выбирают monorepo vs polyrepo, — в предыдущей главе), обычный npm run build перестаёт справляться. Вы меняете один файл в утилитарной библиотеке, а CI пересобирает все 47 пакетов, прогоняет все тесты и тратит на это 35 минут. На следующий день коллега меняет README — и CI тратит те же 35 минут. Инструменты для монорепозиториев решают эту проблему через два механизма: граф зависимостей (понимание, что от чего зависит) и кэширование (повторное использование результатов, которые не изменились).
Три системы доминируют в этом пространстве: Bazel, вышедший из Google, Nx от компании Nrwl и Turborepo, купленный Vercel в 2021 году. Каждая из них занимает свою нишу, и выбор между ними — это выбор между мощностью и простотой.
Bazel: инструмент для тех, кому нужна гарантия
Bazel появился в 2015 году как open-source-версия внутренней системы сборки Google (Blaze). Ключевая идея — герметичность: каждая сборка должна давать один и тот же результат независимо от того, на какой машине она запущена. Bazel достигает этого через явное декларирование всех входов и выходов каждого шага сборки в файлах BUILD.
Stripe перевела более 300 сервисов на Bazel и сократила время CI с 45 минут до 7. Эта цифра объясняется двумя вещами: remote caching (результаты сборки хранятся в общем кэше, и если коллега уже собрал этот модуль с теми же входными данными — вы получаете результат мгновенно) и инкрементальные сборки (Bazel пересобирает ровно те цели, на которые повлияло ваше изменение, а не весь граф).
Bazel работает с любым языком: Java, Go, Python, C++, Rust, JavaScript. Для каждого языка существуют rules — наборы правил сборки, которые сообщество поддерживает на GitHub. Эта полиглотность делает Bazel выбором для компаний с разнородным стеком.
Цена входа высокая. Каждый пакет нуждается в BUILD-файле, описывающем его зависимости, цели сборки и тесты. Разработчику нужно выучить Starlark (DSL на основе Python) и разобраться в концепциях вроде toolchains, platforms и execution strategies. Команды, которые переходят на Bazel, обычно выделяют одного-двух инженеров на полную занятость для поддержки build-инфраструктуры.
Nx: платформа с мнением
Nx позиционирует себя как монорепо-платформу, а не как систему сборки. Помимо кэширования и графа зависимостей, Nx предлагает генераторы кода (scaffolding новых приложений и библиотек), плагины для популярных фреймворков (React, Angular, Next.js, NestJS), визуализацию графа зависимостей в браузере и distributed task execution (Nx Cloud) для распределения CI-задач по нескольким машинам.
Для Nx характерен подход «affected»: при каждом PR система определяет, какие проекты затронуты изменениями, и запускает сборку и тесты для них. В репозитории с 50 пакетами, где ваш PR затрагивает два, CI работает в 25 раз быстрее, чем при полной сборке.
Nx хорошо работает с JavaScript/TypeScript-экосистемой, но поддерживает и другие языки через плагины: Go, .NET, Java. Установка занимает минуты — npx nx init в существующем workspace добавляет конфигурацию, анализирует зависимости и начинает кэшировать результаты задач.
Ограничение Nx — его мнение о структуре проекта. Nx предполагает определённую организацию workspace, и если ваш проект организован иначе, вам придётся либо адаптироваться, либо бороться с инструментом. Для новых проектов это преимущество (готовые шаблоны и convention’ы), для существующих — потенциальная миграция.
Turborepo: кэширование за 10 минут
Turborepo делает одну вещь и делает её хорошо: читает ваши package.json, строит граф зависимостей между пакетами, запускает задачи параллельно и кэширует результаты. Всё. Никаких генераторов, визуализаторов или плагинных систем.
Установка Turborepo в существующий monorepo занимает 10 минут: npm install turbo, создать turbo.json с описанием pipeline, и turbo run build начинает кэшировать результаты сборки. При втором запуске неизменённые пакеты пропускаются, и вывод воспроизводится из кэша — включая stdout и файлы-артефакты.
Remote caching через Vercel (или self-hosted) позволяет расшарить кэш между CI и локальными машинами разработчиков. Если CI уже собрал ваш пакет после мержа в main — ваш следующий turbo run build подхватит этот результат.
Turborepo ограничен JavaScript/TypeScript-экосистемой и менеджерами пакетов npm, yarn, pnpm. Для полиглотного стека он не подходит. Он также не предлагает distributed task execution — все задачи выполняются на одной машине. Для репозиториев с сотнями пакетов это становится узким местом.
Как выбирать
Размер и сложность определяют выбор. Если у вас JavaScript/TypeScript-монорепо с 5–30 пакетами и вам нужно ускорить CI — начните с Turborepo. Десять минут настройки, и вы получите ощутимое ускорение. Если вы растёте, вам нужны генераторы, визуализация зависимостей и распределённый CI — смотрите на Nx. Если у вас полиглотный стек, сотни сервисов и требования к воспроизводимости сборок — Bazel, при условии что вы готовы инвестировать в build-команду.
Все три инструмента решают одну задачу — «не пересобирать то, что не изменилось» — но делают это на разных уровнях абстракции. Turborepo — это кэширующий task runner. Nx — это платформа для монорепо. Bazel — это система сборки промышленного масштаба. Выбирайте инструмент, соразмерный вашей проблеме.