4/14/2026 at 6:50:45 AM
The wording "outside of transaction" irks me. Everything in a relational database is done within a transaction, the only question is whether it's the transaction you think it is, or some other.I believe this is largely an API design problem. Many client APIs (especially ORMs) will start a transaction implicitly for you if you haven't explicitly specified your own, leading to problems like in the article.
Having implicit transactions is just wrong design, IMO. A better-designed API should make transactions very explicit and very visible in the code: if you want to execute a query, you must start a transaction yourself and then query on that transaction supplied as an actual parameter. Implicit transactions should be difficult-to-impossible. We - the programmers - should think about transactions just as we think about querying and manipulating data. Hiding from transactions in the name of "ergonomy" brings more harm than good.
by branko_d
4/14/2026 at 7:40:04 AM
I love how this is done in Software Transactional Memory (STM) in Haskell. There, the transaction code happens in its own type (monad), and there is an explicit conversion function called `atomically :: STM a -> IO a`, which carries out the transaction.This means that the transaction becomes its own block, clearly separated, but which can reference pure values in the surrounding context.
do
…
some IO stuff
…
res <- atomically $ do
…
transaction code
which can reference
results from the IO above
…
…
More IO code using res, which is the result of the transaction
…
by black_knight