syntax = "proto2" and syntax = "proto3" declarations with an incremental, feature-based versioning system. Instead of choosing between two syntaxes with fixed semantics, you configure individual behaviors through feature flags while keeping all messages interoperable.
What are editions?
Historically, protobuf supported two syntaxes with fixed, divergent semantics. Proto2 had explicit presence, closed enums, and extensions. Proto3 had implicit presence, open enums, and required a zero-valued first enum entry. Migrating between the two required wholesale file rewrites, and some behaviors from one syntax were unavailable in the other. Editions replace this model. A.proto file using editions begins with an edition declaration:
edition keyword implies syntax = "editions", and the syntax keyword is deprecated for edition-based files. Protoc rejects files that reference an edition it does not understand.
How editions differ from proto2/proto3
Withsyntax = "proto2" or syntax = "proto3", you got a fixed bundle of behaviors. Editions separate those behaviors into individual feature flags. Each feature has a default value for every edition, and you can override it at the file, message, or field level.
The following table summarizes the key semantic differences and their edition equivalents:
| Behavior | proto2 | proto3 | Edition 2023 default |
|---|---|---|---|
| Field presence | Explicit | Implicit (no optional) | EXPLICIT |
| Enum type | Closed | Open | OPEN |
| Repeated field encoding | Expanded | Packed | PACKED |
| String validation | Variable | Mandatory UTF-8 | MANDATORY |
| Message encoding | Length-prefixed or group | Length-prefixed | LENGTH_PREFIXED |
| JSON handling | Best-effort | Required valid mapping | ALLOW |
Edition 2023 features
Edition 2023 is the first published edition. It defines the following core features in theFeatures message within descriptor.proto:
features.field_presence
Controls whether the generated API tracks whether a singular field has been explicitly set.
EXPLICIT— the field has explicit presence.has_foo()andclear_foo()methods are generated. Default values are serialized when set.IMPLICIT— the field has no presence. The default value is not serialized. Nohas_foo()method is generated.LEGACY_REQUIRED— the field is wire-required. Used when migrating proto2requiredfields.
features.enum_type
Controls how unknown enum values are handled during parsing.
OPEN(default) — unknown values are stored in the field directly as integers.CLOSED— unknown values are stored in the unknown field set.
features.repeated_field_encoding
Controls the wire encoding for repeated scalar fields.
PACKED(default) — uses packed encoding for repeated numeric fields.EXPANDED— uses the older tag-per-element encoding.
features.message_encoding
Controls the wire type used for message-typed fields.
LENGTH_PREFIXED(default) — standard wire type 2 encoding.DELIMITED— group-style encoding (wire types 3/4), the replacement for proto2groupsyntax.
features.json_format
ALLOW(default) — the runtime must support JSON parsing and serialization. Proto-level checks ensure a well-defined JSON mapping.LEGACY_BEST_EFFORT— used for proto2 files that have undefined JSON mappings.
Feature inheritance
Features propagate from parent scopes to child scopes. A feature set at the file level applies to all messages, enums, and fields within the file, unless overridden at a narrower scope.The
optional keyword is accepted in editions as a syntactic shorthand for [features.field_presence = EXPLICIT]. It is not required; presence is controlled entirely by the feature flag.Migrating from proto2 and proto3
Any proto2 or proto3 file can be mechanically migrated to editions without semantic changes. The migration adds explicit feature flags where the file’s old behavior differed from the edition 2023 defaults.- proto2 original
- Editions equivalent
- proto3 original
- Editions equivalent
Edition lifecycle
Editions follow a defined lifecycle:Introduce
A new edition introduces updated defaults for features. Files that upgrade to the new edition and do not use deprecated features require only a no-op change.
Deprecate
Features that are no longer recommended are marked deprecated in a specific edition. Compilers emit warnings when deprecated features are used.
Edition enum in descriptor.proto tracks all known editions:
EDITION_PROTO2 and EDITION_PROTO3 are not valid values for the edition field in a .proto file. They exist so that feature definitions can specify defaults that match historical proto2 and proto3 behavior.
Projects can upgrade individual
.proto files to newer editions independently. Editions do not require all files in a project to be at the same edition level.