Sitemap

Bazel 9 Migration: How to Get Faster Builds Before the Bzlmod Refactor

7 min readFeb 17, 2026

--

Decouple performance improvements from build-system evolution

Bazel 9 is a technically significant release. It completes years of architectural cleanup i.e. removing legacy subsystems, enforcing stricter semantics, and decoupling language-specific rules from the core. From a project health standpoint, these are defensible decisions.

But for engineering organizations running Bazel in production, this release introduces a specific problem that caught everyones attention: the performance improvements many teams are chasing are now gated behind a non-trivial migration. You don’t get faster builds by upgrading. You get faster builds by completing a structural refactoring project and then hoping the new setup delivers the gains you expected.

In this, I tried to explain that build-system evolution and build performance are related but they don’t have to be coupled. There’s a way to get the speed now without waiting for the migration to finish.

This isn’t an anti-Bazel piece, I know. Bazel remains one of the most capable build systems available for large-scale monorepos.But the current question here is about sequencing, risk, and return on engineering investment.

Why Bazel 9 Marks a Structural Turning Point

1. Removal of WORKSPACE and Mandatory Bzlmod

Bazel 8 disabled the legacy WORKSPACE system by default. Bazel 9 removes the supporting code entirely. Bzlmod is now the sole mechanism for external dependency management.

For teams that haven’t yet migrated, this isn’t a deprecation warning to schedule for later. The path forward requires a full rethink of how external dependencies are declared, resolved, and versioned. In large monorepos with hundreds of third-party dependencies, non-trivial version resolution behavior, and long-lived dependency graphs, this is a substantial refactoring effort.

The Bazel team has published a Bzlmod migration tool and an agent-driven migration setup to help. These are useful starting points, but as the release notes acknowledge, they won’t complete the migration for you. The manual work is validating resolution behavior, handling edge cases in custom macros, testing the full build graph and then still falls on your platform team.

This is a structural refactor, not a version bump.

2. Decoupling of C++ Rules (rules_cc)

Bazel 9 completes the “Starlarkification” effort by moving built-in C++ rules out of the Bazel binary and into rules_cc, a separately maintained and versioned Starlark module.

The architectural reasoning is sound: language-specific rules shouldn’t be hardcoded into the build system core. But the operational consequence is real. Teams managing large or long-lived C++ codebases now maintain a compatibility matrix between Bazel core and rules_cc. Toolchain configurations, platform-specific workarounds, and custom build rules that previously relied on stable built-in behavior now depend on two independently moving targets.

This demands specialized expertise and turns build maintenance into a continuous responsibility rather than an occasional upgrade task.

3. Stricter Semantics and Explicit Rule Loading

The --incompatible_autoload_externally flag, which allowed teams to reference moved-out rules as though they were still built-in, is empty by default in Bazel 9 and scheduled for removal in Bazel 10. Every load() statement in every BUILD file must explicitly reference the correct external module.

Bazel 9 also enforces tighter correctness guarantees across the build graph. Previously valid configurations can fail resolution under the new semantics. The effect is that upgrades behave more like migrations as you’re not just bumping a version number, you’re validating that your entire build graph still resolves correctly under new rules.

Bazel 9 formalizes correctness, but at the cost of higher operational overhead.

But Build Systems Can’t Keep Up with Code Velocity

AI Has Changed the Input Curve

The timing of Bazel 9 is worth noting. AI-assisted development toolslike GitHub Copilot, Cursor, Claude Code, and others are rapidely increasing the rate at which code is written, reviewed, and merged. For build infrastructure, this translates directly into higher code churn, more frequent rebuilds, and increased CI pressure.

Build systems, however, still require manual tuning, careful migrations, and human intervention to stay current and healthy. The asymmetry is growing: code generation is accelerating, but the infrastructure that processes that code still moves at the speed of human platform engineers.

Now, The New Problem Is Human Time

Platform engineering teams are finite. Every week spent migrating WORKSPACE to Bzlmod, updating rules_cc references, and fixing resolution failures under stricter semantics is a week not spent improving developer feedback loops, scaling CI infrastructure, or reducing lead time to production.

For organizations where developer velocity is a strategic priority, this trade-off matters more. The maintenance overhead of keeping a build system current can quietly consume the same engineering bandwidth that was supposed to be freed up by adopting that build system in the first place.

Now as code generation accelerates, manual build-system maintenance becomes the limiting factor.

Most build time is spent waiting on compilers

Most discussions about build performance is depend on two distinct layers:

Press enter or click to view image in full size

The build system decides what needs to execute and the execution layer handles how fast that execution happens.

When teams profile slow builds, the majority of wall-clock time is typically spent in the execution layer i.e. waiting for gcc, clang, or ld to finish, waiting for test suites to complete, waiting for large link steps to resolve. The orchestration layer (dependency resolution, graph evaluation) is usually a small fraction of total build time in well-configured projects.

This distinction matters because it changes where acceleration should be applied. If most of your build time is spent executing tools, then optimizing the orchestration layer which is what a Bazel migration primarily affects, may not be the highest-leverage investment.

Most build time is spent executing tools, not deciding what to execute.

Accelerating the Execution Layer, Not the Build System

Incredibuild operates below the build system. It accelerates the execution of compilers, linkers, tests, and other build steps directly without requiring changes to Bazel, CMake, Make, Ninja, or any other build system.

Press enter or click to view image in full size
Image reference from Incredibuild.com

This architectural position is what makes it relevant to the Bazel 9 discussion. Because Incredibuild doesn't depend on which build system is orchestrating the work, or which version of that build system is running, it decouples performance from build-system evolution entirely.

In addition, a centralized shared cache returns previously compiled outputs when inputs and flags match, eliminating redundant work across developers and CI pipelines. The result is that acceleration happens where build time is actually spent i.e. executing tools and without introducing new migration requirements or coupling performance gains to build-system upgrades.

Choose What Comes First: Migration or Performance?

Teams facing the Bazel 9 transition have two paths:

Option A: Migrate first. Commit to the Bzlmod migration, update rules_cc references, fix resolution failures, validate the full build graph under stricter semantics.

Timeline: weeks to months depending on codebase size and dependency complexity. Risk: non-trivial.

Performance outcome: uncertain and delayed until migration completes.

Option B: Measure first. Accelerate current builds immediately using execution-layer optimization on your existing setup that too unchanged. Then establish a concrete performance baseline. Evaluate the Bazel 9 migration with actual data on how much additional speed (if any) it would deliver beyond what you’ve already achieved.

Option B doesn’t eliminate the need for migration. Bzlmod is the future of Bazel dependency management, and teams will eventually need to adopt it. But it changes the migration from a performance-driven emergency into a planned platform improvement that happens on your timeline, not Bazel’s release schedule.

The core principle will be still the same: decouple performance improvements from build-system evolution.

Before You Migrate, Measure First!

If your team is staring down a Bazel 9 migration primarily because builds are slow, it’s worth establishing a baseline first. Profile your current builds. Figure out where wall-clock time is actually going for example compilation, linking, test execution, dependency fetching, queue wait. Most teams have a rough sense of this but haven’t measured it precisely.

Then ask a straightforward question: how much of that time is an orchestration problem (dependency resolution, rebuild decisions) versus an execution problem (waiting for compilers and linkers to finish)? If it’s mostly execution and for large C++ codebases, it usually is, then the migration may not be solving the problem you think it’s solving.

Execution-layer tools like Incredibuild exist specifically for this. You can benchmark distributed execution and shared caching on your current setup without changing anything about your build system. If the numbers close most of the gap, the Bazel 9 migration shifts from an urgent performance fix to a planned platform improvement. If they don’t, you now have concrete data showing exactly where the remaining bottleneck lives and whether a migration will actually address it.

Either way, you’re making the decision with numbers instead of assumptions.

Conclusion

Bazel will continue to evolve as we are. Version 10 will introduce new incompatible changes. rules_cc will release independently. The ecosystem will keep on moving, and platform teams will keep spending cycles staying updated.

The teams that handle this well are the ones that don’t let build-system releases dictate their performance timeline. They separate correctness decisions (which build system to use, when to migrate) from performance decisions (how to make the actual compilation and linking fast).

Remember, migration is inevitable but urgency is optional. And performance shouldn’t reset every time your build system ships a major release.

Teams that separate correctness decisions from performance decisions move faster and stay in control.

Till then, Good Bye!

--

--

Pratik Mahalle
Pratik Mahalle

Written by Pratik Mahalle

That Devops Guy! Platform engineer writing about his experience.