Skip to content

PATTERN Cited by 1 source

Database branch per test over mocking

Pattern

On a substrate where database branching is sub-second and cheap (copy-on-write storage fork; e.g. Lakebase / Neon), replace database-interface mocks with per-test / per-PR / per- developer database branches.

The canonical shape:

  1. Test (or test suite) starts. CI / harness creates a fresh branch of the reference database (production, or a golden fixture).
  2. Test runs against the branch's connection string — using the same ORM, same query code, same schema as production.
  3. Test completes. Branch is discarded (or TTL'd).

The branch creation is fast enough (sub-second to few- seconds) to run per-test-suite, per-PR, or per-feature- branch. Per-individual-test-case is still latency-bound on most substrates in 2026 but is the natural extrapolation as branch-creation continues to get faster.

Canonical framing (Thoughtworks, 2026-04-30)

"The mock objects disappear. The staging collisions disappear. The 'works on my machine but breaks in staging' disappears, developers get a live database to try multiple solutions. The database changes that used to be discovered at deployment are now caught during development, where they're cheap to fix."

(Source: sources/2026-04-30-databricks-backstage-with-lakebase)

The load-bearing claim: the failure modes mock objects produce — divergence from production behavior + false confidence — are not solved by more careful mocking. They're solved by removing the mock layer and replacing it with a cheap real-database branch.

Workflow shapes

Three altitudes at which the pattern applies:

Per-developer IDE branch

  • Trigger: developer creates a git feature branch.
  • Action: IDE extension automatically creates a matching database branch off production (or a golden baseline).
  • Lifetime: persists until the git branch is merged or deleted.
  • Cost: one branch per developer per open feature branch. Per-developer hygiene: clean up stale branches on PR merge.

Thoughtworks describes work on a VS Code / Cursor extension that "synchronizes git and database branches automatically." Not yet shipped at the POC altitude; the shape is the goal.

Per-PR CI branch

  • Trigger: CI workflow triggered by PR.
  • Action: CI job creates a fresh branch off main (or a canonical baseline); runs the full test suite against it; runs the schema migration; publishes a schema diff; tears down the branch on exit.
  • Lifetime: seconds to minutes.
  • Cost: one branch per CI run. Tear-down on CI exit is idempotent + safe.

Already demonstrated in the Thoughtworks POC for Backstage — CI branch validates schema + code together on real data.

Per-QA destructive-test branch

  • Trigger: QA engineer starts testing a feature.
  • Action: QA gets a dedicated branch they can corrupt / reset at will.
  • Lifetime: until QA cycle ends.
  • Cost: one branch per QA engineer per active feature.

Enables QA engineers to run destructive tests (delete all rows, corrupt state, try invalid migration) without blocking other QA work or corrupting a shared staging database.

Trade-offs

Upsides

  • Test fidelity: full real-database behaviour (constraints, transactions, query-planner, lock ordering). Bugs caught during development, not staging.
  • Test infrastructure savings: the 20-30% of test code Thoughtworks cites as mock-object maintenance burden becomes deletion candidates.
  • Staging bottleneck removed: QA and developers don't compete for a shared staging DB. Each can own a branch.
  • Migration safety: schema changes validated against production-shaped data during development, not at deploy time. "The database changes that used to be discovered at deployment are now caught during development, where they're cheap to fix."
  • Solution-space exploration cheaper: multiple solutions can be tried on disposable branches; rollback is free.

Downsides

  • Test-time latency is larger: real-DB hits are roughly 1-2 orders of magnitude slower than in-process mocks. Test suites must be structured for this (parallel runners, fewer-longer tests over many-tiny tests).
  • Branch cleanup discipline required: stale branches accrue storage divergence; without TTL + PR-merge cleanup the total branch count grows unboundedly.
  • Sensitive data considerations: branching production- equivalent data into every developer environment raises PII / compliance concerns. Synthetic or anonymised baselines may be required before production data reaches developer branches.
  • Substrate lock-in: only works on substrates with cheap branching. Teams adopting this workflow structurally couple their developer experience to the copy-on-write-capable provider.
  • External-service dependencies still need mocks: the pattern replaces database mocks, not external-API mocks (payments, email, third-party services).

Structural preconditions

The pattern requires:

  1. Sub-second to few-second branch creation — measured at ~1 s for 63 MB on Lakebase. At minutes-per-branch the per-CI-run shape is still viable; the per-test shape is not.
  2. Scale-to-zero economics — if idle branches accrue significant cost, the "create per CI run, discard on exit" pattern becomes expensive. Lakebase's scale-to-zero economics make this affordable.
  3. Branch isolation — writes on one branch must not affect the parent or siblings. Critical for QA destructive-test branches especially.
  4. Branch cleanup primitive — explicit delete + TTL. Without TTL, stale branches accumulate.

Relationship to adjacent patterns

  • patterns/policy-testing-via-database-branching — same branching primitive applied to governance-policy testing rather than code testing. LangGuard (2026-04-27) canonicalised the policy-testing axis; this pattern is the code-testing sibling.
  • patterns/branching-is-pitr-with-time-now — the architectural unification that makes branching cheap.
  • Per-PR ephemeral environment (pre-existing wiki concept) — the classical cousin of this pattern, typically implemented as a full Kubernetes namespace or compose-stack per PR. Branch-per-test is the database- only, sub-second version of the same idea.

Seen in

  • sources/2026-04-30-databricks-backstage-with-lakebase — canonical first wiki instance. Thoughtworks Backstage POC articulates the eight-step traditional development cycle (mock objects, mocked tests, schema-surprise-at-staging) vs the eight-step branching-enabled cycle (IDE-branch, CI-branch, QA-branch, real-DB-tests, cleanup on merge). Paired with the 1.09-second branching number + the 20-30% mock-code savings claim.
Last updated · 439 distilled / 1,268 read