
We use GORM heavily at work. 10+ Go services, all of them leaning on it for the ORM layer. And there's
one thing about GORM that has quietly bothered me for a long time:
db.Preload("Orders").Find(&suppliers)
Preload takes a string. Just a string. Nobody — not the compiler, not your editor, not your tests — can
tell you whether "Orders" is still a real relation on Supplier. You typo it, you rename the field, you
delete it in a migration, you preload a scalar by mistake — the build succeeds anyway. You find out at
runtime. Or worse, you don't find out, because GORM silently does nothing and the endpoint just
returns empty data.
Preload is a stringly-typed API in a statically-typed language. But Go has go/types. Structs are
inspectable. So why is nobody checking these at build time?
I wrote gpc (Gorm preload checker). It walks your AST, finds every db.Preload("X").Find(&T) chain,
resolves T through go/types (handles pointers, slices, embedded *gorm.DB wrappers, cross-package
models, constants), and walks the dotted path "User.Profile.Address" through the actual struct fields.
If a segment doesn't exist or isn't a relation, it prints the file and line.
Then I pointed it at every Go service we have. Ten codebases. 2,571 Preload calls. It flagged 18.
I went through all 18 by hand expecting false positives. There were none. A sample of what was sitting
in production:
- Preload of a relation field that had been commented out in a migration months ago
- Three preloads of a relation that never existed on the struct — copy-pasted from a different model
- Preload("Service") where the actual field was named OrgService
- Preload of a scalar float column treated as a relation
- A preload referencing a field deleted two quarters ago
18 latent bugs across 10 services. Nobody had filed tickets for any of them. They were just sitting
there, waiting for the right query path to hit prod.
The tool is ~640 lines of Go, MIT licensed, no runtime deps: github.com/your-moon/gpc
Run it on your repo. I'd genuinely like to know if you find any false positives — I haven't seen one
yet, but my sample is biased to one company's code style.
If you use GORM, you almost certainly have at least one of these.