Patterns I would reuse in any Bazel migration

A professional recap of the migration patterns that proved durable: milestone slicing, dual-build policy, CI tag routing, lockfile discipline, and runfiles-aware testing.

Patterns I would reuse in any Bazel migration

If I had to do this again in another company repo, I would not copy files blindly. I would copy patterns. This chapter is the distilled version of those patterns, explained in my voice and grounded in what actually worked.


Pattern 1: milestone slicing beats "big bang"

Core principle

I treated migration as a sequence of milestones with acceptance criteria, not a single branch where everything changes at once.

Loading image...

Why it works:

  • teams can review incremental risk,
  • CI remains interpretable,
  • regression ownership stays clear.

Pattern 2: dual-build policy is a feature, not a failure

I explicitly kept Dockerfile and Bazel OCI paths side by side where needed. That protected release confidence while still forcing Bazel maturity where it mattered.

ConcernWhat I enforcedWhy this is professional
Release confidenceDockerfile matrix remains for specific release paths.No fake parity claims; production behavior remains explicit.
Build graph disciplineBazel OCI builds in CI with pinned bases and visible targets.Migration progress is measurable and enforceable.
Parity gapsDocumented in policy docs and chapter notes.Teams can reason about risk instead of discovering surprises late.

Pattern 3: CI tag routing gives one graph many operating modes

I used tag-based routing in .bazelrc to keep one graph but multiple execution intents.

Representative CI/test routing commands
$
Pragmatic discipline

Tags like requires-network or no-sandbox are not excuses. They are explicit contracts that need periodic reduction over time.


Pattern 4: lockfile and dependency policy are social contracts

MODULE.bazel.lock is not just a generated file. It is the shared dependency fact that keeps local and CI behavior aligned.

What I would always enforce:

  1. version changes happen intentionally,
  2. lockfile diffs are reviewed,
  3. CI and local workflows consume the same lock state.

Pattern 5: runfiles-aware tests prevent hidden path assumptions

A lot of migration pain comes from scripts that work under bazel run but fail under bazel test. The fix is not magical: declare inputs and resolve paths through runfiles-aware logic.

Anti-patternMigration-safe pattern
Assume cwd-relative paths to repo files.Use declared data and runfiles-aware resolution.
Undeclared tools/files in shell tests.Declare all required inputs, then verify in CI under test mode.
Fix failures with ad-hoc absolute paths.Fix target definitions so behavior is stable across machines.

Pattern 6: documentation is part of the build system

I consider docs part of operational quality, not decoration. In this migration:

  • chapters explain trade-offs in sequence,
  • policy docs define allowed behavior,
  • milestone reports define acceptance boundaries.

This turns Bazel adoption into a reusable practice, not just a temporary burst of engineering effort.


Professional summary

This project proves that a polyglot Bazel migration can stay practical and defensible when you combine:

  • milestone-driven delivery,
  • explicit dual-build policy,
  • CI tag routing and governance,
  • lockfile and runfiles discipline,
  • and docs that carry institutional memory.
How to use this chapter

Use these patterns as a reusable checklist before you start your own migration plan. They are technology-agnostic enough to transfer, but concrete enough to execute.