Apache Thrift primer
Apache Thrift is a cross-language IDL plus an RPC stack. You write one.thriftschema, then generate type-safe client and server stubs in C++, Python, Go, Rust, Java, and a dozen other targets. Meridian uses Thrift on hot internal hops where JSON overhead is unacceptable and we still want polyglot consumers.
1. Write the schema
Thrift schemas are field-numbered, like Protobuf. Field numbers are the wire contract, so never renumber or reuse an id. Mark fieldsrequiredsparingly — once shipped, removing a required field is a breaking change for every downstream consumer.
// hero.thrift
namespace cpp meridian.hero
namespace py meridian.hero
struct HeroStats {
1: required string name,
2: required i32 level,
3: required double winRate,
4: optional list<string> roles,
}
service HeroService {
HeroStats GetHero(1: string id),
list<HeroStats> ListByRole(1: string role),
}2. Generate & wire transport
The compiler emits per-language stubs. Pair them with a transport (TSocket, TFramedTransport) and a protocol (TBinaryProtocol or TCompactProtocol). Meridian standardizes on TCompactProtocol over TFramedTransport — it cuts wire size by roughly 40% on hero stat payloads versus TBinaryProtocol.
# generate bindings thrift --gen cpp:no_skeleton hero.thrift thrift --gen py hero.thrift # wire transport (TBinaryProtocol over TSocket) ./hero_server --port 9090 python client.py --host 127.0.0.1 --port 9090
3. Evolve without breaking
Add new fields asoptionalwith fresh field ids. Old clients silently skip unknown ids; new clients tolerate missing optionals. Never delete a struct field — reserve the id with a comment so a future engineer cannot accidentally recycle it. For service methods, additions are safe, signature changes are not. When you must break, version the service name (HeroServiceV2) and run both endpoints in parallel until the older clients drain.
Next up: see thegRPC primerfor a side-by-side comparison, or jump to theProtobuf primerfor schema design tradeoffs.