| # Minimum Required Edition | |
| **Author:** [@mcy](https://github.com/mcy) | |
| **Approved:** 2022-11-15 | |
| A versioning mechanism for descriptors that ensures old runtimes do not load | |
| descriptors that are "too new." | |
| ## Background | |
| Suppose we decide to add a novel definition like | |
| ``` | |
| const int32 MY_CONSTANT = 42; | |
| ``` | |
| to the Protobuf language. This would entail a descriptor change to track the | |
| values of constants, but they would not be loaded properly by older runtimes. | |
| This document describes an addition to `descriptor.proto` that prevents this | |
| version mismatch issue. | |
| [Protobuf Editions](what-are-protobuf-editions.md) intends to add the concept of | |
| an edition to Protobuf, which will be an approximately-annually incrementing | |
| value. Because of their annual nature, and because runtimes need to be updated | |
| to handle new features they implement regardless, we can use them as a poison | |
| pill for old runtimes that try to load descriptors that are "too new." | |
| ## Overview | |
| We propose adding a new field to `FileDescriptorProto`: | |
| ``` | |
| optional string minimum_required_edition = ...; | |
| ``` | |
| This field would exist alongside the `edition` field, and would have the | |
| following semantics: | |
| Every Protobuf runtime implementation must specify the newest edition whose | |
| constructs it can handle (at a particular rev of that implementation). If that | |
| edition is less than `minimum_required_edition`, loading the descriptor must | |
| fail. | |
| "Less than" is defined per the edition total order given in | |
| [Life of an Edition](life-of-an-edition.md). To restate it, it is the following | |
| algorithm: | |
| ``` | |
| def edition_less_than(a, b): | |
| parts_a = a.split(".") | |
| parts_b = b.split(".") | |
| for i in range(0, min(len(parts_a), len(parts_b))): | |
| if int(parts_a[i]) < int(parts_b[i]): return True | |
| return len(a) < len(b) | |
| ``` | |
| `protoc` should keep track of which constructions require which minimum edition. | |
| For example, if constants are introduced in edition 2025, but they are not | |
| present in a file, `protoc` should not require that runtimes understand | |
| constants by picking a lower edition, like 2023 (assuming no other construct | |
| requires a higher edition). | |
| In particular, the following changes should keep the minimum edition constant, | |
| with all other things unchanged: | |
| * An upgrade of the proto compiler. | |
| * Upgrading the specified edition of a file via Prototiller. | |
| ### Bootstrapping Concerns | |
| "Epochs for `descriptor.proto`" (not available externally) describes a potential | |
| issue with bootstrapping. It is not the case here: minimum edition is only | |
| incremented once a particular file uses a new feature. Since `descriptor.proto` | |
| and other schemas used by `protoc` and the backends would not use new features | |
| immediately, introducing a new feature does not immediately stop the compiler | |
| from being able to compile itself. | |
| ### Concerns for Schema Producers | |
| Schema producers should consider changes to their schemas that increase the | |
| minimum required edition to be breaking changes, since it will stop compiled | |
| descriptors from being loaded at runtime. | |
| ## Recommendation | |
| We recommend adding the aforementioned minimum required edition field, along | |
| with the semantics it entails. This logic should be implemented entirely in the | |
| protoc frontend. | |
| ## Alternatives | |
| ### Use a Non-Editions Version Number | |
| Rather than using the editions value, use some other version number. This number | |
| would be incremented rarely (3-5 year horizon). This is the approach proposed | |
| in "Epochs for `descriptor.proto`." | |
| #### Pros | |
| * Does not re-use the editions value for a semantically-different meaning; the | |
| edition remains being "just" a key into a table of features defaults. | |
| #### Cons | |
| * Introduces another version number to Protobuf that increments at its own | |
| cadence. | |
| * Could potentially be confused with the edition value, even though they serve | |
| distinct purposes. | |
| ### Minimum Required Edition Should Not Be Minimal | |
| The proto compiler should not guarantee that the minimum required edition is as | |
| small as it could possibly be. | |
| #### Pros | |
| * Reduces implementation burden. | |
| #### Cons | |
| * This introduces situations where an upgrade of the proto compiler, or an | |
| innocuous change to a schema, can lead the the minimum required edition | |
| being incremented. This is a problem for schema producers. | |
| ### Do Nothing | |
| #### Pros | |
| * Reduces churn in runtimes, since they do not need to implement new handling | |
| for new *editions* (as contrasted to just *features)* regularly. | |
| * Avoids a situation where old software cannot load new descriptors at | |
| runtime. | |
| #### Cons | |
| * Commits us to never changing the descriptor wire format in | |
| backwards-incompatible ways, which has far-reaching effects on evolution. | |
| These consequences are discussed in "Epochs for `descriptor.proto`." |