## 1. Project Overview & Quickstart (protocolbuffers/ruby)

# ruby

The Ruby Programming Language

### Repository Details
- **Repository:** [protocolbuffers/ruby](https://github.com/protocolbuffers/ruby)
- **GitHub Stars:** ⭐ 23,677
- **Primary Language:** Ruby

*Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.*

## 2. Official Technical Reference & Guides (protocolbuffers/protocolbuffers.github.io)

## File: README.md

# Protocol Buffers - Google's data interchange format

Copyright 2008 Google Inc.

https://protobuf.dev

## Overview

Protocol Buffers (a.k.a., protobuf) are Google's language-neutral,
platform-neutral, extensible mechanism for serializing structured data. This
site holds Protocol Buffers documentation.

## Protocol Buffers Repository

The Protocol Buffers GitHub repository is
https://github.com/protocolbuffers/protobuf.

## Developer Community

To be alerted to upcoming changes in Protocol Buffers and connect with protobuf
developers and users,
[join the Google Group](https://groups.google.com/g/protobuf).

---

## File: content/best-practices/_index.md

+++
title = "Proto Best Practices"
weight = 90
description = "An overview of best practices topics."
type = "docs"
no_list = "true"
+++

Best practices content for defining and using protos exists in the following
topics:

*   [Proto Best Practices](/best-practices/dos-donts)
*   [1-1-1 Rule](/best-practices/1-1-1)
*   [Avoid Cargo Culting](/best-practices/no-cargo-cults)

---

## File: content/best-practices/1-1-1.md

+++
title = "1-1-1 Best Practice"
weight = 105
linkTitle = "1-1-1 Best Practice"
description = "All proto definitions should have one top-level element and build target per file."
type = "docs"
aliases = "/programming-guides/1-1-1"
+++

The "1-1-1" best practice advocates structuring definitions with one top-level
entity (message, enum, or extension) per `.proto` file, corresponding to a
single `proto_library` build rule. This approach promotes small, modular proto
definitions. Key benefits include simplified refactoring, potentially improved
build times, and smaller binary sizes due to minimized transitive dependencies.

## Rationale

The 1-1-1 best practice is to keep every proto_library and .proto file as small
as is reasonable, with the ideal being:

*   One `proto_library` build rule
*   One source `.proto` file
*   One top-level entity (message, enum, or extension)

Having the fewest number of message, enum, extension, and services as you
reasonably can makes refactoring easier. Moving files when they're separated is
much easier than extracting messages from a file with other messages.

Following this practice can help build times and binary size by reducing the
size of your transitive dependencies in practice: when some code only needs to
use one enum, under a 1-1-1 design it can depend just on the .proto file that
defines that enum and avoid incidentally pulling in a large set of transitive
dependencies that may only be used by another message defined in the same file.

There are cases where the 1-1-1 ideal is not possible (circular dependencies),
not ideal (extremely conceptually coupled messages which have readability
benefits by being co-located), or where some of the downsides don't apply (when
a .proto file has no imports, then there are no technical concerns about the
size of transitive dependencies). As with any best practice, use good judgment
for when to diverge from the guideline.

One place that modularity of proto schema files is important is when creating
gRPC
definitions. The following set of proto files shows modular structure.

**student_id.proto**

```proto
edition = "2023";

package my.package;

message StudentId {
  string value = 1;
}
```

**full_name.proto**

```proto
edition = "2023";

package my.package;

message FullName {
  string family_name = 1;
  string given_name = 2;
}
```

**student.proto**

```proto
edition = "2023";

package my.package;

import "student_id.proto";
import "full_name.proto";

message Student {
  StudentId id = 1;
  FullName name = 2;
}
```

**create_student_request.proto**

```proto
edition = "2023";

package my.package;

import "full_name.proto";

message CreateStudentRequest {
  FullName name = 1;
}
```

**create_student_response.proto**

```proto
edition = "2023";

package my.package;

import "student.proto";

message CreateStudentResponse {
  Student student = 1;
}
```

**get_student_request.proto**

```proto
edition = "2023";

package my.package;

import "student_id.proto";

message GetStudentRequest {
  StudentId id = 1;
}
```

**get_student_response.proto**

```proto
edition = "2023";

package my.package;

import "student.proto";

message GetStudentResponse {
  Student student = 1;
}
```

**student_service.proto**

```proto
edition = "2023";

package my.package;

import "create_student_request.proto";
import "create_student_response.proto";
import "get_student_request.proto";
import "get_student_response.proto";

service StudentService {
  rpc CreateStudent(CreateStudentRequest) returns (CreateStudentResponse);
  rpc GetStudent(GetStudentRequest) returns (GetStudentResponse);
}
```

The service definition and each of the message definitions are each in their own
file, and you use includes to give access to the messages from other schema
files.

In this example, `Student`, `StudentId`, and `FullName` are domain types that
are reusable across requests and responses. The top-level request and response
protos are unique to each service+method.

If you later need to add a `middle_name` field to the `FullName` message, you
won't need to update every individual top-level message with that new field.
Likewise, if you need to update `Student` with more information, all the
requests and responses get the update. Further, `StudentId` might update to be a
multi-part ID.

Lastly, having even simple types like `StudentId` wrapped as a message means
that you have created a type that has semantics and consolidated documentation.
For something like `FullName` you'll need to be careful with where this PII gets
logged; this is another advantage of not repeating these fields in multiple
top-level messages. You can tag those fields in one place as sensitive
and exclude them from logging.

---

## File: content/best-practices/dos-donts.md

+++
title = "Proto Best Practices"
weight = 90
description = "Shares vetted best practices for authoring Protocol Buffers."
type = "docs"
aliases = "/programming-guides/dos-donts"
+++

Clients and servers are never updated at exactly the same time - even when you
try to update them at the same time. One or the
other may get rolled back. Don’t assume that you can make a breaking change and
it'll be okay because the client and server are in sync.

<a id="dont-re-use-a-tag-number"></a>

## **Don't** Re-use a Tag Number {#reuse-number}

Never re-use a tag number. It messes up deserialization. Even if you think no
one is using the field, don’t re-use a tag number. If the change was live ever,
there could be serialized versions of your proto in a log
somewhere. Or there could be old code in another server that will break.

<a id="do-reserve-tag-numbers-for-deleted-fields"></a>

## **Do** Reserve Tag Numbers for Deleted Fields {#reserve-tag-numbers}

When you delete a field that's no longer used, reserve its tag number so that no
one accidentally re-uses it in the future. Just `reserved 2, 3;` is enough. No
type required (lets you trim dependencies!). You can also reserve names to avoid
recycling now-deleted field names: `reserved foo, bar;`. See the docs on
[reserved field numbers](/programming-guides/editions#fieldreserved)
and
[reserved field names](/programming-guides/editions#reserved-field-names).

<a id="do-reserve-numbers-for-deleted-enum-values"></a>

## **Do** Reserve Numbers for Deleted Enum Values {#reserve-deleted-numbers}

When you delete an enum value that's no longer used, reserve its number so that
no one accidentally re-uses it in the future. Just `reserved 2, 3;` is enough.
You can also reserve names to avoid recycling now-deleted value names: `reserved
FOO, BAR;`. See also
[enum reserved values](/programming-guides/editions#reserved)

<a id="do-put-new-enum-aliases-last"></a>

## **Do** Put New Enum Aliases Last {#enum-aliases-last}

When you add a new enum alias, put the new name last to give
services time to pick it up.

To safely remove the original name (if it's being used for interchange, which it
[shouldn't](#text-format-interchange)), you must do the following:

*   Add the new name below the old name and deprecate the old one (serializers
    will continue to use the old name)

*   After every parser has the schema rolled out, swap the order of the two
    names (serializers will begin using the new name, parsers accept both)

*   After every serializer has that version of the schema, you can delete the
    deprecated name.

> **Note:** While in theory clients shouldn't be using the old name for
> interchange, it's still polite to follow the above steps, especially for
> widely-used enum names.

<a id="dont-change-the-type-of-a-field"></a>

## **Don't** Change the Type of a Field {#change-type}

Almost never change the type of a field; it'll mess up deserialization, same as
re-using a tag number. The
[protobuf docs](/programming-guides/editions#updating)
outline cases where old binary encoded data may successfully parse under a
different type (for example, going between `int32`, `uint32`, `int64` and
`bool`). However, changing a field's type can be difficult to roll out safely
even when the new schema can successfully parse old data.

<a id="dont-add-a-required-field"></a>

## **Don't** Add a Required Field {#add-required}

Never add a required field, instead add `// required` to document the API
contract. Required fields are considered harmful by so many they were
removed from proto3 completely. Make all fields
optional or repeated. You never know how long a message type is going to last
and whether someone will be forced to fill in your required field with an empty
string or zero in four years when it’s no longer logically required but the
proto still says it is.

For proto3 there are no `required` fields, so this advice does not apply.

<a id="dont-make-a-message-with-lots-of-fields"></a>

## **Don't** Make a Message with Lots of Fields {#lots-of-fields}

Don't make a message with “lots” (think: hundreds) of fields. In C++ every field
adds roughly 65 bits to the in-memory object size whether it’s populated or not
(8 bytes for the pointer and, if the field is declared as optional, another bit
in a bitfield that keeps track of whether the field is set). When your proto
grows too large, the generated code may not even compile (for example, in Java
there is a hard limit on the size of a method
).

<a id="do-include-an-unspecified-value-in-an-enum"></a>

## **Do** Include an Unspecified Value in an Enum {#unspecified-enum}

Enums should include a default `FOO_UNSPECIFIED` value as the first value in the
declaration.
When new values are added to an enum, old clients will see the field as unset
and the getter will return the default value or the first-declared value if no
default exists . For consistent behavior with [proto enums][proto-enums],
the first declared enum value should be a default `FOO_UNSPECIFIED` value and
should use tag 0. It may be tempting to declare this default as a semantically
meaningful value but as a general rule, do not, to aid in the evolution of your
protocol as new enum values are added over time. All enum values declared under
a container message are in the same C++ namespace, so prefix the unspecified
value with the enum’s name to avoid compilation errors. If you'll never need
cross-language constants, an `int32` will preserve unknown values and generates
less code. Note that [proto enums][proto-enums] require the first value to be
zero and can round-trip (deserialize, serialize) an unknown enum value.

[example-unspecified]: http://cs/#search/&q=file:proto%20%22_UNSPECIFIED%20=%200%22&type=cs
[proto-enums]: /programming-guides/editions#enum

<a id="dont-use-cc-macro-constants-for-enum-values"></a>

## **Don't** Use C/C++ Macro Constants for Enum Values {#macro-constants}

Using words that have already been defined by the C++ language - specifically,
in its headers such as `math.h`, may cause compilation errors if the `#include`
statement for one of those headers appears before the one for `.proto.h`. Avoid
using macro constants such as "`NULL`," "`NAN`," and "`DOMAIN`" as enum values.

<a id="dont-use-any"></a>

## **Do** prefer extensions over `Any` where possible {#avoid-any}

If you plan to use `Any`, first double-check if extensions can satisfy the use
case instead. If yes, prefer using extensions.

`Any` was created in Proto3 to replace extensions. However, it has a number of
design flaws. For most use cases, prefer using extensions.

The main use case that `Any` fulfills is the uncommon scenario in which
horizontal infrastructure needs to propagate completely arbitrary messages, and
declaring an extension for each possible legal types would be infeasible.

In the future, Protobuf may introduce a new concept that would satisfy the `Any`
use cases without the design flaws, but we have no concrete plans at this time.

<a id="do-use-well-known-types-and-common-types"></a>

## **Do** Use Well-Known Types and Common Types {#well-known-common}

Using the following common, shared types is strongly encouraged. E.g., do not
use `int32 timestamp_seconds_since_epoch` or `int64 timeout_millis` in your code
when a perfectly suitable common type already exists!

<a id="well-known-types"></a><a id="common-types"></a>

*   [`duration`](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/duration.proto)
    is a signed, fixed-length span of time (for example, 42s).
*   [`timestamp`](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/timestamp.proto)
    is a point in time independent of any time zone or calendar (for example,
    2017-01-15T01:30:15.01Z).
*   [`interval`](https://github.com/googleapis/googleapis/blob/master/google/type/interval.proto)
    is a time interval independent of time zone or calendar (for example,
    2017-01-15T01:30:15.01Z - 2017-01-16T02:30:15.01Z).
*   [`date`](https://github.com/googleapis/googleapis/blob/master/google/type/date.proto)
    is a whole calendar date (for example, 2005-09-19).
*   [`month`](https://github.com/googleapis/googleapis/blob/master/google/type/month.proto)
    is a month of year (for example, April).
*   [`dayofweek`](https://github.com/googleapis/googleapis/blob/master/google/type/dayofweek.proto)
    is a day of week (for example, Monday).
*   [`timeofday`](https://github.com/googleapis/googleapis/blob/master/google/type/timeofday.proto)
    is a time of day (for example, 10:42:23).
*   [`field_mask`](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/field_mask.proto)
    is a set of symbolic field paths (for example, f.b.d).
*   [`postal_address`](https://github.com/googleapis/googleapis/blob/master/google/type/postal_address.proto)
    is a postal address (for example, 1600 Amphitheatre Parkway Mountain View,
    CA 94043 USA).
*   [`money`](https://github.com/googleapis/googleapis/blob/master/google/type/money.proto)
    is an amount of money with its currency type (for example, 42 USD).
*   [`latlng`](https://github.com/googleapis/googleapis/blob/master/google/type/latlng.proto)
    is a latitude/longitude pair (for example, 37.386051 latitude and
    -122.083855 longitude).
*   [`color`](https://github.com/googleapis/googleapis/blob/master/google/type/color.proto)
    is a color in the RGBA color space.

**Note:** While the "Well-Known Types" (such as `Duration` and `Timestamp`) are
included with the Protocol Buffers compiler, the "Common Types" (such as `Date`
and `Money`) are not. To use the Common Types, you may need to add a dependency
on the [googleapis repository](https://github.com/googleapis/googleapis).

<a id="do-define-widely-used-message-types-in-separate-files"></a>

## **Do** Define Message Types in Separate Files {#separate-files}

When defining a proto schema, you should have a single message, enum, extension,
service, or group of cyclic dependencies per file. This makes refactoring
easier. Moving files when they're separated is much easier than extracting
messages from a file with other messages. Following this practice also helps to
keep the proto schema files smaller, which enhances maintainability.

If they will be widely used outside of your project, consider putting them in
their own file with no dependencies. Then it's easy for anyone to use those
types without introducing the transitive dependencies in your other proto files.

For more on this topic, see
[1-1-1 Rule](/best-practices/1-1-1).

<a id="dont-change-the-default-value-of-a-field"></a>

## **Don't** Change the Default Value of a Field {#change-default-value}

Almost never change the default value of a proto field. This causes version skew
between clients and servers. A client reading an unset value will see a
different result than a server reading the same unset value when their builds
straddle the proto change. Proto3 removed the ability to
set default values.

<a id="dont-go-from-repeated-to-scalar"></a>

## **Don't** Go from Repeated to Scalar {#repeated-to-scalar}

Although it won't cause crashes, you'll lose data. For JSON, a mismatch in
repeatedness will lose the whole *message*. For numeric proto3 fields and proto2
`packed` fields, going from repeated to scalar will lose all data in that
*field*. For non-numeric proto3 fields and un-annotated proto2 fields, going
from repeated to scalar will result in the last deserialized value "winning."

Going from scalar to repeated is OK in proto2 and in proto3 with
`[packed=false]` because for binary serialization the scalar value becomes a
one-element list.

<a id="do-follow-the-style-guide-for-generated-code"></a>

## **Do** Follow the Style Guide for Generated Code {#follow-style-guide}

Proto generated code is referred to in normal code. Ensure that options in
`.proto` file do not result in generation of code which violate the style guide.
For example:

*   `java_outer_classname` should follow
    https://google.github.io/styleguide/javaguide.html#s5.2.2-class-names

*   `java_package` and `java_alt_package` should follow
    https://google.github.io/styleguide/javaguide.html#s5.2.1-package-names

*   `package`, although used for Java when `java_package` is not present, always
    directly corresponds to C++ namespace and thus should follow
    https://google.github.io/styleguide/cppguide.html#Namespace_Names.
    If these style guides conflict, use `java_package` for Java.

*   `ruby_package` should be in the form `Foo::Bar::Baz` rather than
    `Foo.Bar.Baz`.

<a id="never-use-text-format-messages-for-interchange"></a>

## **Don't** use Text Format Messages for Interchange {#text-format-interchange}

Text-based serialization formats like text format and
JSON represent fields and enum values as strings. As a result, deserialization
of protocol buffers in these formats using old code will fail when a field or
enum value is renamed, or when a new field or enum value or extension is added.
Use binary serialization when possible for data interchange, and use text format
for human editing and debugging only.

If you use protos converted to JSON in your API or for storing data, you may not
be able to safely rename fields or enums at all.

<a id="never-rely-on-serialization-stability-across-builds"></a>

## **Never** Rely on Serialization Stability Across Builds {#serialization-stability}

The stability of proto serialization is not guaranteed across binaries or across
builds of the same binary. Do not rely on it when, for example, building cache
keys.

<a id="dont-generate-java-protos-in-the-same-java-package-as-other-code"></a>

## **Don't** Generate Java Protos in the Same Java Package as Other Code {#generate-java-protos}

Generate Java proto sources into a separate package from your hand-written Java
sources. The `package`, `java_package` and `java_alt_api_package` options
control
[where the generated Java sources are emitted](/reference/java/java-generated#package).
Make sure hand-written Java source code does not also live in that same package.
A common practice is to generate your protos into a `proto` subpackage in your
project that **only** contains those protos (that is, no hand-written source
code).

## **Do** Derive Java Package from the .proto Package (if overridden) {#derive-java-package}

Setting the `java_package` can introduce fully-qualified name collisions in
generated code that did not exist in the .proto semantics. For example, these
two files may create collisions in the generated code despite the
fully-qualified names not colliding in the original schema:

```proto
package x;
option java_package = "com.example.proto";
message Abc {}
```

```proto
package y;
option java_package = "com.example.proto";
message Abc {}
```

To avoid these problems, you should never set the same `java_package` in two
files that have different .proto packages set.

The best practice is to establish a local naming pattern where the package name
is derived from the .proto package. For example, a best practice file with
`package y` might consistently set `option java_package =
"com.example.proto.y"`.

This guidance also applies to any other language-specific options where package
overrides are possible.

## Avoid Using Language Keywords for Field Names {#avoid-keywords}

If the name of a message, field, enum, or enum value is a keyword in the
language that reads from/writes to that field, then protobuf may change the
field name, and may have different ways to access them than normal fields. For
example, see
[this warning about Python](/reference/python/python-generated#keyword-conflicts).

You should also avoid using keywords in your file paths, as this can also cause
problems.

## **Do** Use Different Messages For RPC APIs and Storage {#separate-types-for-storage}

Reusing the same messages for APIs and long-term storage may seem convenient,
reducing boilerplate and overhead of conversion between messages.

However, the needs of long-term storage and live RPC services tend to later
diverge. Using separate types even if they are largely duplicative initially
gives freedom to change your storage format without impacting your external
clients. Layer your code so that modules deal either with client protos, storage
protos, or translation.

There is a cost in maintaining the translation layer, but it quickly pays off
once you have clients and have to do your first storage changes.

## **Don't** Use Booleans for Something That Has Two States Now, but Might Have More Later {#bad-bools}

If you are using boolean for a field, make sure that the field is indeed
describing just two possible states (for all time, not just now and the near
future). The future flexibility of using an enum is often worth it, even if it
only has two values when it is first introduced.

```
message Photo {
  // Bad: True if it's a GIF.
  optional bool gif;

  // Good: File format of the referenced photo (for example, GIF, WebP, PNG).
  optional PhotoType type;
}
```

## **Do** Use java_outer_classname (before Edition 2024) {#java-outer-classname}

Every proto schema definition file should set option `java_outer_classname` to
the `.proto` file name converted to TitleCase with the '.' removed. For example,
the file `student_record_request.proto` should set:

```java
option java_outer_classname = "StudentRecordRequestProto";
```

The default behavior of Edition 2024 files has been aligned with this
recommendation, so no option should be set when using Edition 2024 or later.

---

## File: content/best-practices/no-cargo-cults.md

+++
title = "Avoid Cargo Culting"
weight = 90
description = "Avoid using features where they are not needed."
type = "docs"
+++

Do not
[cargo cult](https://en.wikipedia.org/wiki/Cargo_cult_programming)
settings in proto files. If you are creating a new proto file based on existing
schema definitions, don't apply option settings except for those that you
understand the need for.

## Best Practices Specific to Editions {#editions}

Avoid applying [editions features](/editions/features)
except when they're actually necessary. Features in `.proto` files signal the
use of either experimental future behaviors or deprecated past behaviors. Best
practices for the latest edition will always be the default. New proto schema
definition content should remain feature-free, except if you want to early-adopt
a feature for future behavior that's being rolled out.

Copying-forward feature settings without understanding why they are set can lead
to unexpected behaviors in your code.

---

## File: content/design-decisions/_index.md

+++
title = "Protobuf Team Design Decisions"
weight = 88
description = "Covers the design decisions the Protobuf team has made"
type = "docs"
no_list = "true"
linkTitle = "Design Decisions"
+++

This section contains some positions of the Protobuf team that inform our design
and implementation decisions.

These positions were taken after careful consideration and won't be overturned
on a whim, but are open to being revisited as we gain new information, and as
things develop in the broader ecosystem context.

*   [No Nullable Setters/Getters](/design-decisions/nullable-getters-setters)

---

## File: content/design-decisions/nullable-getters-setters.md

+++
title = "No Nullable Setters/Getters Support"
weight = 89
description = "Covers why Protobuf doesn't support nullable setters and getters"
type = "docs"
aliases = "/programming-guides/nullable-getters-setters/"
+++

We have heard feedback that some folks would like protobuf to support nullable
getters/setters in their null-friendly language of choice (particularly Kotlin,
C#, and Rust). While this does seem to be a helpful feature for folks using
those languages, the design choice has tradeoffs which have led to the Protobuf
team choosing not to implement them.

Explicit presence is not a concept that directly maps to the traditional notion
of nullability. It is subtle, but explicit presence philosophy is closer to "the
fields are not nullable, but you can detect if the field was explicitly assigned
a value or not. Normal access will see some default value if it is not assigned,
but you can check if the field was actively written to or not, when needed."

The biggest reason not to have nullable fields is the intended behavior of
default values specified in a `.proto` file. By design, calling a getter on an
unset field will return the default value of that field.

**Note:** C# does treat *message* fields as nullable. This inconsistency with
other languages stems from the lack of immutable messages, which makes it
impossible to create shared immutable default instances. Because message fields
can't have defaults, there's no functional problem with this.

As an example, consider this `.proto` file:

```proto
message Msg { Child child = 1; }
message Child { Grandchild grandchild = 1; }
message Grandchild { int32 foo = 1 [default = 72]; }
```

and corresponding Kotlin getters:

```kotlin
// With our API where getters are always non-nullable:
msg.child.grandchild.foo == 72

// With nullable submessages the ?. operator fails to get the default value:
msg?.child?.grandchild?.foo == null

// Or verbosely duplicating the default value at the usage site:
(msg?.child?.grandchild?.foo ?: 72)
```

and corresponding Rust getters:

```rust
// With our API:
msg.child().grandchild().foo()   // == 72

// Where every getter is an Option<T>, verbose and no default observed
msg.child().map(|c| c.grandchild()).map(|gc| gc.foo()) // == Option::None

// For the rare situations where code may want to observe both the presence and
// value of a field, the _opt() accessor which returns a custom Optional type
// can also be used here (the Optional type is similar to Option except can also
// be aware of the default value):
msg.child().grandchild().foo_opt() // Optional::Unset(72)
```

If a nullable getter existed, it would necessarily ignore the user-specified
defaults (to return null instead) which would lead to surprising and
inconsistent behavior. If users of nullable getters want to access the default
value of the field, they would have to write their own custom handling to use
the default if null is returned, which removes the supposed benefit of
cleaner/easier code with null getters.

Similarly, we do not provide nullable setters as the behavior would be
unintuitive. Performing a set and then get would not always give the same value
back, and calling a set would only sometimes affect the has-bit for the field.

Note that message-typed fields are always explicit presence fields (with
hazzers). Proto3 defaults to scalar fields having implicit presence (without
hazzers) unless they are explicitly marked `optional`, while Proto2 does not
support implicit presence. With
[Editions](/editions/features#field_presence), explicit
presence is the default behavior unless an implicit presence feature is used.
With the forward expectation that almost all fields will have explicit presence,
the ergonomic concerns that come with nullable getters are expected to be more
of a concern than they may have been for Proto3 users.

Due to these issues, nullable setters/getters would radically change the way
default values can be used. While we understand the possible utility, we have
decided it’s not worth the inconsistencies and difficulty it introduces.

---

## File: content/editions/_index.md

+++
title = "Protobuf Editions"
weight = 41
description = "Topics related to the Protobuf Editions functionality."
type = "docs"
+++

*   [Protobuf Editions Overview](/editions/overview)
*   [Feature Settings for Editions](/editions/features)
*   [Implementing Editions Support](/editions/implementation)

---

## File: content/editions/features.md

+++
title = "Feature Settings for Editions"
weight = 43
description = "Protobuf Editions features and how they affect protobuf behavior."
type = "docs"
+++

This topic provides an overview of the features that are included in the
released edition versions. Subsequent editions' features will be added to this
topic. We announce new editions in
[the News section](/news).

Before configuring feature settings in your new schema definition content, make
sure you understand why you are using them. Avoid
[cargo-culting](/best-practices/no-cargo-cults) with
features.

## Prototiller {#prototiller}

Prototiller is a command-line tool that updates proto schema configuration files
between syntax versions and editions. It hasn't been released yet, but is
referenced throughout this topic.

## Features {#features}

The following sections include all of the behaviors that are configurable using
features in editions. [Preserving proto2 or proto3 Behavior](#preserving) shows
how to override the default behaviors so that your proto definition files act
like proto2 or proto3 files. For more information on how editions and features
work together to set behavior, see
[Protobuf Editions Overview](/editions/overview).

 Feature settings apply at different levels:

**File-level:** These settings apply to all elements (messages, fields, enums,
and so on) that don't have an overriding setting.

**Non-nested:** Messages, enums, and services can override settings made at the
file level. They apply to everything within them (message fields, enum values)
that aren't overridden, but don't apply to other parallel messages and enums.

**Nested:** Oneofs, messages, and enums can override settings from the message
they're nested in.

**Lowest-level:** Fields, extensions, enum values, extension ranges, and methods
are the lowest level at which you can override settings.

Each of the following sections has a comment that states what scope the feature
can be applied to. The following sample shows a mock feature applied to each
scope:

```proto
edition = "2024";

// File-level scope definition
option features.bar = BAZ;

enum Foo {
  // Enum (non-nested scope) definition
  option features.bar = QUX;

  A = 1;
  B = 2;
}

message Corge {
  // Message (non-nested scope) definition
  option features.bar = QUUX;

  message Garply {
    // Message (nested scope) definition
    option features.bar = WALDO;
    string id = 1;
  }

  // Field (lowest-level scope) definition
  Foo A = 1 [features.bar = GRAULT];
}
```

In this example, the setting "`GRAULT"` in the lowest-level scope feature
definition overrides the non-nested-scope "`QUUX`" setting. And within the
Garply message, "`WALDO`" overrides "`QUUX`."

### `features.default_symbol_visibility` {#symbol-vis}

This feature enables setting the default visibility for messages and enums,
making them available or unavailable when imported by other protos. Use of this
feature will reduce dead symbols in order to create smaller binaries.

In addition to setting the defaults for the entire file, you can use the `local`
and `export` keywords to set per-field behavior. Read more about this at
[`export` / `local` Keywords](/editions/overview#export-local).

**Values available:**

*   `EXPORT_ALL`: This is the default prior to Edition 2024. All messages and
    enums are exported by default.
*   `EXPORT_TOP_LEVEL`: All top-level symbols default to export; nested default
    to local.
*   `LOCAL_ALL`: All symbols default to local.
*   `STRICT`: All symbols local by default. Nested types cannot be exported,
    except for a special-case caveat for `message { enum {} reserved 1 to max;
    }`.

**Applicable to the following scope:** file

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ------------------
2026           | `STRICT`
2024           | `EXPORT_TOP_LEVEL`
2023           | `EXPORT_ALL`
proto3         | `EXPORT_ALL`
proto2         | `EXPORT_ALL`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following sample shows how you can apply the feature to elements in your
proto schema definition files:

```proto
// foo.proto
edition = "2024";

// Symbol visibility defaults to EXPORT_TOP_LEVEL. Setting
// default_symbol_visibility overrides these defaults
option features.default_symbol_visibility = LOCAL_ALL;

// Top-level symbols are exported by default in Edition 2024; applying the local
// keyword overrides this
export message LocalMessage {
  int32 baz = 1;
  // Nested symbols are local by default in Edition 2024; applying the export
  // keyword overrides this
  enum ExportedNestedEnum {
    UNKNOWN_EXPORTED_NESTED_ENUM_VALUE = 0;
  }
}

// bar.proto
edition = "2024";

import "foo.proto";

message ImportedMessage {
  // The following is valid because the imported message explicitly overrides
  // the visibility setting in foo.proto
  LocalMessage bar = 1;

  // The following is not valid because default_symbol_visibility is set to
  // `LOCAL_ALL`
  // LocalMessage.ExportedNestedEnum qux = 2;
}
```

### `features.enforce_naming_style` {#enforce_naming}

Introduced in Edition 2024, this feature enables strict naming style enforcement
as defined in
[the style guide](/programming-guides/style) to ensure
protos are round-trippable by default with a feature value to opt-out to use
legacy naming style.

**Values available:**

*   `STYLE2026`: Enforces strict adherence to the 2026 style guide for naming to
    discourage common field-naming collisions.
*   `STYLE2024`: Enforces strict adherence to the style guide for naming.
*   `STYLE_LEGACY`: Applies the pre-Edition 2024 level of style guide
    enforcement.

**Applicable to the following scopes:** file, extension range, message, field,
oneof, enum, enum value, service, method

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------------
2026           | `STYLE2026`
2024           | `STYLE2024`
2023           | `STYLE_LEGACY`
proto3         | `STYLE_LEGACY`
proto2         | `STYLE_LEGACY`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows an Edition 2023 file:

Edition 2023 defaults to `STYLE_LEGACY`, so a non-conformant field name is fine:

```proto
edition = "2023";

message Foo {
  // A non-conforming field name is not a problem
  int64 bar_1 = 1;
}
```

Edition 2024 defaults to `STYLE2024`, so an override is needed to keep the
non-conformant field name:

```proto
edition = "2024";

// To keep the non-conformant field name, override the STYLE2024 setting
option features.enforce_naming_style = STYLE_LEGACY;

message Foo {
  int64 bar_1 = 1;
}
```

The following code sample shows an Edition 2024 file:

Edition 2024 defaults to `STYLE2024`, so collision avoidance is not enforced:

```proto
edition = "2024";

// Field name collision avoidance is not enforced
message Bar {
  int64 x = 1;
  int64 has_x = 2;
}
```

Edition 2026 defaults to `STYLE2026`, so an override is needed to keep the
colliding field names:

```proto
edition = "2024";

option features.enforce_naming_style = STYLE2024;

// To keep the colliding field names, override the STYLE2026 setting
message Bar {
  int64 x = 1;
  int64 has_x = 2;
}
```

### `features.enforce_proto_limits` {#enforce_proto_limits}

Introduced in Edition 2026, this feature enables structure size limit
enforcement in the Protobuf compiler when parsing `.proto` files to prevent
oversize message and enum definitions.

**Values available:**

*   `PROTO_LIMITS2026`: Enforces maximum limit checks:
    *   Up to 1500 fields per message
    *   Up to 1000 oneofs per message
    *   Up to 1200 fields per oneof
    *   Up to 1700 values per enum
*   `LEGACY_NO_EXPLICIT_LIMITS`: Opts out of limit checking.

**Applicable to the following scope:** message, oneof, enum

**Added in:** Edition 2026

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ---------------------------
2026           | `PROTO_LIMITS2026`
2024           | `LEGACY_NO_EXPLICIT_LIMITS`
2023           | `LEGACY_NO_EXPLICIT_LIMITS`
proto3         | `LEGACY_NO_EXPLICIT_LIMITS`
proto2         | `LEGACY_NO_EXPLICIT_LIMITS`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows an Edition 2024 file:

Edition 2024 defaults to `LEGACY_NO_EXPLICIT_LIMITS`, so a message with 1501
fields is fine:

```proto
edition = "2024";

message Foo {
  // A message with more than 1500 fields
  int64 one = 1;
  int64 two = 2;
  int64 three = 3;
  # ...
  int64 one_thousand_five_hundred = 1500;
  int64 one_thousand_five_hundred_and_one = 1501;
}
```

Edition 2026 defaults to `PROTO_LIMITS2026`, so an override is needed to keep
oversized messages:

```proto
edition = "2026";

// To keep the colliding field names, override the STYLE2026 setting
message Foo {
  // Must be set at the level of the oversized message, enum, or oneof as this
  // feature is not allowed at the file level.
  option features.enforce_proto_limits = PROTO_LIMITS2026;

  int64 one = 1;
  int64 two = 2;
  int64 three = 3;
  # ...
  int64 one_thousand_five_hundred = 1500;
  int64 one_thousand_five_hundred_and_one = 1501;
}
```

### `features.enum_type` {#enum_type}

This feature sets the behavior for how enum values that aren't contained within
the defined set are handled. See
[Enum Behavior](/programming-guides/enum) for more
information on open and closed enums.

This feature doesn't impact proto3 files, so this section doesn't have a before
and after of a proto3 file.

**Values available:**

*   `CLOSED:` Closed enums store enum values that are out of range in the
    unknown field set.
*   `OPEN:` Open enums parse out of range values into their fields directly.

**Applicable to the following scopes:** file, enum

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------
2026           | `OPEN`
2024           | `OPEN`
2023           | `OPEN`
proto3         | `OPEN`
proto2         | `CLOSED`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

enum Foo {
  A = 2;
  B = 4;
  C = 6;
}
```

After running [Prototiller](#prototiller), the equivalent code might look like
this:

```proto
edition = "2024";

enum Foo {
  // Setting the enum_type feature overrides the default OPEN enum
  option features.enum_type = CLOSED;
  A = 2;
  B = 4;
  C = 6;
}
```

### `features.field_presence` {#field_presence}

This feature sets the behavior for tracking field presence, or the notion of
whether a protobuf field has a value.

**Values available:**

*   `LEGACY_REQUIRED`: The field is required for parsing and serialization.
    Any explicitly-set value is
    serialized onto the wire (even if it is the same as the default value).
*   `EXPLICIT`: The field has explicit presence tracking. Any explicitly-set
    value is serialized onto the wire (even if it is the same as the default
    value). For singular primitive fields, `has_*` functions are generated for
    fields set to `EXPLICIT`.
*   `IMPLICIT`: The field has no presence tracking. The default value is not
    serialized onto the wire (even if it is explicitly set). `has_*` functions
    are not generated for fields set to `IMPLICIT`.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -----------
2026           | `EXPLICIT`
2024           | `EXPLICIT`
2023           | `EXPLICIT`
proto3         | `IMPLICIT`*
proto2         | `EXPLICIT`

\* proto3 is `IMPLICIT` unless the field has the `optional` label, in which case
it behaves like `EXPLICIT`. See
[Presence in Proto3 APIs](/programming-guides/field_presence#presence-proto3)
for more information.

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message Foo {
  required int32 x = 1;
  optional int32 y = 2;
  repeated int32 z = 3;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

message Foo {
  // Setting the field_presence feature retains the proto2 required behavior
  int32 x = 1 [features.field_presence = LEGACY_REQUIRED];
  int32 y = 2;
  repeated int32 z = 3;
}
```

The following shows a proto3 file:

```proto
syntax = "proto3";

message Bar {
  int32 x = 1;
  optional int32 y = 2;
  repeated int32 z = 3;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";
// Setting the file-level field_presence feature matches the proto3 implicit default
option features.field_presence = IMPLICIT;

message Bar {
  int32 x = 1;
  // Setting the field_presence here retains the explicit state that the proto3
  // field has because of the optional syntax
  int32 y = 2 [features.field_presence = EXPLICIT];
  repeated int32 z = 3;
}
```

Note that the `required` and `optional` labels no longer exist in Editions, as
the corresponding behavior is set explicitly with the `field_presence` feature.

### `features.json_format` {#json_format}

This feature sets the behavior for JSON parsing and serialization.

This feature doesn't impact proto3 files, so this section doesn't have a before
and after of a proto3 file. Editions behavior matches the behavior in proto3.

**Values available:**

*   `ALLOW`: A runtime must allow JSON parsing and serialization. Checks are
    applied at the proto level to make sure that there is a well-defined mapping
    to JSON.
*   `LEGACY_BEST_EFFORT`: A runtime does the best it can to parse and serialize
    JSON. Certain protos are allowed that can result in unspecified behavior at
    runtime (such as many:1 or 1:many mappings).

**Applicable to the following scopes:** file, message, enum

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------------------
2026           | `ALLOW`
2024           | `ALLOW`
2023           | `ALLOW`
proto3         | `ALLOW`
proto2         | `LEGACY_BEST_EFFORT`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message Foo {
  // Warning only
  string bar = 1;
  string bar_ = 2;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";
option features.json_format = LEGACY_BEST_EFFORT;

message Foo {
  string bar = 1;
  string bar_ = 2;
}
```

### `features.message_encoding` {#message_encoding}

This feature sets the behavior for encoding fields when serializing.

This feature doesn't impact proto3 files, so this section doesn't have a before
and after of a proto3 file.

Depending on the language, fields that are "group-like" may have some unexpected
capitalization in generated code and in text-format, in order to provide
backwards compatibility with proto2. Message fields are "group-like" if all of
the following conditions are met:

*   Has `DELIMITED` message encoding specified
*   Message type is defined in the same scope as the field
*   Field name is exactly the lowercased type name

**Values available:**

*   `LENGTH_PREFIXED`: Fields are encoded using the LEN wire type described in
    [Message Structure](/programming-guides/encoding#structure).
*   `DELIMITED`: Message-typed fields are encoded as
    [groups](/programming-guides/proto2#groups).

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -----------------
2026           | `LENGTH_PREFIXED`
2024           | `LENGTH_PREFIXED`
2023           | `LENGTH_PREFIXED`
proto3         | `LENGTH_PREFIXED`
proto2         | `LENGTH_PREFIXED`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message Foo {
  group Bar = 1 {
    optional int32 x = 1;
    repeated int32 y = 2;
  }
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

message Foo {
  message Bar {
    int32 x = 1;
    repeated int32 y = 2;
  }
  Bar bar = 1 [features.message_encoding = DELIMITED];
}
```

### `features.repeated_field_encoding` {#repeated_field_encoding}

This feature is what the proto2/proto3
[`packed` option](/programming-guides/encoding#packed)
for `repeated` fields has been migrated to in Editions.

**Values available:**

*   `PACKED`: `Repeated` fields of a primitive type are encoded as a single LEN
    record that contains each element concatenated.
*   `EXPANDED`: `Repeated` fields are each encoded with the field number for
    each value.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ----------
2026           | `PACKED`
2024           | `PACKED`
2023           | `PACKED`
proto3         | `PACKED`
proto2         | `EXPANDED`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message Foo {
  repeated int32 bar = 6 [packed=true];
  repeated int32 baz = 7;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";
option features.repeated_field_encoding = EXPANDED;

message Foo {
  repeated int32 bar = 6 [features.repeated_field_encoding=PACKED];
  repeated int32 baz = 7;
}
```

The following shows a proto3 file:

```proto
syntax = "proto3";

message Foo {
  repeated int32 bar = 6;
  repeated int32 baz = 7 [packed=false];
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

message Foo {
  repeated int32 bar = 6;
  repeated int32 baz = 7 [features.repeated_field_encoding=EXPANDED];
}
```

### `features.utf8_validation` {#utf8_validation}

This feature sets how strings are validated. It applies to all languages except
where there's a language-specific `utf8_validation` feature that overrides it.
See [`features.(pb.java).utf8_validation`](#java-utf8_validation) for the
Java-language-specific feature.

**Important:** In Java, `map<string, ...>` fields always undergo UTF-8
validation, even when `utf8_validation = NONE` is configured. Because Java keys
will be represented as `java.lang.String` objects, disabling validation would
force the runtime to convert invalid byte sequences *lossily* using replacement
characters. This lossy conversion can result in unexpected behavior, such as key
collisions. As a wire-compatible workaround, consider using a `repeated` field
with a custom "map entry" message instead.

This feature doesn't impact proto3 files, so this section doesn't have a before
and after of a proto3 file.

**Values available:**

*   `VERIFY`: The runtime should verify UTF-8. This is the default proto3
    behavior.
*   `NONE`: The field behaves like an unverified `bytes` field on the wire.
    Parsers may handle this type of field in an unpredictable way, such as
    replacing invalid characters. This is the default proto2 behavior.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------
2026           | `VERIFY`
2024           | `VERIFY`
2023           | `VERIFY`
proto3         | `VERIFY`
proto2         | `NONE`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message MyMessage {
  string foo = 1;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

message MyMessage {
  string foo = 1 [features.utf8_validation = NONE];
}
```

## Language-specific Features {#lang-specific}

Some features apply to specific languages, and not to the same protos in other
languages. Using these features requires you to import the corresponding
*_features.proto file from the language's runtime. The examples in the following
sections show these imports.

### `features.(pb.go).api_level` {#go-api_level}

**Languages:** Go

The `api_level` feature enables you to select which API version the Go protobuf
plugin should generate code for. The Opaque API is the latest version of the
Protocol Buffers implementation for the Go programming language. The previous
version is now called Open Struct API. See the
[Go Protobuf: Releasing the Opaque API](https://go.dev/blog/protobuf-opaque)
blog post for an introduction.

**Values available:**

*   `API_OPEN`: The Open Struct API generates struct types that are open to
    direct access.
*   `API_HYBRID`: Hybrid is a step between Open and Opaque: The Hybrid API also
    includes accessor methods (so you can update your code), but still exports
    the struct fields as before. There is no performance difference; this API
    level only helps with the migration.
*   `API_OPAQUE`: With the Opaque API, the struct fields are hidden and can no
    longer be directly accessed. Instead, the new accessor methods allow for
    getting, setting, or clearing a field.

**Applicable to the following scopes:** message, file

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ------------
2026           | `API_OPAQUE`
2024           | `API_OPAQUE`
2023           | `API_OPEN`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

You can set the `api_level` feature starting in edition 2023:

```proto
edition = "2023";

import option "third_party/golang/protobuf/v2/src/google/protobuf/go_features.proto";

// Remove this line after migrating the code to the Opaque API.
option features.(pb.go).api_level = API_HYBRID;
```

NOTE: `import option` feature was added to reduce any impact that these added
imports may have on runtime reflection behaviors or other descriptor processing.
Always use `import option` if you are importing another `.proto` file only to
use it in options.

See also:
[Opaque API: Migration](/reference/go/opaque-migration)

### `features.(pb.cpp).enum_name_uses_string_view` {#enum-name-string-view}

**Languages:** C++

Before Edition 2024, all generated enum types provide the following function to
obtain the label out of an enum value, which has some overhead to construct the
`std::string` instances at runtime:

```cpp
const std::string& Foo_Name(int);
```

The default feature value in Edition 2024 changes this signature to return
`absl::string_view` to allow for better storage decoupling and potential
memory/CPU savings. If you aren't ready to migrate, yet, you can override this
to set it back to its previous behavior. See
[string_view return type](/support/migration#string_view-return-type)
in the migration guide for more on this topic.

**Values available:**

*   `true`: The enum uses `string_view` for its values.
*   `false`: The enum uses `std::string` for its values.

**Applicable to the following scopes:** file, enum

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -------
2026           | `true`
2024           | `true`
2023           | `false`
proto3         | `false`
proto2         | `false`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

### `features.(pb.cpp).repeated_type` {#cpp-repeated_type}

**Languages:** C++

When set to `PROXY`, repeated field accessors return proxy types
(`RepeatedFieldProxy`) instead of pointers or references to `RepeatedField` or
`RepeatedPtrField`. Proxies allow for a more efficient in-memory representation
of repeated fields in messages.

Importing this feature requires `import option "google/protobuf/cpp_features.proto";`

**Values available:**

*   `PROXY`: Accessors return proxy types.
*   `LEGACY`: Accessors return legacy `RepeatedField` or `RepeatedPtrField`
    pointers/references.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2026

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------
2026           | `LEGACY`
2024           | `LEGACY`
2023           | `LEGACY`
proto3         | `LEGACY`
proto2         | `LEGACY`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

### `features.(pb.java).large_enum` {#java-large_enum}

**Languages:** Java

This language-specific feature enables you to adopt new functionality that
handles large enums in Java without causing compiler errors. Note that this
feature replicates enum-like behavior but has some notable differences. For
example, switch statements are not supported.

**Values available:**

*   `true`: Java enums will use the new functionality.
*   `false`: Java enums will continue to use Java enums.

**Applicable to the following scopes:** file, enum

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -------
2026           | `false`
2024           | `false`
2023           | `false`
proto3         | `false`
proto2         | `false`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

### `features.(pb.cpp/pb.java).legacy_closed_enum` {#legacy_closed_enum}

**Languages:** C++, Java

This feature determines whether a field with an open enum type should be behave
as if it was a closed enum. This allows editions to reproduce
[non-conformant behavior](/programming-guides/enum) in
Java and C++ from proto2 and proto3.

This feature doesn't impact proto3 files, and so this section doesn't have a
before and after of a proto3 file.

**Values available:**

*   `true`: Treats the enum as closed regardless of [`enum_type`](#enum_type).
*   `false`: Respect whatever is set in the `enum_type`.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -------
2026           | `false`
2024           | `false`
2023           | `false`
proto3         | `false`
proto2         | `true`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

import "myproject/proto3file.proto";

message Msg {
  myproject.proto3file.Proto3Enum name = 1;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

import "myproject/proto3file.proto";

import option "third_party/protobuf/cpp_features.proto";
import option "third_party/java/protobuf/java_features.proto";

message Msg {
  myproject.proto3file.Proto3Enum name = 1 [
    features.(pb.cpp).legacy_closed_enum = true,
    features.(pb.java).legacy_closed_enum = true
  ];
}
```

### `features.(pb.java).nest_in_file_class` {#java-nest_in_file}

**Languages:** Java

This feature controls whether the Java generator will nest the generated class
in the Java generated file class. Setting this option to `NO` is the equivalent
of setting `java_multiple_files = true` in proto2/proto3/edition 2023.

The default outer classname is also updated to always be the camel-cased .proto
filename suffixed with Proto by default (for example, `foo/bar_baz.proto`
becomes `BarBazProto`). You can still override this using the
`java_outer_classname` file option and replace the pre-Edition 2024 default of
`BarBaz` or `BarBazOuterClass` depending on the presence of conflicts.

**Values available:**

*   `NO`: Do not nest the generated class in the file class.
*   `YES`: Nest the generated class in the file class.
*   Legacy: Nesting behavior controlled by `java_multiple_files` option (see
    go/java-proto-names#immutable-api-message-names).

**Applicable to the following scopes:** message, enum, service

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | -------
2026           | `NO`
2024           | `NO`
2023           | Legacy
proto3         | Legacy
proto2         | Legacy

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

### `features.(pb.cpp).string_type` {#string_type}

**Languages:** C++

This feature determines how generated code should treat string fields. This
replaces the `ctype` option from proto2 and proto3, and offers a new
`string_type` feature. In Edition 2023, you can specify either `ctype` or
`string_type` on a field, but not both. In Edition 2024, the `ctype` option is
removed.

**Values available:**

*   `VIEW`: Generates `string_view` accessors for the field.
*   `CORD`: Generates `Cord` accessors for the field. Not supported on extension
    fields.
*   `STRING`: Generates `string` accessors for the field.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | --------
2026           | `VIEW`
2024           | `VIEW`
2023           | `STRING`
proto3         | `STRING`
proto2         | `STRING`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

message Foo {
  optional string bar = 6;
  optional string baz = 7 [ctype = CORD];
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

import option "third_party/protobuf/cpp_features.proto";

message Foo {
  string bar = 6 [features.(pb.cpp).string_type = STRING];
  string baz = 7 [features.(pb.cpp).string_type = CORD];
}
```

The following shows a proto3 file:

```proto
syntax = "proto3"

message Foo {
  string bar = 6;
  string baz = 7 [ctype = CORD];
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

import option "third_party/protobuf/cpp_features.proto";

message Foo {
  string bar = 6 [features.(pb.cpp).string_type = STRING];
  string baz = 7 [features.(pb.cpp).string_type = CORD];
}
```

### `features.(pb.java).utf8_validation` {#java-utf8_validation}

**Languages:** Java

This language-specific feature enables you to override the file-level settings
at the field level for Java only.

This feature doesn't impact proto3 files, and so this section doesn't have a
before and after of a proto3 file.

**Values available:**

*   `DEFAULT`: The behavior matches that set by
    [`features.utf8_validation`](#utf8_validation).
*   `VERIFY`: Overrides the file-level `features.utf8_validation` setting to
    force it to `VERIFY` for Java only.

**Applicable to the following scopes:** file, field

**Added in:** Edition 2023

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ---------
2026           | `DEFAULT`
2024           | `DEFAULT`
2023           | `DEFAULT`
proto3         | `DEFAULT`
proto2         | `DEFAULT`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

The following code sample shows a proto2 file:

```proto
syntax = "proto2";

option java_string_check_utf8=true;

message MyMessage {
  string foo = 1;
  string bar = 2;
}
```

After running Prototiller, the equivalent code might look like this:

```proto
edition = "2024";

import "google/protobuf/java_features.proto";

option features.utf8_validation = NONE;
option features.(pb.java).utf8_validation = VERIFY;
message MyMessage {
  string foo = 1;
  string bar = 2;
}
```

### `features.(pb.go).strip_enum_prefix` {#go-strip_enum_prefix}

**Languages:** Go

Enum values are not scoped by their containing enum name, so
[prefixing every value with the enum name is recommended](/programming-guides/style#enums):

```proto
edition = "2024";

enum Strip {
  STRIP_ZERO = 0;
  STRIP_ONE = 1;
}
```

However, the generated Go code will now contain two prefixes!

```go
type Strip int32

const (
    Strip_STRIP_ZERO Strip = 0
    Strip_STRIP_ONE  Strip = 1
)
```

The language-specific `strip_enum_prefix` feature determines whether the Go code
generator strips the repetitive prefix or not.

**Values available:**

*   `STRIP_ENUM_PREFIX_KEEP`: Keep the name as-is, even if repetitive.
*   `STRIP_ENUM_PREFIX_GENERATE_BOTH`: Generate both, a full name and a stripped
    name (to help with migrating your Go code).
*   `STRIP_ENUM_PREFIX_STRIP`: Strip the enum name prefix from enum value names.

**Applicable to the following scopes:** file, enum, enum value

**Added in:** Edition 2024

**Default behavior per syntax/edition:**

Syntax/edition | Default
-------------- | ------------------------
2026           | `STRIP_ENUM_PREFIX_KEEP`
2024           | `STRIP_ENUM_PREFIX_KEEP`

**Note:** Feature settings on different schema elements
[have different scopes](#cascading).

You can set the `strip_enum_prefix` feature in edition 2024 (or newer) .proto
files:

```proto
edition = "2024";

import "google/protobuf/go_features.proto";

option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_STRIP;

enum Strip {
  STRIP_ZERO = 0;
  STRIP_ONE = 1;
}
```

The generated Go code will now strip the `STRIP` prefix:

```go
type Strip int32

const (
    Strip_ZERO Strip = 0
    Strip_ONE  Strip = 1
)
```

## Preserving proto2 or proto3 Behavior in Edition 2023 {#preserving}

You may want to move to the editions format but not deal with updates to the way
that generated code behaves yet. This section shows the changes that the
Prototiller tool makes to your .proto files to make edition-2023-based protos
behave like a proto2 or proto3 file.

After these changes are made at the file level, you get the proto2 or proto3
defaults. You can override at lower levels (message level, field level) to
consider additional behavior differences (such as
[required, proto3 optional](#caveats)) or if you want your definition to only be
*mostly* like proto2 or proto3.

We recommend using Prototiller unless you have a specific reason not to. To
manually apply all of these instead of using Prototiller, add the content from
the following sections to the top of your .proto file.

### Proto2 Behavior {#proto2-behavior}

The following shows the settings to replicate proto2 behavior with Edition 2023.

```proto
edition = "2023";

import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";

option features.field_presence = EXPLICIT;
option features.enum_type = CLOSED;
option features.repeated_field_encoding = EXPANDED;
option features.json_format = LEGACY_BEST_EFFORT;
option features.utf8_validation = NONE;
option features.(pb.cpp).legacy_closed_enum = true;
option features.(pb.java).legacy_closed_enum = true;
```

### Proto3 Behavior {#proto3-behavior}

The following shows the settings to replicate proto3 behavior with Edition 2023.

```proto
edition = "2023";

import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";

option features.field_presence = IMPLICIT;
option features.enum_type = OPEN;
// `packed=false` needs to be transformed to field-level repeated_field_encoding
// features in Editions syntax
option features.json_format = ALLOW;
option features.utf8_validation = VERIFY;
option features.(pb.cpp).legacy_closed_enum = false;
option features.(pb.java).legacy_closed_enum = false;
```

### Edition 2023 to 2024 {#2023-2024}

The following shows the settings to replicate Edition 2023 behavior with
Edition 2024.

```proto
// foo/bar_baz.proto
edition = "2024";

import option "third_party/protobuf/cpp_features.proto";
import option "third_party/java/protobuf/java_features.proto";
import option "third_party/golang/protobuf/v2/src/google/protobuf/go_features.proto";

// If previously relying on edition 2023 default java_outer_classname.
option java_outer_classname = "BarBaz" // or BarBazOuterClass

option features.(pb.cpp).string_type = STRING;
option features.enforce_naming_style = STYLE_LEGACY;
option features.default_symbol_visibility = EXPORT_ALL;
option features.(pb.cpp).enum_name_uses_string_view = false;
option features.(pb.go).api_level = API_OPEN;

message MyMessage {
  option features.(pb.java).nest_in_file_class = YES;
}
```

### Edition 2024 to 2026 {#2024-2026}

The following shows the settings to replicate Edition 2024 behavior with
Edition 2026.

```proto
edition = "2026";

option features.enforce_naming_style = STYLE2024;
option features.default_symbol_visibility = EXPORT_TOP_LEVEL;
```

### Caveats and Exceptions {#caveats}

This section shows the changes that you'll need to make manually if you choose
not to use Prototiller.

Setting the file-level defaults shown in the previous section sets the default
behaviors in most cases, but there are a few exceptions.

**Edition 2023 and later**

*   `optional`: Remove all instances of the `optional` label and change the
    [`features.field_presence`](#field_presence) to `EXPLICIT` if the file
    default is `IMPLICIT`.
*   `required`: Remove all instances of the `required` label and add the
    [`features.field_presence=LEGACY_REQUIRED`](#field_presence) option at the
    field level.
*   `groups`: Unwrap the `groups` into a separate message and add the
    `features.message_encoding = DELIMITED` option at the field level. See
    [`features.message_encoding`](#message_encoding) for more on this.
*   `java_string_check_utf8`: Remove this file option and replace it with the
    [`features.(pb.java).utf8_validation`](#java-utf8_validation). You'll need
    to import Java features, as covered in
    [Language-specific Features](#lang-specific).
*   `packed`: For proto2 files converted to editions format, remove the `packed`
    field option and add `[features.repeated_field_encoding=PACKED]` at the
    field level when you don't want the `EXPANDED` behavior that you set in
    [Proto2 Behavior](#proto2-behavior). For proto3 files converted to editions
    format, add `[features.repeated_field_encoding=EXPANDED]` at the field level
    when you don't want the default proto3 behavior.

**Edition 2024 and later**

*   (C++) `ctype`: Remove all instances of the `ctype` option and set the
    [`features.(pb.cpp).string_type`](#string_type) value.
*   (C++ and Go) `weak`: Remove weak
    imports. Use
    [`import option`](/editions/overview#import-option)
    instead.
*   (Java) `java_multiple_files`: Remove `java_multiple_files` and use
    [`features.(pb.java).nest_in_file_class`](#java-nest_in_file) instead.

**Edition 2026 and later**

*   (C++) Remove Language-specific options (`cc_api_version`,
    `cc_utf8_verification`, `cc_enable_arenas`).
*   Set `features.enforce_proto_limits` to `LEGACY_NO_EXPLICIT_LIMITS` within
    1.  Messages that have either more than 1500 fields or more than 1000 oneofs
    1.  Oneofs that have more than 1200 fields
    1.  Enums that have more than 1700 values

---

## File: content/editions/implementation.md

+++
title = "Implementing Editions Support"
weight = 43
description = "Instructions for implementing Editions support in runtimes and plugins."
type = "docs"
+++

This topic explains how to implement editions in new runtimes and generators.

## Overview {#overview}

### Edition 2023 {#edition-2023}

The first edition released is Edition 2023, which is designed to unify proto2
and proto3 syntax. The features we’ve added to cover the difference in behaviors
are detailed in
[Feature Settings for Editions](/editions/features).

### Feature Definition {#feature-definition}

In addition to *supporting* editions and the global features we've defined, you
may want to define your own features to leverage the infrastructure. This will
allow you to define arbitrary features that can be used by your generators and
runtimes to gate new behaviors. The first step is to claim an extension number
for the `FeatureSet` message in descriptor.proto above 9999. You can send a
pull-request to us in GitHub, and it will be included in our next release (see,
for example, [#15439](https://github.com/protocolbuffers/protobuf/pull/15439)).

Once you have your extension number, you can create your features proto (similar
to
[cpp_features.proto](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/cpp_features.proto)).
These will typically look something like:

```proto
edition = "2023";

package foo;

import "google/protobuf/descriptor.proto";

extend google.protobuf.FeatureSet {
  MyFeatures features = <extension #>;
}

message MyFeatures {
  enum FeatureValue {
    FEATURE_VALUE_UNKNOWN = 0;
    VALUE1 = 1;
    VALUE2 = 2;
  }

  FeatureValue feature_value = 1 [
    targets = TARGET_TYPE_FIELD,
    targets = TARGET_TYPE_FILE,
    feature_support = {
      edition_introduced: EDITION_2023,
      edition_deprecated: EDITION_2024,
      deprecation_warning: "Feature will be removed in 2025",
      edition_removed: EDITION_2025,
    },
    edition_defaults = { edition: EDITION_LEGACY, value: "VALUE1" },
    edition_defaults = { edition: EDITION_2024, value: "VALUE2" }
  ];
}
```

Here we’ve defined a new enum feature `foo.feature_value` (currently only
boolean and enum types are supported). In addition to defining the values it can
take, you also need to specify how it can be used:

*   **Targets** - specifies the type of proto descriptors this feature can be
    attached to. This controls where users can explicitly specify the feature.
    Every type must be explicitly listed.
*   **Feature support** - specifies the lifetime of this feature relative to
    edition. You must specify the edition it was introduced in, and it will not
    be allowed before then. You can optionally deprecate or remove the feature
    in later editions.
*   **Edition defaults** - specifies any changes to the default value of the
    feature. This must cover every supported edition, but you can leave out any
    edition where the default didn’t change. Note that `EDITION_PROTO2` and
    `EDITION_PROTO3` can be specified here to provide defaults for the “legacy”
    editions (see [Legacy Editions](#legacy_editions)).

#### What is a Feature? {#what-feature}

Features are designed to provide a mechanism for ratcheting down bad behavior
over time, on edition boundaries. While the timeline for actually removing a
feature may be years (or decades) in the future, the desired goal of any feature
should be eventual removal. When a bad behavior is identified, you can introduce
a new feature that guards the fix. In the next edition (or possibly later), you
would flip the default value while still allowing users to retain their old
behavior when upgrading. At some point in the future you would mark the feature
deprecated, which would trigger a custom warning to any users overriding it. In
a later edition, you would then mark it removed, preventing users from
overriding it anymore (but the default value would still apply). Until support
for that last edition is dropped in a breaking release, the feature will remain
usable for protos stuck on older editions, giving them time to migrate.

Flags that control optional behaviors you have no intention of removing are
better implemented as
[custom options](/programming-guides/proto2/#customoptions).
This is related to the reason we’ve restricted features to be either boolean or
enum types. Any behavior controlled by a (relatively) unbounded number of values
probably isn’t a good fit for the editions framework, since it’s not realistic
to eventually turn down that many different behaviors.

One caveat to this is behaviors related to wire boundaries. Using
language-specific features to control serialization or parsing behavior can be
dangerous, since any other language could be on the other side. Wire-format
changes should always be controlled by global features in `descriptor.proto`,
which can be respected by every runtime uniformly.

### Generators {#generators}

Generators written in C++ get a lot for free, because they use the C++ runtime.
They don’t need to handle [Feature Resolution](#feature_resolution) themselves,
and if they need any feature extensions they can register them in
`GetFeatureExtensions` in their CodeGenerator. They can generally use
`GetResolvedSourceFeatures` for access to resolved features for a descriptor in
codegen and `GetUnresolvedSourceFeatures` for access to their own unresolved
features.

Plugins written in the same language as the runtime they generate code for may
need some custom bootstrapping for their feature definitions.

#### Explicit Support {#explicit-support}

Generators must specify exactly which editions they support. This allows you to
safely add support for an edition after it’s been released, on your own
schedule. Protoc will reject any editions protos sent to generators that don’t
include `FEATURE_SUPPORTS_EDITIONS` in the `supported_features` field of their
`CodeGeneratorResponse`. Additionally, we have `minimum_edition` and
`maximum_edition` fields for specifying your precise support window. Once you’ve
defined all of the code and feature changes for a new edition, you can bump
`maximum_edition` to advertise this support.

#### Codegen Tests {#codegen-tests}

We have a set of codegen tests that can be used to lock down that Edition 2023
produces no unexpected functional changes. These have been very useful in
languages like C++ and Java, where a significant amount of the functionality is
in gencode. On the other hand, in languages like Python, where the gencode is
basically just a collection of serialized descriptors, these are not quite as
useful.

This infrastructure is not reusable yet, but is planned to be in a future
release. At that point you will be able to use them to verify that migrating to
editions doesn’t have any unexpected codegen changes.

### Runtimes {#runtimes}

Runtimes without reflection or dynamic messages should not need to do anything
to implement Editions. All of that logic should be handled by the code
generator.

Languages *with* reflection but *without* dynamic messages need resolved
features, but may optionally choose to handle it in their generator only. This
can be done by passing both resolved and unresolved feature sets to the runtime
during codegen. This avoids re-implementing
[Feature Resolution](#feature_resolution) in the runtime with the main downside
being efficiency, since it will create a unique feature set for every
descriptor.

Languages with dynamic messages must fully implement Editions, because they need
to be able to build descriptors at runtime.

#### Syntax Reflection {#syntax_reflection}

The first step in implementing Editions in a runtime with reflection is to
remove all direct checks of the `syntax` keyword. All of these should be moved
to finer-grained feature helpers, which can continue to use `syntax` if
necessary.

The following feature helpers should be implemented on descriptors, with
language-appropriate naming:

*   `FieldDescriptor::has_presence` - Whether or not a field has explicit
    presence
    *   Repeated fields *never* have presence
    *   Message, extension, and oneof fields *always* have explicit presence
    *   Everything else has presence iff `field_presence` is not `IMPLICIT`
*   `FieldDescriptor::is_required` - Whether or not a field is required
*   `FieldDescriptor::requires_utf8_validation` - Whether or not a field should
    be checked for utf8 validity
*   `FieldDescriptor::is_packed` - Whether or not a repeated field has packed
    encoding
*   `FieldDescriptor::is_delimited` - Whether or not a message field has
    delimited encoding
*   `EnumDescriptor::is_closed` - Whether or not a field is closed

{{% alert title="Note" color="note" %}} In
most languages, the message encoding feature is still currently signaled by
`TYPE_GROUP` and required fields still have `LABEL_REQUIRED` set. This is not
ideal, and was done to make downstream migrations easier. Eventually, these
should be migrated to the appropriate helpers and
`TYPE_MESSAGE/LABEL_OPTIONAL`.{{% /alert %}}

Downstream users should migrate to these new helpers instead of using syntax
directly. The following class of existing descriptor APIs should ideally be
deprecated and eventually removed, since they leak syntax information:

*   `FileDescriptor` syntax
*   Proto3 optional APIs
    *   `FieldDescriptor::has_optional_keyword`
    *   `OneofDescriptor::is_synthetic`
    *   `Descriptor::*real_oneof*` - should be renamed to just “oneof” and the
        existing “oneof” helpers should be removed, since they leak information
        about synthetic oneofs (which don’t exist in editions).
*   Group type
    *   The `TYPE_GROUP` enum value should be removed, replaced with the
        `is_delimited` helper.
*   Required label
    *   The `LABEL_REQUIRED` enum value should be removed, replaced with the
        `is_required` helper.

There are many classes of user code where these checks exist but *aren’t*
hostile to editions. For example, code that needs to handle proto3 `optional`
specially because of its synthetic oneof implementation won’t be hostile to
editions as long as the polarity is something like `syntax == "proto3"` (rather
than checking `syntax != "proto2"`).

If it’s not possible to remove these APIs entirely, they should be deprecated
and discouraged.

#### Feature Visibility {#visibility}

As discussed in
[editions-feature-visibility](https://github.com/protocolbuffers/protobuf/blob/main/docs/design/editions/editions-feature-visibility.md),
feature protos should remain an internal detail of any Protobuf implementation.
The *behaviors* they control should be exposed via descriptor methods, but the
protos themselves should not. Notably, this means that any options that are
exposed to the users need to have their `features` fields stripped out.

The one case where we permit features to leak out is when serializing
descriptors. The resulting descriptor protos should be a faithful representation
of the original proto files, and should contain *unresolved features* inside of
the options.

#### Legacy Editions {#legacy_editions}

As discussed more in
[legacy-syntax-editions](https://github.com/protocolbuffers/protobuf/blob/main/docs/design/editions/legacy-syntax-editions.md),
a great way to get early coverage of your editions implementation is to unify
proto2, proto3, and editions. This effectively migrates proto2 and proto3 to
editions under the hood, and makes all of the helpers implemented in
[Syntax Reflection](#syntax_reflection) use features exclusively (instead of
branching on syntax). This can be done by inserting a *feature inference* phase
into [Feature Resolution](#feature_resolution), where various aspects of the
proto file can inform what features are appropriate. These features can then be
merged into the parent’s features to get the resolved feature set.

While we provide reasonable defaults for proto2/proto3 already, for edition 2023
the following additional inferences are required:

*   required - we infer `LEGACY_REQUIRED` presence when a field has
    `LABEL_REQUIRED`
*   groups - we infer `DELIMITED` message encoding when a field has `TYPE_GROUP`
*   packed - we infer `PACKED` encoding when the `packed` option is true
*   expanded - we infer `EXPANDED` encoding when a proto3 field has `packed`
    explicitly set to false

#### Conformance Tests {#conformance-tests}

Editions-specific conformance tests have been added, but need to be opted-in to.
A `--maximum_edition 2023` flag can be passed to the runner to enable these. You
will need to configure your testee binary to handle the following new message
types:

*   `protobuf_test_messages.editions.proto2.TestAllTypesProto2` - Identical to
    the old proto2 message, but transformed to edition 2023
*   `protobuf_test_messages.editions.proto3.TestAllTypesProto3` - Identical to
    the old proto3 message, but transformed to edition 2023
*   `protobuf_test_messages.editions.TestAllTypesEdition2023` - Used to cover
    edition-2023-specific test cases

### Feature Resolution {#feature-resolution}

Editions use lexical scoping to define features, meaning that any non-C++ code
that needs to implement editions support will need to reimplement our *feature
resolution* algorithm. However, the bulk of the work is handled by protoc
itself, which can be configured to output an intermediate `FeatureSetDefaults`
message. This message contains a “compilation” of a set of feature definition
files, laying out the default feature values in every edition.

For example, the feature definition above would compile to the following
defaults between proto2 and edition 2025 (in text-format notation):

```
defaults {
  edition: EDITION_PROTO2
  overridable_features { [foo.features] {} }
  fixed_features {
    // Global feature defaults…
    [foo.features] { feature_value: VALUE1 }
  }
}
defaults {
  edition: EDITION_PROTO3
  overridable_features { [foo.features] {} }
  fixed_features {
    // Global feature defaults…
    [foo.features] { feature_value: VALUE1 }
  }
}
defaults {
  edition: EDITION_2023
  overridable_features {
    // Global feature defaults…
    [foo.features] { feature_value: VALUE1 }
  }
}
defaults {
  edition: EDITION_2024
  overridable_features {
    // Global feature defaults…
    [foo.features] { feature_value: VALUE2 }
  }
}
defaults {
  edition: EDITION_2025
  overridable_features {
    // Global feature defaults…
  }
  fixed_features { [foo.features] { feature_value: VALUE2 } }
}
minimum_edition: EDITION_PROTO2
maximum_edition: EDITION_2025

```

Global feature defaults are left out for compactness, but they would also be
present. This object contains an ordered list of every edition with a unique set
of defaults (some editions may end up not being present) within the specified
range. Each set of defaults is split into *overridable* and *fixed* features.
The former are supported features for the edition that can be freely overridden
by users. The fixed features are those which haven’t yet been introduced or have
been removed, and can’t be overridden by users.

We provide a Bazel rule for compiling these intermediate objects:

```
load("@com_google_protobuf//editions:defaults.bzl", "compile_edition_defaults")

compile_edition_defaults(
    name = "my_defaults",
    srcs = ["//some/path:lang_features_proto"],
    maximum_edition = "PROTO2",
    minimum_edition = "2024",
)
```

The output `FeatureSetDefaults` can be embedded into a raw string literal in
whatever language you need to do feature resolution in. We also provide an
`embed_edition_defaults` macro to do this:

```
embed_edition_defaults(
    name = "embed_my_defaults",
    defaults = ":my_defaults",
    output = "my_defaults.h",
    placeholder = "DEFAULTS_DATA",
    template = "my_defaults.h.template",
)
```

Alternatively, you can invoke protoc directly (outside of Bazel) to generate
this data:

```
protoc --edition_defaults_out=defaults.binpb --edition_defaults_minimum=PROTO2 --edition_defaults_maximum=2023 <feature files...>
```

Once the defaults message is hooked up and parsed by your code, feature
resolution for a file descriptor at a given edition follows a simple algorithm:

1.  Validate that the edition is in the appropriate range [`minimum_edition`,
    `maximum_edition`]
2.  Binary-search the ordered `defaults` field for the highest entry less than
    or equal to the edition
3.  Merge `overridable_features` into `fixed_features` from the selected
    defaults
4.  Merge any explicit features set on the descriptor (the `features` field in
    the file options)

From there, you can recursively resolve features for all other descriptors:

1.  Initialize to the parent descriptor’s feature set
2.  Merge any explicit features set on the descriptor (the `features` field in
    the options)

For determining the “parent” descriptor, you can reference our
[C++ implementation](https://github.com/protocolbuffers/protobuf/blob/27.x/src/google/protobuf/descriptor.cc#L1129).
This is straightforward in most cases, but extensions are a bit surprising
because their parent is the enclosing scope rather than the extendee. Oneofs
also need to be considered as the parent of their fields.

#### Conformance Tests {#conformance-tests}

In a future release, we plan to add conformance tests to verify feature
resolution cross-language. Until then, our regular
[conformance tests](#conformance_tests) do give partial coverage, and our
[example inheritance unit tests](https://github.com/protocolbuffers/protobuf/blob/27.x/python/google/protobuf/internal/descriptor_test.py#L1386)
can be ported to provide more comprehensive coverage.

### Examples {#examples}

Below are some real examples of how we implemented editions support in our
runtimes and plugins.

#### Java {#java}

*   [#14138](https://github.com/protocolbuffers/protobuf/pull/14138) - Bootstrap
    compiler with C++ gencode for Java features proto
*   [#14377](https://github.com/protocolbuffers/protobuf/pull/14377) - Use
    features in Java, Kotlin, and Java Lite code generators, including codegen
    tests
*   [#15210](https://github.com/protocolbuffers/protobuf/pull/15210) - Use
    features in Java full runtimes covering Java features bootstrap, feature
    resolution, and legacy editions, along with unit-tests and conformance
    testing

#### Pure Python {#python}

*   [#14546](https://github.com/protocolbuffers/protobuf/pull/14546) - Setup
    codegen tests in advance
*   [#14547](https://github.com/protocolbuffers/protobuf/pull/14547) - Fully
    implements editions in one shot, along with unit-tests and conformance
    testing

#### 𝛍pb {#upb}

*   [#14638](https://github.com/protocolbuffers/protobuf/pull/14638) - First
    pass at editions implementation covering feature resolution and legacy
    editions
*   [#14667](https://github.com/protocolbuffers/protobuf/pull/14667) - Added
    more complete handling of field label/type, support for upb’s code
    generator, and some tests
*   [#14678](https://github.com/protocolbuffers/protobuf/pull/14678) - Hooks up
    upb to the Python runtime, with more unit tests and conformance tests

#### Ruby {#ruby}

*   [#16132](https://github.com/protocolbuffers/protobuf/pull/16132) - Hook up
    upb/Java to all four Ruby runtimes for full editions support