transactioncheck
- title
- transactioncheck
- type
- toolbox
- summary
- Go linter catching database calls that use the outer repo instead of the transaction handle
- tags
- golang, static-analysis, databases
- language
- Go
- license
- MIT
- created
- 2026-04-15
- updated
- 2026-04-15
A Go linter that detects database operations leaking outside transaction boundaries. Built with the go-analysis-framework after the author shipped a transaction bug to production.
What it catches
In codebases using callback-based transactions with repository patterns:
return s.repo.Transaction(ctx, func(tx models.Repo) error {
user, err := s.repo.GetUser(ctx, userID) // FLAGGED: uses s.repo, not tx
return tx.SaveUser(ctx, user)
})
The linter flags uses of the outer repository (s.repo) inside transaction callbacks where the transaction parameter (tx) should be used instead.
Two violation types:
- Direct method calls on the outer repo
- Passing the outer repo to helper functions instead of
tx
How it works
- AST walking filtered to
*ast.CallExprnodes - Identifies
Transactionmethod calls on repository interfaces - Captures the callback's
txparameter as atypes.Object - Scans the callback body for references to the outer repo
- Recursively analyzes helper functions that receive
tx
The types.Object comparison handles variable shadowing correctly โ two identifiers sharing the same object point to the same variable.
Usage
Run standalone:
go install github.com/leonhfr/transactioncheck/cmd/transactioncheck@latest
transactioncheck ./...
Or via mise task:
[tasks.transactioncheck]
run = 'bin/transactioncheck ./...'
Testing
Uses analysistest with // want comments:
_ = s.repo.GetUser( // want "using non-transaction repo..."
ctx, "123",
)
Limitations
- Requires consistent repository interface naming (configurable)
- Only tracks violations within the same package (no cross-package analysis)
- Recursive analysis may miss violations through interfaces
Linked from