At some point in a growing engineering organization, someone opens a pull request and the CI pipeline finishes in forty-five minutes. The first time it happens, engineers shrug. The second time, someone files a ticket. By the third month of it, senior engineers are leaving the office early because there is no point sitting around waiting for a green checkmark. I have seen this pattern at organizations large and small, and the root cause is almost always the same: the codebase grew into a monorepo, and nobody replaced the build tooling designed for a single application.
Monorepos are genuinely useful. Atomic cross-service commits, unified dependency management, simpler code sharing, and consistent tooling are real benefits. The engineers who migrated to monorepos were not wrong. What they often did not anticipate was that standard build tools, npm install && npm run build and make all, execute every package on every build. When you have two packages, that is fine. When you have two hundred, every CI run rebuilds the world, and the only question is how long developers are willing to tolerate it.
The answer to that problem is build systems purpose-built for monorepos: tools that understand your dependency graph, rebuild only what changed, and cache artifacts so that unchanged work is never repeated. In 2026, four tools account for most production monorepo deployments: Bazel, Nx, Turborepo, and Pants. A fifth, Buck2, matters if you are at Meta-adjacent scale or invested in the Rust ecosystem. This guide explains what each tool actually does, when to use it, and how to make the migration without destroying your team’s productivity mid-flight.
Why Standard Tooling Breaks Down
Before reaching for any of these tools, it is worth understanding exactly where conventional build systems fail. The problem has two parts.
The first part is that most build systems lack a precise dependency graph. When you run jest --coverage or go build ./..., those tools do not know that packages/auth only depends on packages/utils and has no relationship with packages/billing. They run everything. The entire test suite. Every linter, every build artifact, every type check. As the graph grows, so does the runtime, linearly and then super-linearly as integration tests accumulate.
The second part is that even tools that support caching often cache at the wrong granularity. npm caches node_modules. Maven caches jar artifacts. But neither of these caches captures the full build fingerprint: the specific source files that produced a specific output, under a specific set of environment variables, with a specific compiler version. Without that fingerprint, you cannot safely skip a build step, because you cannot prove the cached result is still valid.
Bazel, Nx, Turborepo, and Pants all solve both problems: they build a dependency graph from your source tree, and they cache build artifacts using cryptographic fingerprints of inputs. The difference between them is the scope and rigor of how they do it.

Bazel: Hermetic Builds at Google Scale
Bazel started life as Blaze, Google’s internal build system, before being open-sourced in 2015. As of late 2026, the current stable release is in the 9.x LTS series, with 9.3.0 landing in early October. Google remains the primary developer, though a BUILD Foundation was established under the Linux Foundation umbrella at BazelCon 2025, which gives the community a formal governance structure independent of any single corporate backer.
Bazel’s defining characteristic is hermeticity. Every build action runs in a sandbox with an explicit, declared set of inputs and outputs. If your py_binary rule does not declare that it reads a config file, Bazel will not let it read that config file, full stop. This strictness is annoying to set up and it is the entire point: once your BUILD files accurately describe your dependencies, Bazel can skip any action whose inputs have not changed, with cryptographic certainty rather than heuristics.
The dependency model operates at the target level, which Bazel defines as a named group of source files, typically a single library, binary, or test suite. You write BUILD files (using Starlark, a restricted Python dialect) that declare what each target contains and what it depends on. A BUILD file for a Go microservice might look like a ten-line description of its sources and its library dependencies. Bazel reads those files across the entire repository and constructs a directed acyclic graph of every target and every dependency between them.
When a change lands, Bazel walks the graph forward from the changed files to identify every target whose inputs are affected, then runs only those targets. If you changed one library that is transitively depended on by forty services, Bazel will test all forty services. If you changed a documentation file that nothing in the build graph depends on, Bazel will not run anything. This is precise in a way that filesystem-modification-time-based approaches simply are not.
The operational overhead is real. BUILD files need to be maintained. Hermetic builds mean you cannot shell out to arbitrary system utilities unless you have declared them as dependencies. Setting up remote execution (not just caching, but actually running build actions on a fleet of worker machines) requires infrastructure investment: you need a compatible Remote Execution API server, which products like BuildBuddy and EngFlow provide commercially, or which you can self-host with the open-source bazel-remote project.
I have worked with teams where the Bazel setup took three months to get right, and then paid back that investment within a quarter by cutting CI times by a large margin. I have also seen teams that spent six months on Bazel, never fully understood it, and ended up with a build system that was slower than what they replaced. The tool rewards investment. It does not work well when treated as a black box.
Who should use Bazel: Teams with polyglot codebases, C++ or Java at the core, strict reproducibility requirements, or existing Bazel expertise. Organizations that want to invest in remote execution (not just caching) for maximum parallelism. Roughly speaking, if your organization could hire a build infrastructure engineer or a team, Bazel is worth serious consideration.
Nx: The JavaScript Ecosystem’s Monorepo Hub
Nx is the dominant monorepo tool for JavaScript and TypeScript projects, and with version 23.x (23.2.1 was available as of September 2026), it has moved well beyond its Angular origins into a broader platform. Nrwl, the company behind Nx, makes money on Nx Cloud, the commercial remote cache and distributed task execution product. The open-source core is genuinely useful on its own; the cloud product accelerates it.
The core concept in Nx is the affected computation. Before running any task, Nx computes which projects have changed since your base branch, then runs tasks only for affected projects and their dependents. This is similar to Bazel’s approach, but implemented at the package level rather than the target level, and with much less setup friction. You do not write BUILD files. Nx infers your dependency graph from package.json imports, tsconfig.json paths, and framework-specific conventions.
The trade-off is granularity. Nx does not know about individual source files within a package; it treats the entire package as a unit. If you change one line in a shared utilities library, Nx reruns every package that depends on it, even if only one function in that library changed. Bazel, with proper BUILD file declarations, could be more selective. For most teams, the package-level granularity is sufficient and the lower setup cost is worth it.
Nx’s plugin ecosystem is its strongest selling point for JavaScript teams. There are official plugins for React, Next.js, Remix, Angular, Node, NestJS, Express, Vite, Webpack, esbuild, and Jest, each with code generators for creating new components, services, and libraries. The generators enforce consistent project structure across the monorepo, which matters when you have fifty developers who each have their own opinions about folder organization.
In 2026, Nx’s roadmap introduced what they call Self-Healing CI: the system analyzes CI failures, generates fixes, validates them, and applies them automatically when it is confident. The initial version targets flaky test detection and common configuration errors. This is a genuinely interesting direction, and I am cautiously optimistic about it; but treat it as an emerging capability rather than a production reliability guarantee until your team has run it for a while.
For remote caching, Nx Cloud’s free Hobby tier covers most small teams. Larger organizations pay per active contributor on the Team plan. If you need on-premises deployment for compliance reasons, that is available on the Enterprise tier. You can also use third-party compatible caches, since Nx supports a cache backend interface.
Who should use Nx: JavaScript and TypeScript monorepos, especially those using React, Angular, or Node. Teams that want to minimize setup time and get incremental builds working quickly. Organizations that are already in the frontend/Node ecosystem and want code generation, consistent structure, and a curated plugin ecosystem alongside the build acceleration.
Turborepo: Simplicity First
Turborepo came from Jared Palmer and was acquired by Vercel in 2021. The current release line is 2.x, with 2.11.x available by October 2026. If Nx is the tool that gives you a framework for your whole monorepo, Turborepo is the tool that adds caching to a monorepo that already exists, with minimal configuration overhead.
The core configuration is a single turbo.json file at the repository root. You declare your pipeline: which tasks depend on which other tasks, which task outputs should be cached, and which environment variables are part of the cache key. Getting from zero to a working remote cache typically takes an afternoon. I have seen teams adopt Turborepo in a day and start seeing build time improvements immediately.
Turborepo’s remote cache supports two paths. Vercel’s managed Remote Cache is free and does not require hosting on Vercel; you authenticate with a Vercel account token and point your CI at the API. If you cannot send build artifacts to Vercel’s servers for compliance or regulatory reasons, the open-source ducktors/turborepo-remote-cache project implements the same /v8/artifacts API specification and can be deployed against S3, GCS, Azure Blob, or local disk. A community guide from July 2026 confirms the self-hosted option works with the 2.10.x series.
The turbo query feature, which uses GraphQL to let you query your dependency graph and determine what is affected by a given change, reached stable status in the 2.9 release. This is useful for CI optimization: you can ask Turborepo which tasks are affected by a set of changed files and then run only those tasks, without letting Turborepo manage the full task execution. Teams that have custom orchestration needs, or that want to run GPU-accelerated test suites on specific runner types, use this to wire Turborepo’s graph analysis into their own CI pipelines.
The deliberate limitation of Turborepo is scope. It does not have a plugin system. It does not have code generators. It does not schedule parallel execution across machines (though it parallelizes tasks locally). It is a caching layer and a dependency graph for your task runner, and that is what it should be. For teams that have outgrown npm workspaces but do not want to commit to the complexity of Nx or Bazel, Turborepo is often the right answer.
Vercel’s marketing claims significant build time reductions in their release announcements; treat these figures as vendor claims rather than established benchmarks. Your results will depend heavily on your specific test suite composition, cache hit rate, and whether your tasks have the right input/output declarations.
Who should use Turborepo: JavaScript and TypeScript monorepos (with some emerging Rust support via Cargo workspaces in 2.10.x) that want fast time-to-value. Teams where CI times are painful but Nx’s full framework is more than you need. Organizations already using Vercel for deployment who want integrated tooling.
Pants: Deep Python Support with a Polyglot Core
Pants (the Pants Build System) is community-maintained under the pantsbuild GitHub organization, with the 2.26.x series as the most recent stable line. If you primarily write Python and you want build tooling that understands Python as deeply as Bazel understands Java, Pants is worth serious attention.
Pants’s distinguishing capability is target inference. In Bazel, you write BUILD files that explicitly list every source file in every target. In Pants, you write minimal BUILD files, and Pants reads your Python import statements to infer which files belong together. It handles requirements.txt and lockfiles, multiple Python interpreter versions in the same repo, virtual environments per target, and PEX packaging (Python EXecutable bundles that can be deployed as single files). The Python story is exceptionally mature.
Beyond Python, Pants has first-class support for Go, Java, Scala, Kotlin, and Shell. The 2.26.x release added a Kubernetes backend, which lets you declare Kubernetes manifests as build targets, run kubectl commands as part of your pipeline, and track dependencies from your application code to the manifests that deploy it. That is a useful integration for teams running services on Kubernetes.
Like Bazel, Pants uses the Remote Execution API for both remote caching and remote execution. It integrates with the same commercial and self-hosted backends: BuildBuddy, EngFlow, and open-source options. The setup is less complex than Bazel’s because you have less to configure (no BUILD file declarations for every source file), but it is more complex than Nx or Turborepo because the RE API integration requires running and configuring backend infrastructure.
One thing I will say plainly: Pants’s community is smaller than Bazel’s, and its corporate backing is less visible than Nx’s (Nrwl) or Turborepo’s (Vercel). If you encounter an edge case or a bug, your escalation path is the open-source community, not a support contract. That is a real operational consideration.
Who should use Pants: Python-heavy organizations, especially data engineering and machine learning teams that mix Python with JVM languages. Organizations that want Bazel-quality hermeticity without writing BUILD files for every Python module. Teams comfortable operating RE API infrastructure.

Remote Caching: The Non-Negotiable Foundation
Whatever tool you choose, remote caching is not optional. Local caching helps individual developers. Remote caching is what makes CI fast, because it allows every CI run to share the artifact cache with every other CI run.
The mechanism is straightforward: before executing a build action, the tool computes a cache key from the action’s inputs (source file hashes, environment variables, compiler version, tool flags). It checks the remote cache for an artifact at that key. If the artifact exists, it downloads it and skips execution. If not, it executes, then uploads the result. For a large monorepo where most packages do not change between commits, the cold cache miss on the first run and the warm cache hit on subsequent runs represents the entire value of the investment.
The practical consideration is cache key correctness. If your cache keys do not accurately capture all the inputs to a build action, you will get false cache hits: the tool believes a cached artifact is valid when it is not, because something changed that was not in the key. This causes subtle, intermittent build failures that are painful to diagnose. Bazel and Pants have the most rigorous input tracking because of their hermetic sandbox model. Nx and Turborepo rely on you declaring the right input patterns in your configuration, and missing a configuration file from your inputs will cause incorrect cache hits until you track down the inconsistency.
Investing time in getting your cache key declarations right before going to production is the single highest-leverage action you can take with any of these tools.
Choosing: A Decision Framework
The honest framework I use when evaluating build tools for a team is a sequence of questions.
What languages does your codebase actually use? If you are 90% JavaScript and TypeScript, Nx or Turborepo. If you are heavily Python with some JVM, Pants. If you are polyglot with C++, Java, or Go as first-class citizens and you cannot afford language-specific tool gaps, Bazel. If you are at Meta scale with Rust in your critical path, Buck2.
How much configuration complexity can your team sustain? Turborepo has the lowest setup cost. Nx is moderate. Pants is medium-high. Bazel is high. This is not just about the initial setup; it is about the ongoing maintenance load when you add a new language, a new framework, or a new team that does not understand the build system.
Do you need remote execution, or just remote caching? Remote caching alone, which distributes artifacts between CI runners and developer machines, is enough for most teams. Remote execution, which actually farms build actions out to a pool of worker machines for maximum parallelism, is what you need when a single machine cannot run your build in the time budget even with caching. Remote execution requires Bazel or Pants (both support the RE API). Nx and Turborepo do not support remote execution in the same sense, though Nx Cloud’s Distributed Task Execution (Nx Agents) distributes tasks at the process level across multiple machines, which achieves similar wall-clock results for most workloads.
What does your existing CI/CD pipeline look like? If you are on GitHub Actions with self-hosted runners on Kubernetes, any of these tools will integrate. If you have Dagger-based pipelines, Dagger has native support for calling Nx and Turborepo tasks. The integration surface is not a strong differentiator; all four tools expose CLI commands that standard CI pipelines call.
Do you use an internal developer portal or self-service platform? If you have built internal tooling around your monorepo structure (scaffolding for new services, automated dependency management, service catalog integrations), Nx’s plugin and generator system makes that tooling easier to build and maintain. Backstage and Port both have Nx integrations.

Migration Strategy: Avoiding the Big Bang
The worst monorepo build tool migrations I have seen tried to do everything at once: adopt the new tool, migrate all packages, set up remote caching, train the entire team, and retire the old system in a single sprint. They invariably took six months instead of six weeks, broke CI for extended periods, and left engineers bitter about the whole effort.
The approach that works is incremental adoption, starting at the edges.
Pick one package that is well-isolated, has clear dependencies, and is owned by engineers who are willing to deal with build tool weirdness for a few weeks. Migrate it to the new tool. Get remote caching working for that one package. Measure the build time improvement. Write down what was surprising or painful.
Then migrate the packages that depend on it. At this point you have a small subgraph of the dependency tree under the new tool, with the rest still on the old system. The key insight is that you can run both systems in parallel during the migration: the old system handles unchanged packages, the new system handles migrated packages. Tools like Nx and Turborepo explicitly support this coexistence mode. Bazel supports it through --build_event_stream and careful configuration of which directories Bazel owns.
Do not migrate shared libraries last; migrate them first. They are the highest leverage because everything depends on them, and once they are in the new system, their downstream dependents can be migrated with confidence.
The platform engineering team should own the build tooling decision and the migration playbook. This is not a project for individual application teams to each solve independently. Once you have multiple teams each adopting Nx or Bazel in slightly different ways, you have not improved the situation; you have just created a different kind of chaos.
Remote Caching Infrastructure in Practice
For Nx and Turborepo, the infrastructure story is simple enough that a single engineer can set it up in a day: you configure a remote cache endpoint, generate an access token, and add two environment variables to your CI configuration. The complexity comes in maintaining cache hit rates over time.
For Bazel and Pants, you are operating RE API infrastructure. The open-source bazel-remote server is the simplest self-hosted option: it runs as a container, stores artifacts on a local disk or in S3/GCS, and exposes the gRPC endpoint that Bazel expects. It is production-usable with a reasonable Kubernetes deployment. BuildBuddy and EngFlow offer managed commercial options with additional features like build analytics, invocation comparison, and test result history, which become valuable when you are debugging cache miss rates or trying to understand which targets are consuming CI time.
Regardless of which backend you use, cache metrics matter. Most of these tools expose cache hit rates, and you should have alerts on them. A sudden drop in cache hit rates usually means something changed about how cache keys are constructed, either an undeclared input was added to a build action, or a configuration file that should be part of the key is not. Investigating a cache hit rate drop as soon as it happens is much less painful than diagnosing it weeks later when nobody remembers what changed.
The AI Coding Agent Dimension
Something has changed in the last two years that makes monorepo build tooling more urgent: AI coding agents generate code much faster than human engineers, and they generate it across multiple packages simultaneously. When a coding agent opens a PR that touches twelve packages, your CI pipeline runs the full dependency graph rooted at those twelve packages. Without build caching, that scales badly very fast.
Teams that have deployed AI coding agents at scale consistently report that build system efficiency became a bottleneck they had not anticipated. If your current CI times are borderline acceptable with human-written PRs, they will be unacceptable once AI agents are opening PRs at a substantially higher rate. This is a legitimate forcing function to invest in proper build tooling now rather than later.
The cloud development environments dimension compounds this: when developers run their work in ephemeral cloud VMs, local caches do not persist between sessions. Every new CDE instance starts with a cold local cache, which makes remote caching not just beneficial but essentially required for acceptable iteration speeds.
Conclusion
Twenty years in this industry has taught me that build system problems follow a predictable curve: manageable at first, expensive to fix in the middle of rapid growth, and catastrophic if ignored too long. The engineering debt from inadequate monorepo tooling compounds in ways that are hard to quantify until you start tracking developer time spent waiting for CI.
The good news is that all four of the tools in this article are mature, production-tested, and genuinely solve the problem. The choice is mostly about matching tool complexity to team capacity. Start with Turborepo if you need fast wins in a JS monorepo. Start with Nx if you want an opinionated ecosystem. Start with Bazel if you have the appetite for deep integration and genuinely polyglot needs. Start with Pants if Python is your primary language and you want something between Turborepo’s simplicity and Bazel’s rigor.
What I would not do is wait. The cost of the transition gets higher every month you add more packages to an unmeasured build graph. Pick the tool that fits your current team, get remote caching working, and measure. The numbers will tell you whether you need to go deeper.
For teams thinking through their broader platform engineering strategy, build tooling belongs alongside CI/CD, development environments, and internal portals as a first-class platform concern: something the platform team owns, maintains, and continuously improves, not a collection of scripts each team manages for itself.
Get Cloud Architecture Insights
Practical deep dives on infrastructure, security, and scaling. No spam, no fluff.
By subscribing, you agree to receive emails. Unsubscribe anytime.
