AIP-126

Enumerations

It is common for a field to only accept or provide a discrete and limited set of values. In these cases, it can be useful to use enumerations (generally abbreviated "enums") in order to clearly communicate what the set of allowed values are.

Guidance

APIs may expose enum objects for sets of values that are expected to change infrequently:

// A representation of a book.
message Book {
  // Other fields...

  // Possible formats in which the book may be published.
  enum Format {
    // Default value. This value is unused.
    FORMAT_UNSPECIFIED = 0;

    // The printed format, in hardback.
    HARDBACK = 1;

    // The printed format, in paperback.
    PAPERBACK = 2;

    // An electronic book format.
    EBOOK = 3;

    // An audio recording.
    AUDIOBOOK = 4;
  }

  // The format of the book.
  Format format = 99;

  // Other fields...
}
  • All enum values must use UPPER_SNAKE_CASE.
  • The first value of the enum should be the name of the enum itself followed by the suffix _UNSPECIFIED.
    • An exception to this rule is if there is a clearly useful zero value. In particular, if an enum needs to present an UNKNOWN, it is usually clearer and more useful for it to be a zero value rather than having both.
  • Enums which will only be used in a single message should be nested within that message. In this case, the enum should be declared immediately before it is used.
    • The non-zero values of such a nested enum definition should not be prefixed by the name of the enum itself. This generally requires users to write MyState.MYSTATE_ACTIVE in their code, which is unnecessarily verbose.
  • Enums which will be used by multiple messages should be defined at the package level and should be defined at the bottom of the proto file (see AIP-191).
    • Some languages (including C++) hoist enum values into the parent namespace, which can result in conflicts for enums with the same values in the same proto package. To avoid sharing values, APIs should prefix package-level enum values with the name of the enum.
  • Enums should document whether the enum is frozen or they expect to add values in the future.

When to use enums

Enums can be more accessible and readable than strings or booleans in many cases, but they do add overhead when they change. Therefore, enums should receive new values infrequently. While the definition of "infrequently" may change based on individual use cases, a good rule of thumb is no more than once a year. For enums that change frequently, the API should use a string and document the format.

Additionally, enums should not be used when there is a competing, widely-adopted standard representation (such as with language codes or media types).

Note: If an enumerated value needs to be shared across APIs, an enum may be used, but the assignment between enum values and their corresponding integers must match.

Alternatives

For enumerated values where the set of allowed values changes frequently, APIs should use a string field instead, and must document the allowed values. String fields with enumerated values should use kebab-case for their values.

For enumerated values where there is a competing, widely-adopted standard representation (generally, but not necessarily, a string), that standard representation should be used. This is true even if only a small subset of values are permitted, because using enums in this situation often leads to frustrating lookup tables when trying to use multiple APIs together.

Boolean fields may be used in situations where it is clear that no further flexibility will be needed. The default value must be false.

Note: When using protocol buffers, it is impossible to distinguish between false and unset. If this is a requirement, an enum may be a better design choice (although google.protobuf.BoolValue is also available).

Further reading

  • For states, a special type of enum, see AIP-216.