How the Project Started
Over the past few years, I’ve worked intensively with gRPC and ProtoBuf—a brilliant, secure, and high-performance way to connect services. However, there’s often no way around RESTful APIs. Even if you can use gateways or proxies for translation, the challenge of providing clear “contracts” for consumers remains.
These contracts have long been described using OpenAPI specs. But writing OpenAPI often feels tedious; while it’s clearly understandable, it’s also too bloated to navigate without getting lost. A turning point for me was TypeSpec. Ever since I started writing my API specs with it, the whole topic has become far more approachable. The IDE support is excellent, the scope is reduced manifold—and the best part: clean OpenAPI specs can be generated from it with ease.
Ideally, you’d generate types and stubs directly from these specs. While gRPC provides suitable tools out of the box, I’ve never been satisfied with the results for Go in the OpenAPI ecosystem. Initially, I used Java-based tools, which felt somewhat out of place in my environment. That impression stuck with me so much that I started working on my own emitter for TypeSpec.
The Detour: A Custom TypeSpec Emitter
TypeSpec emitters—written in TypeScript or JavaScript—can generate any kind of output via templates, including Go code,
of course. But my goal was maintainable code that could also modify existing structures. I envisioned an AST-based
approach: TypeSpec -> IR -> Go. Initial experiments were promising, but let’s be realistic: this is a massive
undertaking. It’s feasible for pure types, but client and server stubs are a different story.
In truth, the origin of my project was something else: stable code generation as a foundation for test clients and server mocks—making APIs easy to test and control during development.
Rediscovering ogen
A crucial part of my iterative approach is reflection. That’s when I took another close look at ogen. Some time ago,
I had experimented with it but wasn’t convinced yet. But tools evolve. Revisiting it now, I have to say: I’m quite
impressed by ogen.
It’s not just about the functional result—it’s also about the style of the generated code. The way it handles optional
or nullable fields cleanly in Go is elegant. In some respects, I even prefer ogen over gRPC, especially compared to
its oneof implementation.
Sharpening the Focus
This means I can now fully concentrate on the core of Spec CLI: clients—with a focus on testing—and mock servers for integration tests. With this refined direction, the project can finally gain momentum.
The decision to rely on
ogenrather than reinventing the wheel with a custom emitter is entirely pragmatic. It’s tempting to build your own generators for perfect AST control, but the maintenance effort rarely justifies the benefit—especially when excellent tools already exist. The focus on testing clients and mocks gives the project a clear raison d’être beyond pure code generation.