|
| 1 | ++++ |
| 2 | +title = "Protobuf Editions Overview" |
| 3 | +weight = 42 |
| 4 | +description = "An overview of the Protobuf Editions functionality." |
| 5 | +type = "docs" |
| 6 | ++++ |
| 7 | + |
| 8 | +Protobuf Editions replace the proto2 and proto3 designations that we have used |
| 9 | +for Protocol Buffers. Instead of adding `syntax = "proto2"` or `syntax = |
| 10 | +"proto3"` at the top of proto definition files, you use an edition number, such |
| 11 | +as `edition = "2024"`, to specify the default behaviors your file will have. |
| 12 | +Editions enable the language to evolve incrementally over time. |
| 13 | + |
| 14 | +Instead of the hardcoded behaviors that older versions have had, editions |
| 15 | +represent a collection of features with a default value (behavior) per feature. |
| 16 | +Features are options on a file, message, field, enum, and so on, that specify |
| 17 | +the behavior of protoc, the code generators, and protobuf runtimes. You can |
| 18 | +explicitly override a behavior at those different levels (file, message, field, |
| 19 | +...) when your needs don't match the default behavior for the edition you've |
| 20 | +selected. You can also override your overrides. The |
| 21 | +[section later in this topic on inheritance](#inheritance) goes into more detail |
| 22 | +on that. |
| 23 | + |
| 24 | +## Lifecycles of a Feature {#lifecycles} |
| 25 | + |
| 26 | +Editions provide the fundamental increments for the lifecycle of a feature. |
| 27 | +Features have an expected lifecycle: introducing |
| 28 | +it, changing its default behavior, deprecating it, and then removing it. For |
| 29 | +example: |
| 30 | + |
| 31 | +1. Edition 2031 creates `feature.amazing_new_feature` with a default value of |
| 32 | + `false`. This value maintains the same behavior as all earlier editions. |
| 33 | + That is, it defaults to no impact. |
| 34 | + |
| 35 | +2. Developers update their .proto files to `edition = "2031"`. |
| 36 | + |
| 37 | +3. A later edition, such as edition 2033, switches the default of |
| 38 | + `feature.amazing_new_feature` from `false` to `true`. This is the desired |
| 39 | + behavior for all protos, and the reason that the protobuf team created the |
| 40 | + feature. |
| 41 | + |
| 42 | + Using the Prototiller tool to migrate earlier versions of proto files to |
| 43 | + edition 2033 adds explicit `feature.amazing_new_feature = false` entries as |
| 44 | + needed to continue to retain the previous behavior. Developers remove these |
| 45 | + newly-added settings when they want the new behavior to apply to their |
| 46 | + .proto files. |
| 47 | + |
| 48 | +<!-- mdformat off (preserve single lines/no wrapping) --> |
| 49 | + |
| 50 | +5. At some point, `feature.amazing_new_feature` is marked deprecated in an edition and removed in a later one. {value=5} |
| 51 | + |
| 52 | + When a feature is removed, the code generators for that behavior and the |
| 53 | + runtime libraries that support it may also be removed. The timelines will be |
| 54 | + generous, though. Following the example in the earlier steps of the |
| 55 | + lifecycle, the deprecation might happen in edition 2034 but not be removed |
| 56 | + until edition 2036, roughly two years later. Removing a feature will always |
| 57 | + initiate a major version bump. |
| 58 | + |
| 59 | +<!-- mdformat on --> |
| 60 | + |
| 61 | +Because of this lifecycle, any `.proto` file that does not use deprecated |
| 62 | +features has a no-op upgrade from one edition to the next. |
| 63 | +You will have the full |
| 64 | +window of the Google migration plus the deprecation window to upgrade your code. |
| 65 | + |
| 66 | +The preceding lifecycle example used boolean values for the features, but |
| 67 | +features may also use enums. For example, `features.field_presence` has values |
| 68 | +`LEGACY_REQUIRED`, `EXPLICIT`, and `IMPLICIT.` |
| 69 | + |
| 70 | +## Migrating to Protobuf Editions {#migrating} |
| 71 | + |
| 72 | +Editions won't break existing binaries and don't change a message's binary, |
| 73 | +text, or JSON serialization format. The first edition is as minimally disruptive |
| 74 | +as possible. The first edition establishes the baseline and combines proto2 and |
| 75 | +proto3 definitions into a new single definition format. |
| 76 | + |
| 77 | +When the subsequent editions are released, default behaviors for features may |
| 78 | +change. You can have Prototiller do a no-op transformation of your .proto file |
| 79 | +or you can choose to accept some or all of the new behaviors. Editions are |
| 80 | +planned to be released roughly once a year. |
| 81 | + |
| 82 | +### Proto2 to Editions {#proto2-migration} |
| 83 | + |
| 84 | +This section shows a proto2 file, and what it might look like after running the |
| 85 | +Prototiller tool to change the definition files to use Protobuf Editions syntax. |
| 86 | + |
| 87 | +<section class="tabs"> |
| 88 | + |
| 89 | +#### Proto2 syntax {.new-tab} |
| 90 | + |
| 91 | +```proto |
| 92 | +// proto2 file |
| 93 | +syntax = "proto2"; |
| 94 | +
|
| 95 | +message Player { |
| 96 | + // in proto2, optional fields have explicit presence |
| 97 | + optional string name = 1; |
| 98 | + // proto2 still supports the problematic "required" field rule |
| 99 | + required int32 id = 2; |
| 100 | + // in proto2 this is not packed by default |
| 101 | + repeated int32 scores = 3; |
| 102 | +
|
| 103 | + enum Handed { |
| 104 | + HANDED_UNSPECIFIED = 0, |
| 105 | + HANDED_LEFT = 1, |
| 106 | + HANDED_RIGHT = 2, |
| 107 | + HANDED_AMBIDEXTROUS = 3, |
| 108 | + } |
| 109 | +
|
| 110 | + // in proto2 enums are closed |
| 111 | + optional Handed handed = 4; |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +#### Editions syntax {.new-tab} |
| 116 | + |
| 117 | +```proto |
| 118 | +// Edition version of proto2 file |
| 119 | +edition = "2023"; |
| 120 | +
|
| 121 | +message Player { |
| 122 | + // fields have explicit presence, so no explicit setting needed |
| 123 | + string name = 1; |
| 124 | + // to match the proto2 behavior, LEGACY_REQUIRED is set at the field level |
| 125 | + int32 id = 2 [features.field_presence = LEGACY_REQUIRED]; |
| 126 | + // to match the proto2 behavior, EXPANDED is set at the field level |
| 127 | + repeated int32 scores = 3 [features.repeated_field_encoding = EXPANDED]; |
| 128 | +
|
| 129 | + enum Handed { |
| 130 | + // this overrides the default edition 2023 behavior, which is OPEN |
| 131 | + option features.enum_type = CLOSED; |
| 132 | + HANDED_UNSPECIFIED = 0, |
| 133 | + HANDED_LEFT = 1, |
| 134 | + HANDED_RIGHT = 2, |
| 135 | + HANDED_AMBIDEXTROUS = 3, |
| 136 | + } |
| 137 | +
|
| 138 | + Handed handed = 4; |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +</section> |
| 143 | + |
| 144 | +### Proto3 to Editions {#proto3-migration} |
| 145 | + |
| 146 | +This section shows a proto3 file, and what it might look like after running the |
| 147 | +Prototiller tool to change the definition files to use Protobuf Editions syntax. |
| 148 | + |
| 149 | +<section class="tabs"> |
| 150 | + |
| 151 | +#### Proto3 syntax {.new-tab} |
| 152 | + |
| 153 | +```proto |
| 154 | +// proto3 file |
| 155 | +syntax = "proto3"; |
| 156 | +
|
| 157 | +message Player { |
| 158 | + // in proto3, optional fields have explicit presence |
| 159 | + optional string name = 1; |
| 160 | + // in proto3 no specified field rule defaults to implicit presence |
| 161 | + int32 id = 2; |
| 162 | + // in proto3 this is packed by default |
| 163 | + repeated int32 scores = 3; |
| 164 | +
|
| 165 | + enum Handed { |
| 166 | + HANDED_UNSPECIFIED = 0, |
| 167 | + HANDED_LEFT = 1, |
| 168 | + HANDED_RIGHT = 2, |
| 169 | + HANDED_AMBIDEXTROUS = 3, |
| 170 | + } |
| 171 | +
|
| 172 | + // in proto3 enums are open |
| 173 | + optional Handed handed = 4; |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +#### Editions syntax {.new-tab} |
| 178 | + |
| 179 | +```proto |
| 180 | +// Editions version of proto3 file |
| 181 | +edition = "2023"; |
| 182 | +
|
| 183 | +message Player { |
| 184 | + // fields have explicit presence, so no explicit setting needed |
| 185 | + string name = 1; |
| 186 | + // to match the proto3 behavior, IMPLICIT is set at the field level |
| 187 | + int32 id = 2 [features.field_presence = IMPLICIT]; |
| 188 | + // PACKED is the default state, and is provided just for illustration |
| 189 | + repeated int32 scores = 3 [features.repeated_field_encoding = PACKED]; |
| 190 | +
|
| 191 | + enum Handed { |
| 192 | + HANDED_UNSPECIFIED = 0, |
| 193 | + HANDED_LEFT = 1, |
| 194 | + HANDED_RIGHT = 2, |
| 195 | + HANDED_AMBIDEXTROUS = 3, |
| 196 | + } |
| 197 | +
|
| 198 | + Handed handed = 4 [features.field_presence = IMPLICIT]; |
| 199 | +} |
| 200 | +``` |
| 201 | + |
| 202 | +</section> |
| 203 | + |
| 204 | +### Inheritance {#inheritance} |
| 205 | + |
| 206 | +Editions syntax supports inheritance, with a per-feature list of allowed |
| 207 | +targets. For example, in the first edition, features can be specified at only |
| 208 | +the file level or the lowest level of granularity. Inheritance enables you to |
| 209 | +set the default behavior for a feature across an entire file, and then override |
| 210 | +that behavior at the message, field, enum, enum value, oneof, service, or |
| 211 | +method. Settings made at a higher level (file, message) apply when no setting is |
| 212 | +made within the same scope (field, enum value). Any features not explicitly set |
| 213 | +conform to the behavior defined in the edition version used for the .proto file. |
| 214 | + |
| 215 | +The following code sample shows some features being set at the file, message, |
| 216 | +and enum level. The settings are in the highlighted lines: |
| 217 | + |
| 218 | +```proto {highlight="lines:3,7,16"} |
| 219 | +edition = "2023"; |
| 220 | +
|
| 221 | +option features.enum_type = CLOSED; |
| 222 | +
|
| 223 | +message Person { |
| 224 | + string name = 1; |
| 225 | + int32 id = 2 [features.presence = IMPLICIT]; |
| 226 | +
|
| 227 | + enum Pay_Type |
| 228 | + PAY_TYPE_UNSPECIFIED = 1, |
| 229 | + PAY_TYPE_SALARY = 2, |
| 230 | + PAY_TYPE_HOURLY = 3 |
| 231 | + } |
| 232 | +
|
| 233 | + enum Employment { |
| 234 | + option features.enum_type = OPEN; |
| 235 | + EMPLOYMENT_UNSPECIFIED = 0, |
| 236 | + EMPLOYMENT_FULLTIME = 1, |
| 237 | + EMPLOYMENT_PARTTIME = 2, |
| 238 | + } |
| 239 | + Employment employment = 4; |
| 240 | +} |
| 241 | +``` |
| 242 | + |
| 243 | +In the preceding example, the presence feature is set to `IMPLICIT`; it would |
| 244 | +default to `EXPLICIT` if it wasn't set. The `Pay_Type` `enum` will be `CLOSED`, |
| 245 | +as it inherits the file-level setting. The `Employment` `enum`, though, will be |
| 246 | +`OPEN`, as it is set within the enum. |
| 247 | + |
| 248 | +### Prototiller {#prototiller} |
| 249 | + |
| 250 | +We provide both a migration guide and migration tooling that ease the migration |
| 251 | +to editions. The tool, called Prototiller, will enable you to: |
| 252 | + |
| 253 | +* convert proto2 and proto3 definition files to the new editions syntax, at |
| 254 | + scale |
| 255 | +* migrate files from one edition to another |
| 256 | +* manipulate proto files in other ways |
| 257 | + |
| 258 | +### Backward Compatibility {#compatibility} |
| 259 | + |
| 260 | +We are building Protobuf Editions to be as minimally disruptive as possible. For |
| 261 | +example, you can import proto2 and proto3 definitions into editions-based |
| 262 | +definition files, and vice versa: |
| 263 | + |
| 264 | +```proto |
| 265 | +// file myproject/foo.proto |
| 266 | +syntax = "proto2"; |
| 267 | +
|
| 268 | +enum Employment { |
| 269 | + EMPLOYMENT_UNSPECIFIED = 0, |
| 270 | + EMPLOYMENT_FULLTIME = 1, |
| 271 | + EMPLOYMENT_PARTTIME = 2, |
| 272 | +} |
| 273 | +``` |
| 274 | + |
| 275 | +```proto |
| 276 | +// file myproject/edition.proto |
| 277 | +edition = "2023"; |
| 278 | +
|
| 279 | +import "myproject/foo.proto"; |
| 280 | +``` |
| 281 | + |
| 282 | +While the generated code changes when you move from proto2 or proto3 to |
| 283 | +editions, the wire format does not. You'll still be able to access proto2 and |
| 284 | +proto3 data files or file streams using your editions-syntax proto definitions. |
0 commit comments