ADR-0002 — Monorepo for backend, clients, contracts, and ops
- Status: Accepted
- Date: 2026-06-11
- Related: 07, ADR-0001, ADR-0003
Context
BitVault spans Go services + CLI, a Next.js web app, a future RN app, protobuf contracts, SQL migrations, and deployment artifacts. These change together: a node-schema change touches the proto, the service, the event, the consumers, the REST mapping, the web client, and the docs. The team is small. Contract drift between API producer and consumers (R8) is a top risk.
Decision
Use a single monorepo containing backend (internal/, cmd/, pkg/),
contracts (proto/, gen/), clients (apps/web, apps/mobile), ops (deploy/,
migrations/), tests, and docs.
Tooling: Go workspaces (go.work) for Go modules; a JS monorepo tool
(pnpm workspaces + Turborepo) for apps/*; buf for protobuf lint + codegen;
a single task runner (Taskfile.yml); path-scoped CI so unaffected components
don’t rebuild.
Consequences
Positive
- One source of truth for contracts; generated Go/TS/OpenAPI cannot drift (R8).
- Atomic cross-cutting PRs; one version of shared platform libs.
- The extraction story is a diff in
cmd/, not a repo split (ADR-0001). - Simpler onboarding: clone once, see everything.
Negative / costs
- CI must be path-aware to stay fast as the tree grows.
- Mixed-language tooling (Go + JS + proto) needs deliberate setup.
- Coarse repo permissions (mitigated by CODEOWNERS).
- Larger checkout; potential for VCS scaling concerns at very large scale (not a concern at this size).
Alternatives considered
- Polyrepo (repo per service/client): rejected — reintroduces the contract- drift and cross-repo-PR pain the monorepo eliminates; premature for one team.
- Two repos (backend vs frontend): rejected — the proto/codegen boundary is exactly where drift hurts most; keeping it in one repo is the point.