Skip to main content

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.

FeatureProtobufAvro
OriginGoogle (2008)Apache Hadoop project (2009)
Specificationprotobuf.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 encodingTag (field number + wire type) + value per fieldValues only – in schema-defined order, no tags
Schema at decode timeReader schema only – unknown tags preservedBoth writer and reader schema required
Payload compactnessVery compact – tag overhead per fieldMaximally compact – zero per-field identifier overhead
Code generationRequired – protoc compiler generates stubsOptional – reflection API available without codegen
Language supportC++, Java, Python, Go, Rust, C#, PHP, Ruby, Dart, Kotlin, SwiftJava, Python, Go, C, C#, Ruby, Rust (fastavro, avro crate)
Schema evolutionField numbers never reused; add fields freelyBACKWARD / FORWARD / FULL compatibility modes enforced by registry
Nullable fieldsproto3: all fields nullable by defaultUnion type ["null", "T"] – null must be listed first for BACKWARD compat
Logical typesNone built-in – use int64 for timestamps7 defined: date, time-millis/micros, timestamp-millis/micros, duration, decimal
gRPC transportNative – gRPC uses Protobuf by defaultNot used with gRPC natively
Kafka / streamingSupported but less commonStandard Kafka wire format with Confluent Schema Registry
Container fileNo built-in file formatObject 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.