Bazel, Nx, Turborepo: сравнение — Лаборатория DX
Mid Авторский

Bazel, Nx, Turborepo: сравнение

Три подхода к сборке в монорепозиториях — от Google-scale до стартапа

Первоисточник

Bazel Overview

Google, 2015

Зачем монорепозиторию система сборки

Когда в одном репозитории лежат десятки пакетов (о выборе 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). Ключевая идея — герметичность: каждая сборка даёт один и тот же результат независимо от машины, на которой запущена. Достигается это через явное декларирование всех входов и выходов каждого шага сборки в файлах 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 — система сборки промышленного масштаба.