Protobuf vs Avro
Both Protobuf and Avro require a schema to encode and decode data – neither is self-describing at the wire level. The key architectural difference is how they handle field identity on the wire. Protobuf encodes a tag (field number + wire type) before every field value. The tag tells the decoder which field it is reading and how to interpret the bytes. This means Protobuf messages can be decoded without the writer schema – you only need the reader schema. Fields with unknown numbers are preserved for forward compatibility. Avro encodes only values, in the order defined by the schema, with no field names or tags. To decode an Avro binary payload you need both the writer schema (used when encoding) and the reader schema (used when decoding). Schema resolution rules map fields between versions by name. This produces maximally compact output – a row with 100 fields has zero per-field overhead – but requires schema coordination between producer and consumer. In the Kafka ecosystem, Confluent Schema Registry stores schemas by integer ID. Every Kafka message carries a 5-byte prefix (0x00 + 4-byte schema ID) and consumers fetch the writer schema from the registry. This makes Avro practical for Kafka despite the requirement for both schemas. For gRPC and microservice RPC, Protobuf is the standard choice. For Kafka event streaming and data warehouse pipelines, Avro is the standard choice.
Protobuf and Avro are both schema-first binary serialization formats, but they target different ecosystems and make different tradeoffs. Protobuf (Google, gRPC) uses field numbers for compact encoding and strong static typing across 10+ languages. Avro (Apache, Kafka) embeds the schema in JSON and encodes values without any field identifiers – maximally compact for wide rows. Avro's schema evolution model (BACKWARD/FORWARD/FULL compatibility modes) is more explicit and is enforced by Confluent Schema Registry.
| Feature | Protobuf | Avro |
|---|---|---|
| Origin | Google (2008) | Apache Hadoop project (2009) |
| Specification | protobuf.dev (open source), RFC 9996 (MIME types) | avro.apache.org spec 1.11 (Apache Foundation) |
| Schema language | .proto IDL (custom text format) | JSON schema (embedded in .avsc files) |
| Wire encoding | Tag (field number + wire type) + value per field | Values only – in schema-defined order, no tags |
| Schema at decode time | Reader schema only – unknown tags preserved | Both writer and reader schema required |
| Payload compactness | Very compact – tag overhead per field | Maximally compact – zero per-field identifier overhead |
| Code generation | Required – protoc compiler generates stubs | Optional – reflection API available without codegen |
| Language support | C++, Java, Python, Go, Rust, C#, PHP, Ruby, Dart, Kotlin, Swift | Java, Python, Go, C, C#, Ruby, Rust (fastavro, avro crate) |
| Schema evolution | Field numbers never reused; add fields freely | BACKWARD / FORWARD / FULL compatibility modes enforced by registry |
| Nullable fields | proto3: all fields nullable by default | Union type ["null", "T"] – null must be listed first for BACKWARD compat |
| Logical types | None built-in – use int64 for timestamps | 7 defined: date, time-millis/micros, timestamp-millis/micros, duration, decimal |
| gRPC transport | Native – gRPC uses Protobuf by default | Not used with gRPC natively |
| Kafka / streaming | Supported but less common | Standard Kafka wire format with Confluent Schema Registry |
| Container file | No built-in file format | Object Container File (.avro): self-describing with embedded schema |
When to use Protobuf
Protobuf is correct for: gRPC services (mandatory), internal microservice RPC with strongly-typed generated clients, polyglot environments needing first-class support across 10+ languages, and any system where you want to decode messages without a schema registry. Protobuf's static generated code provides compile-time type safety that Avro's reflection API does not.
When to use Avro
Avro is correct for: Apache Kafka event streaming (the de facto standard with Confluent Schema Registry), Apache Spark/Hadoop batch pipelines, data warehouse ingestion (Snowflake, BigQuery, Databricks all support Avro natively), and any scenario where schema evolution needs to be centrally governed and enforced at publish time. Avro's BACKWARD/FORWARD/FULL compatibility modes make it the right choice for long-lived event schemas that must evolve without breaking consumers.
Common Mistakes
- Using Avro without a schema registry in Kafka – without Schema Registry, every Avro producer and consumer must coordinate schema versions out-of-band. The 5-byte schema ID prefix is the mechanism that makes Avro practical; without it, Avro loses its primary advantage over Protobuf.
- Reusing Protobuf field numbers – field numbers in Protobuf must never be reused once a field has been deleted, or decoders using an old schema will silently misparse data. Reserve deleted field numbers with the reserved keyword.
- Making nullable Avro fields with union ['T', 'null'] instead of ['null', 'T'] – putting null second breaks BACKWARD compatibility. Schema Registry enforces the null-first convention for nullable unions.
- Using Protobuf for Kafka when the team already owns a Schema Registry – Protobuf works with Confluent Schema Registry (it added Protobuf support in 2020), but the tooling, documentation, and community patterns for Kafka are all Avro-first.
- Assuming Avro logical types are universally supported – decimal and duration logical types require library support. Not all Avro libraries handle all logical types. Verify your specific library version supports the logical types you need.
FAQ
Can Protobuf be used with Kafka?
Yes. Confluent Schema Registry added Protobuf support in version 5.5 (2020). Protobuf schemas are stored and versioned in the registry. However, the Kafka ecosystem tooling (Kafka Streams, ksqlDB, Spark connectors) has deeper Avro integration built over many more years. For new Kafka projects in 2025, Avro remains the path of least resistance. For teams already using Protobuf for gRPC, using Protobuf for Kafka as well avoids maintaining two schema systems.
Which format is more compact: Protobuf or Avro?
Avro is typically more compact for wide schemas (many fields) because it has zero per-field overhead – no field number tag. Protobuf has a minimum 1-byte tag overhead per field. For narrow schemas (2–5 fields), the difference is negligible. For schemas with 50+ fields, Avro's overhead advantage compounds. Both are significantly more compact than JSON or XML.
Does Avro require code generation?
No. Avro supports both specific (code-generated) and generic (reflection-based) record APIs. The generic API lets you work with Avro data as dynamic maps without generating classes. Protobuf also has a reflection API, but idiomatic Protobuf usage is almost always via generated stubs because the generated code is where the type safety value is captured.