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