Protocol Buffers (Protobuf) in gRPC

Protocol Buffers define compact, strongly typed message schemas used by gRPC services. A `.proto` schema describes messages and RPC contracts, and generated Java classes handle serialization and deserialization.

Protocol Buffers, commonly called Protobuf, are a schema language and binary serialization format. In a gRPC application, a `.proto` file can define both message structures and service methods, giving client and server code a shared contract.

A message is made from typed fields, each identified by a numeric field tag. For example, `string name = 1;` means that `name` has field number `1`. The field number is part of the wire format, so changing or reusing numbers carelessly can break compatibility with existing serialized data.

The Protobuf compiler, `protoc`, generates language-specific classes from the schema. In Java, generated message classes provide APIs for constructing messages and converting them to and from the Protobuf binary representation. gRPC tooling also generates client and server-side interfaces from service definitions.

Protobuf is designed for efficient network communication: its binary encoding is generally smaller and faster to process than text formats such as JSON. The schema also supplies explicit types, which helps catch contract mismatches during development rather than relying only on runtime parsing.

For evolving a production schema, preserve existing field numbers and avoid reusing numbers from removed fields. In `proto3`, fields can be added in a backward-compatible way when clients and servers are designed to tolerate unknown fields and the new fields are handled appropriately.

Key points

Interview questions

What is Protocol Buffers and why is it commonly used with gRPC?

Protocol Buffers are a language-neutral way to define structured messages and serialize them efficiently. A `.proto` file provides a shared schema that can be compiled into Java and other language-specific classes. gRPC uses Protobuf schemas to define request and response messages and commonly the RPC service contract as well. This gives distributed services a strongly typed contract and an efficient binary wire format.

Why does a Protobuf field have a number such as `string name = 1`?

The number is the field's tag in the Protobuf wire format. The serialized representation uses that numeric identifier so the decoder can associate encoded data with the correct field. The tag therefore becomes part of the schema's compatibility contract. Existing field numbers should not be casually changed or reused.

What does `protoc` generate for a Java gRPC project?

`protoc` compiles the `.proto` definitions into language-specific code. For Java, this includes generated message classes for Protobuf messages. With the appropriate gRPC Java plugin, service definitions also produce generated client and server APIs. Application code uses these generated types instead of manually implementing the wire encoding.

How would you evolve a Protobuf schema without breaking existing clients?

Keep the numeric tags of existing fields unchanged. Add new fields with new numbers, and avoid reusing numbers belonging to removed fields. Clients and servers should tolerate fields they do not understand, allowing different versions to communicate during a rollout. Schema changes should therefore be reviewed as compatibility changes, not merely as local code edits.

Why might a backend team choose Protobuf instead of JSON for service-to-service communication?

Protobuf provides an explicit schema, compact binary encoding, and generated strongly typed APIs. These properties can reduce payload size and avoid repeated manual parsing or loosely typed contracts. It also integrates directly with gRPC, where the same schema can describe RPC messages and services. The trade-off is that binary payloads are less human-readable and require tooling to inspect conveniently.