I prefer to use Zod or JSON Schema as the source of truth. Then I use QuickType [1] in the build process to generate code in different languages. This lets me share data structures. I mostly do this in Tauri apps to keep the same data shape in TypeScript and Rust. I also use it to define protocols, like for REST APIs.
Here are the advantages of this approach compared to using guts lib: - I get validation with clear error messages. - It supports many languages out of the box. - I don’t need to maintain a custom library. - JSON Schema is well supported in LLMs (for example, with structured output or vibe coding).