Skip to content

Commit a7e3a7d

Browse files
ePaulPaul Ebermanntfrauenstein
authored
Deprecate x-extensible-enum (#837)
* Deprecate x-extensible-enum Implements #831. * Code review suggestions Co-authored-by: Thomas Frauenstein <thomas.frauenstein@zalando.de> * Update PR for october, mention old versions * apply a few editorial suggestions * Apply suggestions from code review editorial: comma, word order Co-authored-by: Thomas Frauenstein <thomas.frauenstein@zalando.de> * remove semantic replication. * split bullet point * reword "rules apply for legacy" Co-authored-by: Thomas Frauenstein <thomas.frauenstein@zalando.de> * Apply suggestion from @ePaul Co-authored-by: Paŭlo Ebermann <paul.ebermann@zalando.de> * Update chapters/compatibility.adoc --------- Co-authored-by: Paul Ebermann <paul@zalando.de> Co-authored-by: Thomas Frauenstein <thomas.frauenstein@zalando.de>
1 parent 24fa5f7 commit a7e3a7d

File tree

3 files changed

+95
-38
lines changed

3 files changed

+95
-38
lines changed

chapters/compatibility.adoc

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ services in a backward-compatible way:
5151
make sure that all constraints are clearly defined in description.
5252
* `enum` ranges can be reduced when used as input parameters, only if the server
5353
is ready to accept and handle old range values too. The range can be reduced
54-
when used as output parameters.
54+
when used only as output parameters.
5555
* `enum` ranges cannot be extended when used for output parameters — clients may
56-
not be prepared to handle it. However, enum ranges can be extended when used
57-
for input parameters.
56+
not be prepared to handle it. However, `enum` ranges can be extended when used
57+
only for input parameters.
5858
* You <<112>> that are used for output parameters and likely to
5959
be extended with growing functionality. The API specification should be updated
6060
first before returning new values.
@@ -124,8 +124,11 @@ Service clients should apply the robustness principle:
124124
http://martinfowler.com/bliki/TolerantReader.html["TolerantReader"] post),
125125
i.e. ignore new fields but do not eliminate them from payload if needed for
126126
subsequent {PUT} requests.
127-
** Be prepared that {x-extensible-enum} return parameters (see <<112, rule 112>>) may deliver new values;
128-
either be agnostic or provide default behavior for unknown values, and do not eliminate them.
127+
** Be prepared that <<112, extensible enum>> return parameters
128+
may deliver new values;
129+
either be agnostic or provide default behavior for unknown values, and
130+
do not eliminate them when passed to subsequent {PUT} requests.
131+
(This means you cannot simply implement it by using a limited enumeration type like a Java `enum`.)
129132
** Be prepared to handle HTTP status codes not explicitly specified in endpoint
130133
definitions. Note also, that status codes are extensible. Default handling is
131134
how you would treat the corresponding {x00} code (see
@@ -289,20 +292,69 @@ level data structures, since they don't support compatible, future extensions.
289292

290293

291294
[#112]
292-
== {SHOULD} use open-ended list of values (`x-extensible-enum`) for enumeration types
295+
== {SHOULD} use open-ended list of values (via `examples`) for enumeration types
293296

294297
JSON schema `enum` is per definition a closed set of values that is assumed to be
295-
complete and not intended for extension. This closed principle of enumerations
296-
imposes compatibility issues when an enumeration must be extended. To avoid
297-
these issues, we recommend to use an open-ended list of values instead
298-
of an enumeration unless:
298+
complete and not intended for extension. This means, extending the list of values of
299+
`enum` is considered an incompatible change, and needs to be aligned with all consumers
300+
like other incompatible changes.
299301

300-
1. the API has full control of the enumeration values, i.e. the list of values
302+
To avoid these issues, we recommend to use `enum` only if
303+
304+
1. the API owner has full control of the enumeration values, i.e. the list of values
301305
does not depend on any external tool or interface, and
302306
2. the list of values is complete with respect to any thinkable and unthinkable
303307
future feature.
304308

305-
To specify an open-ended list of values via the {x-extensible-enum} property as follows:
309+
In all other cases, where additional values are imaginable our recommendation is this:
310+
311+
* Use `examples` with the list of (currently known) values
312+
* Add `[Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112).` as a standard prefix to the description.
313+
314+
This indicates that only the listed values are currently possible, but
315+
consumers need to be aware that this list can be extended without notice
316+
(see below for details).
317+
318+
[source,yaml]
319+
----
320+
delivery_method:
321+
type: string
322+
examples:
323+
- PARCEL
324+
- LETTER
325+
- EMAIL
326+
description: [Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112). The chosen delivery method of the invoice.
327+
----
328+
329+
See <<240>> about enum value naming conventions – these apply here too.
330+
331+
332+
**Important**:
333+
334+
* API consumers must be prepared for the fact that also other values can be returned
335+
with server responses (or be contained in consumed events), and implement a
336+
fallback / default behavior for unknown new values, see <<108>>.
337+
* API owners must take care to extend these extensible enums in a compatible way, i.e.
338+
not changing the semantics of already existing / documented values.
339+
* API implementations should validate the values provided with the input payload
340+
and only accept values listed in `examples`.
341+
* The list should not be reduced for inputs (that would be an incompatible change).
342+
* Before additional values are accepted or returned, API owners should update the API description and extend
343+
the `examples` list, see <<107>>.
344+
345+
(Note that the last 3 bullet points do not apply for uses of `examples` _without_ the
346+
"Extensible enum." prefix in the description – here any value fitting the rest
347+
of the schema needs to be expected.)
348+
349+
Note that the plural `examples` on schemas was only introduced with OpenAPI 3.1 (with an update of the JSON schema version referenced).
350+
APIs defined with older OpenAPI versions can't use this format, and instead need to use
351+
the {x-extensible-enum} described below↓.
352+
353+
=== Historic Note: `x-extensible-enum`
354+
355+
Previously (until October 2025) this guideline recommended a proprietary
356+
JSON schema extension {x-extensible-enum} with the same semantic.
357+
The example above would be specified as follows:
306358

307359
[source,yaml]
308360
----
@@ -312,23 +364,20 @@ delivery_methods:
312364
- PARCEL
313365
- LETTER
314366
- EMAIL
367+
description: The chosen delivery method of the invoice.
315368
----
316369

317-
*Note:* {x-extensible-enum} is a proprietary extension of the JSON Schema standard that
318-
is e.g. visible via Swagger UI, but ignored by most other tools.
319-
320-
See <<240>> about enum value naming conventions.
370+
The "important" rules above apply in an analog way for API providers and consumers using {x-extensible-enum}.
321371

322-
Note, {x-extensible-enum} is a different concept than JSON schema `examples` which is
323-
just a list of a few example values, whereas {x-extensible-enum} defines all valid
324-
values (for a specific API and service version) and has the advantage of an extensible
325-
full type-range specification that is validated by the service.
372+
This rule originated in the time before JSON schema and OpenAPI schema
373+
had the plural `examples` property (OpenAPI schema had singular `example`,
374+
JSON schema had neither).
326375

327-
*Important:* Clients must be prepared for extensions of enums returned with server responses, i.e.
328-
must implement a fallback / default behavior to handle unknown new enum values -- see <<108>>.
329-
API owners must take care to extend enums in a compatible way that does not change the
330-
semantics of already existing enum values, for instance, do not split an old enum value
331-
into two new enum values. Services should only extend {x-extensible-enum} ranges, and only accept
332-
and return values listed in the API specification, i.e. the API specification needs to be updated first
333-
before the service accepts/returns new values -- see also <<107>>.
376+
The completeness semantic would in theory allow some validation by
377+
intermediaries (but that was rarely implemented).
378+
It was visible in a few tools (e.g. Swagger UI), but ignored by most others.
334379

380+
After the team responsible for Nakadi (our Zalando-internal event bus) decided
381+
to not validate x-extensible-enum, but instead even reject it in event type
382+
definitions, we (the API guideline maintainers) revisited this rule to
383+
recommend `examples` instead.

chapters/events.adoc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -952,23 +952,29 @@ maintain compatibility as they will not be in a position to serve
952952
versioned media types on demand.
953953
954954
For event schema, these are considered backward compatible changes, as
955-
seen by consumers -
955+
seen by consumers:
956956
957957
* Adding new optional fields to JSON objects.
958958
* Changing the order of fields (field order in objects is arbitrary).
959959
* Changing the order of values with same type in an array.
960-
* Removing optional fields.
960+
* Removing optional fields (or rather, stop sending values for the optional
961+
field – actually removing it from the schema is not allowed, as otherwise
962+
it could be added back with an incompatible type).
961963
* Removing an individual value from an enumeration.
962-
* Adding new value to a {x-extensible-enum} field (see <<112, rule 112>> and <<108, rule 108>>).
964+
* Adding new values to extensible enum fields (see <<112, rule 112>> and <<108, rule 108>>).
963965
964966
These are considered backwards-incompatible changes, as seen by
965-
consumers -
967+
consumers:
966968
967969
* Removing required fields from JSON objects.
968970
* Changing the default value of a field.
969971
* Changing the type of a field, object, enum or array.
970972
* Changing the order of values with different type in an array (also
971973
known as a tuple).
972-
* Adding a new optional field to redefine the meaning of an existing
973-
field (also known as a co-occurrence constraint).
974-
* Adding a value to an enumeration. Instead, you <<112>>.
974+
* Adding a new optional field to redefine the meaning of a (previously)
975+
existing field (also known as a co-occurrence constraint).
976+
* Adding a value to an `enum` enumeration. Instead, you <<112>>.
977+
978+
When an incompatible change is required, it needs to be aligned with all consumers.
979+
Only start sending values not covered by the previous schema after all consumers
980+
are prepared to consume the new values.

chapters/json-guidelines.adoc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,14 @@ as if it came from the same hand.
136136

137137
Enumerations should be represented as `string` typed OpenAPI definitions of
138138
request parameters or model properties.
139-
Enum values (using `enum` or {x-extensible-enum}) need to consistently use
140-
the upper-snake case format, e.g. `VALUE` or `YET_ANOTHER_VALUE`.
141-
This approach allows to clearly distinguish values from properties or other elements.
139+
Enum values (using `enum`, {x-extensible-enum} or extensible enums using `examples`)
140+
need to consistently use the upper-snake case format, e.g. `VALUE`
141+
or `YET_ANOTHER_VALUE`.
142+
This approach allows to clearly distinguish enum values from properties or
143+
other elements.
142144

143145
**Exception:** This rule does not apply for case sensitive values sourced from outside
144-
API specification scope, e.g. for language codes from {ISO-639-1}[ISO 639-1], or when
146+
of the API specification scope, e.g. for language codes from {ISO-639-1}[ISO 639-1], or when
145147
declaring possible values for a <<137,rule 137>> [`sort` parameter].
146148

147149

0 commit comments

Comments
 (0)