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:
- Test (or test suite) starts. CI / harness creates a fresh branch of the reference database (production, or a golden fixture).
- Test runs against the branch's connection string — using the same ORM, same query code, same schema as production.
- 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:
- 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.
- 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.
- Branch isolation — writes on one branch must not affect the parent or siblings. Critical for QA destructive-test branches especially.
- 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.
Related¶
- concepts/integration-tests-against-real-database — the workflow primitive this pattern formalises.
- concepts/mock-object-maintenance-cost — the specific cost this pattern eliminates.
- concepts/database-branching — substrate primitive.
- concepts/copy-on-write-storage-fork — mechanism.
- concepts/test-feedback-loop — property optimised.
- systems/lakebase — canonical substrate in POC.
- patterns/branching-is-pitr-with-time-now — the architectural unification that makes cheap branching possible.
- patterns/policy-testing-via-database-branching — sibling pattern at governance-policy altitude.