Skip to content

# ⚙️ Bazel Adoption Evaluation for Ariane Monorepo

⚙️ Bazel Adoption Evaluation for Ariane Monorepo

Section titled “⚙️ Bazel Adoption Evaluation for Ariane Monorepo”

Date: 2025-09-17

DomainTechBuild ModeNotes
Web AppAstro (site/)Vite/Astro incrementalSSR + serverless (Vercel)
CMSSanity Studio (studio/)Sanity CLI bundlerDeploy via webhook
Auth InfraZitadel (docker-compose)Docker images / remote hostShell + YAML orchestration
WASMRust → wasm (wasm/session-signer)wasm-packOutputs into site/src/lib/wasm/session-signer/
Scripts / OpsBashDirect executionDeployment, status, security scans
TestingVitest, PlaywrightNode-basedAlready parallelizable
SecurityCodeQL / Trivy / auditsGitHub ActionsYAML-managed
ValueBenefitApplicability Here
Deterministic / Hermetic BuildsReproducible outputs, fewer “works on my machine” issuesMedium (JS toolchains are evolving; reproducibility improves with pinned toolchains)
Fine-Grained CachingAvoids rebuilding unaffected targets (esp. CI)High once monorepo grows (currently modest)
Remote Cache / Build Farm ReadyScale with contributors / heavier Rust or future Go/ML servicesFuture potential
Unified Build GraphRust, TypeScript, Docker, tests in one DAGGood alignment (cross-language)
Test Impact AnalysisOnly re-run impacted testsUseful once > few hundred tests
Consistent Tool VersionsToolchain pinning via rules_js / rules_rustModerate value (pnpm / corepack alt)
Incremental Migration PossibleDirectory-by-directoryFeasible
CategoryCost
Initial Cognitive LoadBazel concepts (WORKSPACE, BUILD targets, visibility, toolchains)
Rules MaturityJS ecosystem: migration from deprecated rules_nodejs to rules_js / bazelbuild/rules_ts still evolving
Local DXAstro + Sanity dev servers expect ad-hoc watch; Bazel integration means wrappers or leave them outside Bazel
Caching ROICurrent repo size small; potential over-engineering now
OnboardingContributors must learn Bazel to modify build graph
Workflow DuplicationGitHub Actions YAML still required; Bazel adds a layer, not replaces CI logic
AlternativeStrengthWeakness vs Bazel
Nx / TurborepoSimple task graph, caching, remote cache optionsLess hermetic; limited cross-language depth
MoonrepoLanguage-agnostic, good task runnerSmaller ecosystem than Bazel
Just + Make + task graphMinimal overheadNo deep dependency graph awareness
GitHub Actions Caching StrategyLow complexityManual invalidation logic

5. When Bazel Makes Sense (Trigger Conditions)

Section titled “5. When Bazel Makes Sense (Trigger Conditions)”

Adopt once ≥2 of these become true:

  1. WASM / Rust modules expand (e.g., multiple crates / shared libs)
  2. Additional backend service(s) (e.g., gateway, API server) added
  3. Test suite runtime > 8–10 minutes without caching
  4. Frequent partial rebuild needs during PR iteration
  5. Need reproducible SBOM / provenance chain for builds (supply chain compliance)
PhaseScopeGoalsExit Criteria
0 – EvaluateDry-run sandbox branchValidate rules_js + rules_rust synergyAll POC targets build + test
1 – WASM Corewasm/session-signer onlyHermetic Rust → wasm artifact + hashingBazel build matches current wasm-pack output
2 – Test OrchestrationIntroduce Bazel test targets wrapping Vitest (select dirs)Run subset tests via Bazel cachingPassing selective test runs
3 – Site Build WrapperWrap astro build as Bazel sh_binary / genrule (non-hermetic)Cache static build artifactsBuild output reproducible & cache hit on no code change
4 – Multi-Lang GraphAdd license scan, SBOM, security scan as Bazel test or sh_test targetsUnified pipeline viewCI executes Bazel for majority of quality gates
5 – Remote Cache (Optional)Configure GCS/S3 cacheReduce cold build time on CI>50% cache hit on repeated PRs
WORKSPACE.bazel
├─ rules_js, rules_rust, rules_python (if ever), platforms
├─ npm translation lock (via rules_js: pnpm / package.json import)
└─ rust toolchain pinned
wasm/session-signer/BUILD.bazel
rust_library(
name = "signer_lib",
srcs = ["src/lib.rs"],
crate_features = ["wasm"],
)
# Custom rule or genrule to invoke wasm-bindgen/wasm-pack if needed:
genrule(
name = "signer_wasm",
srcs = [":signer_lib"],
outs = ["signer_bg.wasm", "signer.js"],
cmd = "$(location //tools:wasm_pack) build --release --target web --out-dir $(RULEDIR) $(execpath :signer_lib)",
)
tests/BUILD.bazel (later)
nodejs_test(
name = "unit_auth_tests",
data = glob(["unit/**.test.ts"]),
entry_point = "//tools:test_runner.js",
)

NOTE: nodejs_test semantics differ depending on chosen ruleset. With modern rules_js, you’d define js_run_binary + harness script.

Astro’s dev server + HMR: keep outside Bazel for local iteration; only wrap production astro build for caching & dependency graph inclusion. Same pattern for sanity build.

AreaRecommended RuleComment
JavaScript / TypeScriptrules_jsReplacement trajectory for rules_nodejs; integrates package lock import
Rustrules_rustMature; good for crate graphs
Docker (optional)rules_oci / rules_dockerFor deterministic image layering; could replace manual docker-compose build flows for custom services (Zitadel third-party—leave external)
WASMCustom wrapper + wasm-bindgen-cliNo first-class universal rule; simple genrule acceptable
OPA Policiesrules_opa or shell sh_test invoking opa evalLight integration
  1. bazel fetch //... (warm external repos)
  2. bazel build //wasm/... //site:astro_build (selective subsets)
  3. bazel test //tests:unit_auth_tests --test_output=errors
  4. Fallback tasks (Lighthouse / Playwright) remain external until wrappers stable
  5. Introduce bazelrc with common flags: --remote_cache=... (future), --experimental_remote_downloader=..., --repository_cache=...
RiskMitigation
Early Full MigrationStart with WASM only
Complex Custom RulesPrefer thin genrule wrappers first
Developer FrictionProvide make / npm script aliases e.g. npm run build:wasm:bazel
Unnecessary WrappingDon’t Bazel-ize ephemeral dev flows (tunnels, local HMR)
MetricBaselineTarget After Phase 3
Cold CI build time(Measure first)-25%
Repeat PR build time(Measure)-40% with cache
Selective test runtimeFull suite< 30% of full on small changes
Cache hit rate0%> 60% after stable graph

Given current repository size and diversity, immediate Bazel adoption is optional and not critical to velocity. The highest leverage near-term engineering investment remains security/test breadth + automation gating (already being enhanced). Adopt Bazel later when build graph complexity or test duration starts to materially impact cycle time.

14. If You Want to Explore Now (Low-Risk Steps)

Section titled “14. If You Want to Explore Now (Low-Risk Steps)”
  1. Create experimental/bazel/ branch
  2. Add WORKSPACE.bazel + minimal wasm/session-signer/BUILD.bazel
  3. Confirm bazel build //wasm/session-signer:signer_wasm reproduces wasm-pack output
  4. Document steps in BAZEL_ADOPTION_LOG.md
  5. Re-evaluate value after 2 weeks of trial

15. Quick POC Snippets (Not Yet Added to Repo)

Section titled “15. Quick POC Snippets (Not Yet Added to Repo)”
# WORKSPACE.bazel (excerpt)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# rules_rust
http_archive(
name = "rules_rust",
urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.44.0/rules_rust-0.44.0.tar.gz"],
sha256 = "<sha256_here>",
)
load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies")
rules_rust_dependencies()
# rules_js (example)
http_archive(
name = "aspect_rules_js",
urls = ["https://github.com/aspect-build/rules_js/releases/download/v2.0.0/rules_js-v2.0.0.tar.gz"],
sha256 = "<sha256_here>",
)
# wasm/session-signer/BUILD.bazel (simplified)
load("@rules_rust//rust:defs.bzl", "rust_library")
rust_library(
name = "signer_lib",
srcs = glob(["src/**/*.rs"]),
edition = "2021",
)
genrule(
name = "signer_wasm",
srcs = [":signer_lib"],
outs = ["signer_bg.wasm"],
cmd = "wasm-pack build --release --target web --out-dir $(@D)/out . && cp $(@D)/out/*wasm $@",
tools = ["@npm//:wasm-pack"], # or system dependency first pass
)

Adopt incrementally and experimentally; do not pause current testing/security roadmap for Bazel migration yet. Value can be re-assessed after codebase or team scale increases.


If you’d like, next step can be an experimental branch scaffold—just say the word and I can generate initial WORKSPACE + BUILD stubs (kept isolated).