Developer ships a sneaky Go transaction bug — so he built a linter

April 14, 2026
Close-up of a man with binary code projected on his face, symbolizing cybersecurity.
Photo by cottonbro studio on Pexels

The bug

It has been reported that a Go backend developer accidentally shipped a transaction bug that silently let database operations run outside their intended transaction. The mistake was banal: inside a Transaction(ctx, func(tx models.Repo) error { ... }) callback the code still called s.repo.GetUser(...) instead of tx.GetUser(...). It compiles fine. Tests often pass. Failures show up under load as subtle data corruption. Cue long, frustrated debugging sessions — the kind that makes you bang your head against the desk.

Building the linter

Rather than leave it to luck, the author turned to static analysis. Using Go’s go/analysis framework he wrote a custom linter that walks the AST, identifies the transaction parameter, tracks which repository value each call uses, and flags calls that leak to the outer repository. The implementation allegedly includes recursive analysis of helper functions, traversal of callbacks, and tests driven by analysistest — so the rule runs at compile time, not just in code review or CI.

Why it matters

This is a neat example of catching structural, not behavioral, bugs with tooling. Static checks aren’t glamorous, but they’re a pragmatic guardrail — small insurance that stops silent corruption before it happens. So: want fewer late-night postmortems? Maybe it’s time to add a linter to your repo. The author’s final note: you should write your own linter — and honestly, could there be a better kind of tech therapy?

Sources: leonh.fr, Hacker News