Discussion
I shipped a transaction bug, so I built a linter
mnahkies: Aside from data consistency issues mentioned, you can also quickly get yourself into connection pool exhaustion issues, where concurrent requests have already obtained a transaction but are asking for another accidentally, then all stall holding the first open until timeouts occur.
dgunay: I wrote exactly this linter a while back after making the same mistake. Very annoying. Unlike you I did try to get it into golangci-lint but the process wore me down. In the age of LLMs maybe it'd be worth another try.
rowyourboat: This shows, once more, that humans are bad with modes. You have two copies of the repo, one in a transaction and one not in a transaction.The problem is that the thing you use to build the transaction can also be used to directly manipulate the DB. A better API design would be to separate those two things.