Skip to content

PATTERN Cited by 3 sources

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/database-branch-per-test-over-mocking — 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.
  • 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-09-18-databricks-database-branching-a-developers-guide-to-git-style-workflows — Per-PR isolation as the canonical CI shape. Databricks 2026-09-18 Tier-3 explainer names the pattern precisely: "CI creates the branch when the PR opens, applies the proposed migrations, and runs integration tests against it. When the PR closes, the pipeline deletes the branch." The load-bearing benefit is conflict isolation — "two developers can make conflicting schema changes without affecting each other's tests" — each PR gets its own branched production-like state instead of contending over shared staging. Reinforces the realistic-data rationale over mocking: a migration can pass on an empty DB but fail against production-scale rows, so testing on a branch forked from a production snapshot catches the failure before staging. Pairs the pattern with a TTL guardrail so abandoned PRs don't leave CI branches running.

  • sources/2026-05-29-databricks-enabling-evolutionary-database-development-database-branching-with-lakebase compensating-layer coming out when

Practice #4 of evolutionary database design becomes affordable. Re-uses Fowler's 2003 Jen protagonist running the Split Column refactoring on a Lakebase branch — application tests pass against real Postgres, no mocks, no in-memory substitutes. "Whatever her team uses – Flyway, Liquibase, Alembic, Knex, Prisma – the script lives in the code repo, alongside the application changes... She runs her test suite. Tests pass against real Postgres no mocks, no in-memory substitutes." New named IDE-glue system: Lakebase SCM Extension. Sibling pattern at developer altitude: patterns/per-developer-database-branch-paired-with-code-branch; sibling at CI altitude: patterns/database-branch-per-test-over-mocking.

  • 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.

Merged aliases

  • policy-testing-via-database-branching
  • view-over-copy-for-test-data-environment- ci-ephemeral-database-branch-with-schema-diff-comment
Last updated · 766 distilled / 2,225 read