Like everyone else, I've been experimenting with agentic coding. Lately I've been vibing with a bunch of frameworks to figure out what's good and what sucks stack-wise when it comes to AI code generation. On the way I ventured out of my regular stack, picking up the Spring framework for the first time and coming across a (to me) novel way of querying the database. This is the story of re-writing that idea in Go.
Spring Data JPA is an ORM with a very comprehensive feature set. The bit that caught me was its query methods: a neat piece of metadata programming where you define a method on a repository interface and the framework derives the SQL straight from the method name. Define findByLastNameAndAgeGreaterThan and you get WHERE last_name = ? AND age > ?. I was in love with the terseness and how little you actually have to write — so I re-wrote that bit in Go as a code generator. Behold github.com/ebuckley/repomatic.
It comes with far less sugar than the full JPA API does, but it holds the same basic principle: you hand it a plain interface and some plain old structs for your model layer, and it generates the implementation. So an interface like this:
//go:generate go run github.com/ebuckley/repomatic/cmd/repomaticgen -type=Books,Authors
type Books interface {
FindByAuthorLastName(db repomatic.DB, ctx context.Context, lastName string) ([]Book, error)
}
...turns into a query like this:
SELECT books.*
FROM books
JOIN authors ON books.author_id = authors.id
WHERE authors.last_name = $1
Under the hood I got to build a parser and a Go SQL generator that emits the struct implementation of the defined interface. There were challenges, like trying to disambiguate field names from operator names, and joining to related fields in a sensible way.
Where I landed I am happy enough with. It's probably not something you would lightly take into production yet, but it was a fun exercise and a usable library for prototyping on top of SQL.
I wrote this with Claude Code, applying what I think of as a 'diffusion' model of code generation: start with rough interfaces and a bit of direction, generate a proof of concept, write tests, then repeat until you're happy with the implementation. Start blurry, sharpen on each pass.
The primary way it differs from the JPA implementation is that there is zero reflection at runtime. Instead I generate the actual Go code ahead of compile time.
My initial thought was that code generation would be the sensible, idiomatic Go approach — but while trying to implement it dynamically I learned that it's actually the only approach. In the Java world you can reflect on the method name and then construct your implementation of the interface at runtime. This is simply impossible in Go. You can use an unsafe operation to replace pointers to functions, but you can't construct the implementation of an interface during runtime — there has to be an actual struct implementing it, and then a small type called an itable joins the interface to its implementation. So I went down a rabbit hole learning about Go's internal memory layout, and came out the other side generating real code.
Another tricky problem was parsing identifiers and separating them from operators. In a normal language you have a character to do this — first_and_lastname and age splits cleanly into 3 tokens [first_and_lastname, and, age]. In my little JPA-style repo method there are no separators at all; it's all one camelCase word. So FirstAndLastName could be tokenized as [first, and, last_name] or as a single field [first_and_lastname], and only one of those is right.
To solve this I kept a memory of the field names defined on each type and searched that first. The final trick is to always search for the largest token first, then reduce to token + operator until something matches.
I am very busy, and I never would have gotten around to this if I didn't have agents at my fingertips. It's fun to iterate and play with ideas. I might use this in a future project — dunno!