Favicon of Apache Thrift

Apache Thrift

Apache Thrift is a cross-language services framework: an interface definition language and code generator that emit both the serialization code and the RPC client and server for more than a dozen languages, with pluggable protocols and transports underneath.

LicenseOpen source (permissive)· Apache-2.0
DeploymentSelf-hostedManaged
PricingFree
Operational complexityLow
WorkloadInteractiveStreaming
LanguageC++, Java, Python, PHP, Ruby

Use it when

  • One definition should generate types, serialization, and a working RPC client and server across a very wide polyglot estate; the compiler targets 28 languages.
  • The wire encoding should be a choice: binary, compact, or JSON protocols over pluggable transports, from one set of definitions.
  • You maintain systems that embedded it early; the Hive metastore API and Parquet's metadata are both defined in Thrift IDL.
  • Services and exceptions belong in the IDL with generated behavior, not conventions layered on top.

Think twice when

  • A schema registry is part of the platform; no Kafka registry accepts Thrift, which closes the registry-governed path outright.
  • New service development is on the table; the ecosystem's momentum is with protobuf and gRPC, and even Meta now evolves its own separate fbthrift.
  • Numeric payloads need unsigned integers or 32-bit floats; the type system has neither.
  • Requiredness discipline is weak on the team; required fields cannot be deprecated and removing one breaks compatibility.

How it runs

The thrift compiler joins every language's build and emits clients, servers, and types; processes then speak the binary or compact protocol over pluggable transports. The wire has been stable for sixteen years while the 0.x libraries churn above it, with releases roughly twice a year.

Details

Compare

How Apache Thrift answers the questions Serialization Formats turns on.

Serialization Formats
How it works
EncodingThe answer no other row here gives: the encoding is a choice rather than a property of the format. Protocols (binary, compact, JSON, SimpleJSON) and transports (buffered, framed, HTTP, memory, zlib) are pluggable and orthogonal, so one set of definitions travels as dense binary between services and as JSON where a human has to read it. Binary writes a field header of type tag plus 16-bit field id before each value; compact zigzags and varints its integers and stores the field id as a 4-bit delta from the previous field, falling back to a long form when the gap exceeds 15. A server can tell which it is being sent from the first byte. "Thrift" therefore names a family of encodings, which is why comparing it to a single wire format is always slightly wrong
Random accessNone, and structurally rather than by omission. Both protocols are sequential streams with no offsets, no index and no framing that would let a reader jump to a field; decoding starts at the beginning. The compact protocol makes that stronger than a missing feature: field ids are stored as deltas from the preceding field, so the location and identity of a value cannot be known without having decoded every header before it. This is the axis Cap'n Proto and FlatBuffers were built on, and Thrift sits at the far end of it beside protobuf, a format for messages you parse, not buffers you address
Self-describingPartially, and the boundary is precise. Every field on the wire carries a type tag and its numeric field id, so a reader can walk the structure and skip what it does not recognise (the binary protocol specification says unknown fields "are simply ignored") which is what makes rolling upgrades work without a schema exchange. What the wire does not carry is names or meaning: field 7 is an i32, and only the .thrift file knows it is a timestamp in milliseconds. That places it between Avro, which cannot read a byte without the writer's schema, and JSON, which carries every name and no types
Schema evolutionA design goal stated in the project's own README ("specifically designed to support non-atomic version changes across client and server code", so a server can be upgraded while older clients keep calling it, and newer clients can call older servers. The mechanism is numeric field ids plus requiredness, and the specification is unusually candid about where it goes wrong: required fields "drastically limit the options with regard to soft versioning" because they cannot be deprecated, and removing one or relaxing it to optional breaks compatibility outright. Default requiredness) internally "opt-in, req-out", is the recommended starting point and optional with isset flags the safe one. All of it is enforced by convention and review, since nothing checks a change before it ships
Type systembool, byte and i8, i16, i32, i64, double, string, binary and uuid, with list, set and map containers, and struct, union, exception, enum, typedef, const and service as declarations. Two absences are deliberate and both bite: no unsigned integers, which the documentation justifies by their absence from many target languages, and no 32-bit float, double or nothing, which is a real cost for numeric payloads where protobuf and Avro both offer single precision. What sets the type system apart is the other end: exceptions and services are IDL constructs with generated behaviour rather than conventions layered on top, which is what makes this a service framework that happens to serialize
RPCThe reason this row exists, and the only one here where RPC is in the format's own toolchain rather than a separate project layered over it. The exchange is half duplex: the client sends a message header carrying a method name, a message type and an int32 sequence id, then the argument struct; the server replies with its own header and a result or exception struct. Message types are Call, Reply, Exception and Oneway; the sequence id lets a client detect out-of-order responses; a oneway call skips the response entirely and the IDL enforces a void return for it. A multiplexed protocol prefixes the service name onto the method so one endpoint serves several services, at the cost of being incompatible with unmultiplexed peers. Where protobuf needed gRPC to become an RPC story, Thrift shipped with one in 2007
Spec versionsThe version number is the eye-catching part and the least informative. Sixteen years as an Apache top-level project and still on 0.x, with 0.24.0 released in July 2026, no published release calendar and a README that says only that the project strives to release twice yearly. The wire is the stable part, and extended rather than revised: the binary protocol, compact protocol and RPC exchange are specified as documents in the repository, and the most recent substantive addition (a builtin uuid type in 0.18.0, carrying a new type code) left everything already written decoding exactly as before. The churn is above the wire, in generated APIs and language libraries, where a compiler targeting 28 languages breaks something in most releases. A small tell about how the documentation keeps up: the IDL specification on master is stamped for 0.25.0 while the language support matrix beside it still says 0.22.0
Adoption and maturityThe veteran of the capability, and its adoption is better measured in what it holds up than in its stars. Written at Facebook and open sourced in April 2007, into the Apache Incubator in 2008 and a top-level project in 2010, with about 10.9k stars and 4.1k forks today. Two loads it still carries are in this catalog already: the Hive Metastore's API is defined in Thrift IDL, which is why "Hive metastore compatible" recurs across the table-catalog leaf, and Parquet's metadata is Thrift-defined, which is why Nimble's choice of FlatBuffers over thrift and protobuf was worth stating. Two facts point the other way and belong beside them: Cassandra removed the Thrift protocol outright in 4.0, and Meta, who wrote it, now runs facebook/fbthrift, by its own README "not a distribution of Apache Thrift" but an evolved internal branch with a compiler rewritten from scratch, going its own way
Connections
Registry supportNone, and this is the cell that decides Kafka shortlists. Every registry in this catalog's schema-registry leaf accepts the same short list and Thrift is on none of them: Confluent takes Avro, JSON Schema and Protobuf; Karapace the identical three; AWS Glue those three with version caveats; Buf's registry Protobuf and nothing else; and Apicurio, the broadest at nine artifact types, spends its breadth on OpenAPI, AsyncAPI, GraphQL, WSDL and XSD rather than on Thrift. Apicurio can register custom artifact types at deployment time, which is the only door and not an open one. A platform standardising on a schema registry is not standardising on this
Generated codeMandatory, and the widest in the capability: from one definition the compiler emits the types, the serialization and a working RPC client and server for 28 languages. That is the project's tagline and its real differentiator, but the same fact is the cost, because nothing here can be read dynamically the way an Avro record can be decoded against a schema fetched at runtime. Without generated code you have bytes, type tags and field numbers. Every language a service touches needs the compiler in its build, which is the price of the breadth

Share:

Alternatives to Apache Thrift

Favicon

 

  
  
Favicon

 

  
  
Favicon