Wire Types
Define a network message format that can evolve without breaking existing consumers. Every service that communicates over a wire needs this.
Hew
Wire types live in the same .hew file as your code. Field numbers (@N) ensure binary compatibility across versions. #[json(camelCase)] controls JSON serialization naming. The compiler enforces that you don't reuse or remove field numbers.
#[json(camelCase)]
wire type UserEvent {
user_id: i32 @1;
user_age: i32 @2;
}
// Adding a new field later:
// email: String @3; <- existing consumers ignore @3
fn main() {
let event = UserEvent {
user_id: 42,
user_age: 30,
};
println(f"User {event.user_id}, age {event.user_age}");
}Go + Protobuf
Protobuf definitions live in a separate .proto file using a different language. A code generation step (protoc) produces Go structs before you can compile. Three files, two languages, one build tool.
// user_event.proto — separate file, separate language
syntax = "proto3";
message UserEvent {
int32 user_id = 1;
int32 user_age = 2;
}# Generate Go code from .proto definition
protoc --go_out=. user_event.proto// Use the generated code
import pb "myapp/proto"
event := &pb.UserEvent{UserId: 42, UserAge: 30}
data, err := proto.Marshal(event)
if err != nil {
log.Fatal(err)
}
var decoded pb.UserEvent
proto.Unmarshal(data, &decoded)Rust + serde
Serde gives you JSON serialization with derive macros, but no field-number stability for binary evolution. For binary wire compatibility, you need the protobuf toolchain (same as Go) plus a crate like prost.
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct UserEvent {
user_id: i32,
user_age: i32,
}
// No field numbering — JSON only, no binary wire stability
// For binary stability, need prost crate + .proto file
fn main() {
let event = UserEvent {
user_id: 42,
user_age: 30,
};
let json = serde_json::to_string(&event).unwrap();
let decoded: UserEvent =
serde_json::from_str(&json).unwrap();
}What this shows
Developer experience
Hew eliminates the .proto → codegen → import pipeline. Wire types are defined alongside the code that uses them — one file, one compilation step. No separate build tool, no generated code to keep in sync.
Debugging
In protobuf, reusing a field number with a different type causes silent data corruption — the wrong field gets populated with garbled data, and you only notice downstream. Proto3 ignores unknown fields gracefully, but the compiler won't catch a reused number across services. Hew's compiler checks field numbers at compile time, catching reuse and removal before the code ships.
Trade-offs
Protobuf has mature tooling, cross-language support (Java, Python, C++, and more), and an IDL-first design that forces schema documentation. Teams working across multiple languages benefit from a shared .proto definition. Hew's wire types are currently Hew-only — they work well for Hew-to-Hew communication but don't generate bindings for other languages.