Skip to main content

Protobuf Editions

Protocol Buffers has evolved through three editions. proto2 introduced the IDL. proto3 simplified it. Editions 2023 replaces the binary proto2/proto3 choice with per-feature flags for fine-grained control.

proto2

Stable2008
syntax = "proto2";

proto2 is the original open-source release of Protocol Buffers (2008).

Key Features

+

required, optional, and repeated field labels

+

Explicit default values: string name = 1 [default = 'unknown'];

+

Extensions: allow external files to add fields to a message

+

Groups (deprecated): embedded message with wire type 3/4

+2 more

Limitations

!

required fields break schema evolution – adding or removing requires coordinated multi-service deployment

!

More verbose than proto3 – every field needs a label

proto3

Stable2016
syntax = "proto3";

proto3 is the simplified 2016 edition of Protocol Buffers.

Key Features

+

No required fields – all fields implicitly optional

+

Simplified defaults: all zero values (no explicit default syntax)

+

Native map<K, V> type

+

oneof for mutually exclusive fields

+4 more

Limitations

!

No field presence for default-valued scalars without optional keyword – unset and zero are indistinguishable

!

No explicit default values – always zero value

Editions 2023

Current2023
edition = "2023";

Editions 2023 is the current and recommended Protobuf edition, replacing the proto2/proto3 binary choice with per-feature flags.

Key Features

+

Replaces proto2/proto3 binary choice with per-feature flags

+

features.field_presence: EXPLICIT | IMPLICIT | LEGACY_REQUIRED

+

features.enum_type: OPEN | CLOSED

+

features.repeated_field_encoding: PACKED | EXPANDED

+5 more

Limitations

!

Tooling support still maturing – some language SDKs have partial Editions support as of 2024

!

More complex schema files when overriding features at field level

Feature Comparison

How each edition handles the core behaviors that affect schema compatibility and code generation.

Featureproto2proto3Editions 2023
Syntax keywordsyntax = "proto2"syntax = "proto3"edition = "2023"
Field labelsrequired / optional / repeatedoptional (implicit) / repeated / singularfeatures.field_presence per field
required fieldsSupported (avoid)RemovedLEGACY_REQUIRED (avoid)
Scalar defaultsExplicit (default = value)Zero value onlyZero value by default; EXPLICIT presence restores proto2 semantics
Field presence (scalars)HasField() on optionalNo HasField() by default; optional keyword restores itEXPLICIT: HasField() tracked. IMPLICIT: not tracked.
Enum unknown valuesClosed – error on unknownOpen – preserved as integerfeatures.enum_type: OPEN or CLOSED per enum
Repeated field packing[packed=true] opt-inDefault packed for numericsfeatures.repeated_field_encoding: PACKED or EXPANDED
MapsNot supported nativelyNative map<K, V>Native map<K, V>
JSON mappingNot defined in specDefined and mandatoryDefined and mandatory
ExtensionsSupportedRemoved (use Any or oneof)Not supported (use Any or oneof)
UTF-8 validationNot requiredRequiredfeatures.utf8_validation: VERIFY or NONE

proto2

2008

proto2 is the original open-source release of Protocol Buffers (2008). It introduced required, optional, and repeated field labels. required fields are still valid in proto2 but are considered harmful – they prevent schema evolution. proto2 is maintained but new schemas should use proto3 or Editions 2023.

proto2 was open-sourced by Google in 2008 as the first public version of Protocol Buffers. It introduced the core concepts of .proto IDL, field numbers, wire types, and generated code.

Field labels in proto2: required: field must be present in every message. A message without a required field is considered incomplete. Using required prevents backward-compatible schema evolution – adding or removing required fields breaks existing producers and consumers. optional: field may or may not be present. If absent, the default value is used. optional fields can be checked for presence (HasField). repeated: zero or more values. Ordered list.

Extensions: proto2 introduced the extensions mechanism, allowing external .proto files to add fields to a message within a reserved number range. Largely superseded by proto3 Any and Editions features.

Default values in proto2: explicitly specified per field (default = value syntax). If not specified, the language default applies (0, empty string, false).

Proto2 is still widely used in codebases predating 2016. Existing proto2 schemas should not be migrated unless there is a specific reason – the migration risk outweighs any benefit for stable schemas.

Key Features

  • +required, optional, and repeated field labels
  • +Explicit default values: string name = 1 [default = 'unknown'];
  • +Extensions: allow external files to add fields to a message
  • +Groups (deprecated): embedded message with wire type 3/4
  • +HasField() available for all optional fields
  • +Enum values are closed – unknown values trigger an error (proto2 parser)

Limitations

  • !required fields break schema evolution – adding or removing requires coordinated multi-service deployment
  • !More verbose than proto3 – every field needs a label
  • !Extensions replaced by more ergonomic Editions features
  • !Not forward compatible with proto3 in some edge cases around required field handling

proto3

2016

proto3 is the simplified 2016 edition of Protocol Buffers. It removed required fields, simplified default values, added maps and oneof, and introduced JSON mapping. proto3 is the most widely deployed edition. New projects should use proto3 or Editions 2023.

proto3 was released in 2016 and became the standard edition for new Protobuf schemas. It simplified the language by removing required and making optional implicit.

Key changes from proto2: No required fields: all fields are implicitly optional. This was the most controversial change – required fields in proto2 were the primary source of schema evolution failures. No explicit default values: all fields default to the language zero value (0, empty string, false, empty bytes, nil enum). Custom defaults are not allowed. JSON mapping defined: every proto3 message has a well-defined JSON representation. Used by gRPC-web and REST transcoding. Maps: native map<K, V> syntax instead of a repeated message workaround. Oneof: mutually exclusive fields in a single union. Enum value 0 required: the first enum value must be zero and serves as the default.

Field presence in proto3: by default, proto3 scalar fields do not track presence. A field set to 0 / empty string / false is indistinguishable from an unset field on the wire. Use proto3 optional (optional keyword in proto3 syntax, added 2020) to restore HasField() semantics.

Packed repeated fields: proto3 makes packing the default for numeric repeated fields. proto2 required explicit [packed=true].

Key Features

  • +No required fields – all fields implicitly optional
  • +Simplified defaults: all zero values (no explicit default syntax)
  • +Native map<K, V> type
  • +oneof for mutually exclusive fields
  • +Well-defined JSON mapping for every message type
  • +Enum value 0 required as the first/default value
  • +Packed repeated fields by default for numeric types
  • +optional keyword (2020) restores field presence tracking for scalars

Limitations

  • !No field presence for default-valued scalars without optional keyword – unset and zero are indistinguishable
  • !No explicit default values – always zero value
  • !Enum values are open – unknown values are preserved as integers, not errors
  • !JSON mapping adds overhead for services that never use JSON

Editions 2023

2023

Editions 2023 is the current and recommended Protobuf edition, replacing the proto2/proto3 binary choice with per-feature flags. Features like field_presence, enum_type, repeated_field_encoding, and utf8_validation can be set per file, message, or field. Editions 2023 is backward compatible with both proto2 and proto3 semantics.

Editions replaces the coarse proto2/proto3 choice with fine-grained feature flags. Instead of selecting an edition that bundles a fixed set of behaviors, you choose the behaviors you want per file, per message type, or per field.

Core concept: every behavior that previously differed between proto2 and proto3 is now a named feature with explicit values. The edition sets the default values for all features; individual fields can override.

Key features in Editions 2023:

features.field_presence: EXPLICIT – field is always present if set, HasField() works for scalars (proto2 optional semantics) IMPLICIT – field presence not tracked; zero value = unset (proto3 default semantics) LEGACY_REQUIRED – equivalent to proto2 required (avoid in new schemas)

features.enum_type: OPEN – unknown enum values preserved as integers (proto3 behavior) CLOSED – unknown enum values trigger error (proto2 behavior)

features.repeated_field_encoding: PACKED – all values in one length-delimited block (proto3 default for numerics) EXPANDED – one tag per value (proto2 default)

features.utf8_validation: VERIFY – string fields are validated as valid UTF-8 NONE – no UTF-8 validation (proto2 legacy behavior)

features.message_encoding: LENGTH_PREFIXED – standard wire type 2 encoding DELIMITED – proto2 group encoding (wire types 3/4) for legacy compatibility

Migration: protoc and the buf CLI include tools to migrate proto2 and proto3 files to Editions syntax. The generated code behavior is unchanged during migration – Editions is a schema-level change, not a wire-format change.

Key Features

  • +Replaces proto2/proto3 binary choice with per-feature flags
  • +features.field_presence: EXPLICIT | IMPLICIT | LEGACY_REQUIRED
  • +features.enum_type: OPEN | CLOSED
  • +features.repeated_field_encoding: PACKED | EXPANDED
  • +features.utf8_validation: VERIFY | NONE
  • +features.message_encoding: LENGTH_PREFIXED | DELIMITED
  • +Feature scope: file, message, field – fine-grained override at any level
  • +Backward compatible with proto2 and proto3 semantics via feature combinations
  • +Migration tooling: protoc --edition_defaults, buf migrate

Limitations

  • !Tooling support still maturing – some language SDKs have partial Editions support as of 2024
  • !More complex schema files when overriding features at field level
  • !New concept to learn for teams accustomed to proto2/proto3
  • !LEGACY_REQUIRED is available but still considered harmful for the same reasons as proto2 required