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"
I treated migration as a sequence of milestones with acceptance criteria, not a single branch where everything changes at once.
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.
| Concern | What I enforced | Why this is professional |
|---|---|---|
| Release confidence | Dockerfile matrix remains for specific release paths. | No fake parity claims; production behavior remains explicit. |
| Build graph discipline | Bazel OCI builds in CI with pinned bases and visible targets. | Migration progress is measurable and enforceable. |
| Parity gaps | Documented 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.
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:
- version changes happen intentionally,
- lockfile diffs are reviewed,
- 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-pattern | Migration-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.
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.