Note · Building on MCP
What a database driver with no transactions actually costs
A suite of 1,257 passing tests shipped a bug that returned 500 on every call, because the tests and production ran on different database drivers.
- Written by
- Corey
- Published
- 19/08/2026
The tests were green. All 1,257 of them.
getDb() returned a neon-http client and cast it to NodePgDatabase. That cast is the whole
bug. neon-http has no transactions, but the type says it does, so db.transaction() compiles
cleanly and throws only when it runs, under a driver the tests never used.
What that buys you is a suite proving the code typechecks and proving nothing about whether it works. The failure surfaced in production, on every call, as a 500.
Two things came out of it. Concurrency that leaned on a transaction is now a unique index plus
ON CONFLICT, which the database enforces whether or not the driver can hold a transaction
open. And the test database driver matches the deployed one, because a test running on different
infrastructure from production is testing different software.
The general form is worth more than the specific fix. A cast is a claim you make to the compiler, and the compiler believes you. If the claim is wrong, every test downstream of it is measuring the wrong thing, and the greener the suite the longer it takes anyone to notice.
Where this stops working
- This is specific to drivers without transaction support. Against a driver that genuinely implements them, the workaround here is pointless overhead and you should not copy it.
- A unique index plus ON CONFLICT only covers concurrency you can express as a constraint. Multi-row invariants still need a real transaction, which means changing driver rather than working around it.