Skip to content

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:

// Possible formats in which a book may be published.
enum Format {
// The printed format, in hardback.
Hardback = 'HARDBACK',
// The printed format, in paperback.
Paperback = 'PAPERBACK',
// An electronic book format.
Ebook = 'EBOOK',
// An audio recording.
Audiobook = 'AUDIOBOOK',
}
  • All enum values should use a consistent case format across an organization. In many cases, this is dictated by the IDL the organization uses.
  • 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.

Alternatives

Enums should not be used when there is a competing, widely-adopted standard representation (such as with language codes or media types). Instead, 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.

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 a uniform case system (snake_case, kebab-case, etc.) throughout an organization.

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

Compatibility

Adding values to an enum has the potential to be disruptive to existing clients. Consider code written against the Format enum in an earlier version where only the first two options were available:

switch (book.format) {
case Format.Hardback:
// Do something...
break;
case Format.Paperback:
// Do something...
break;
default:
// When new enum values are introduced, pre-existing client code may
// throw errors or act in unexpected ways.
throw new Error('Unrecognized value.');
}

Services may add new values to existing enums; however, they should add enums carefully; think about what will happen if a client system does not know about a new value.

Additionally, in IDLs where enum values are presented in a specific order, services should only add new values to the end. An exception to this rule is if the enum conforms to an external standard (for example, an enum representing HTTP status codes would add a new 3xx value alongside the others, not at the end).

Interface Definitions

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 zero value of the enum should be the name of the enum itself followed by the suffix _UNSPECIFIED. The service may either allow or prohibit use of this value.
  • 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.
  • If multiple enums are in the same namespace, they must not share any values. (This is because enums do not provide their own namespace for their values in some languages.)
  • If an enumerated value needs to be shared across APIs, an enum may be used, but the assignment between the value names and the tag numbers must match.

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 AEP-216.