Skip to main content

ADR-002: Bazel with staged adoption

Context​

A monorepo with Kotlin, TypeScript, Python, and Protobuf requires a build system that handles all languages hermetically. The build system must also support Protobuf code generation as the source of truth for cross-platform contracts.

During MVP (Month 0-6), the team is small and velocity matters more than perfect hermeticity. A staged adoption allows using platform-native tools (Gradle, pnpm, uv) during development while establishing Bazel as the CI and production build system.

Decision​

Adopt Bazel 7.5+ as the polyglot build system with staged adoption:

  • Phase 1 (MVP): Platform-native tools for inner loop (Gradle for Kotlin, pnpm for TypeScript, uv for Python). Bazel for CI, proto codegen, and production builds.
  • Phase 2+: Migrate inner-loop builds to Bazel as the team grows and hermeticity becomes more valuable.

Alternatives considered​

OptionProsCons
Bazel (chosen)Polyglot, hermetic, excellent caching, industry standard for large reposSteep learning curve, complex BUILD files
NxGreat JS/TS support, simpler config, good cachingWeak polyglot support (Kotlin/Python plugins immature)
Gradle (multi-project)Native Kotlin support, mature ecosystemPoor TypeScript/Python support, not truly polyglot
Buck2Meta-backed, fast, hermeticSmaller community, fewer plugins, less documentation
PantsGood Python support, growing Kotlin supportSmaller ecosystem, weaker TypeScript story

Consequences​

What becomes easier​

  • Proto codegen produces consistent types across all platforms
  • CI caching is reliable and reproducible
  • Adding new languages/platforms in the future is straightforward
  • Production builds are hermetic and deterministic

What becomes harder​

  • Two build systems to maintain during Phase 1 (Bazel + native)
  • BUILD files need maintenance alongside source code
  • Developers need to learn Bazel basics