From 84a926b0eb77efcfc138c859b1a461324f14df3f Mon Sep 17 00:00:00 2001 From: Paul Ebermann Date: Tue, 29 Apr 2025 17:28:11 +0200 Subject: [PATCH 01/10] Deprecate x-extensible-enum Implements #831. --- chapters/compatibility.adoc | 101 +++++++++++++++++++++++++--------- chapters/events.adoc | 20 ++++--- chapters/json-guidelines.adoc | 12 ++-- 3 files changed, 94 insertions(+), 39 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 52f24376..3230ab08 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -51,10 +51,10 @@ services in a backward-compatible way: make sure that all constraints are clearly defined in description. * `enum` ranges can be reduced when used as input parameters, only if the server is ready to accept and handle old range values too. The range can be reduced - when used as output parameters. + when used only as output parameters. * `enum` ranges cannot be extended when used for output parameters — clients may - not be prepared to handle it. However, enum ranges can be extended when used - for input parameters. + not be prepared to handle it. However, `enum` ranges can be extended when used + only for input parameters. * You <<112>> that are used for output parameters and likely to be extended with growing functionality. The API definition should be updated first before returning new values. @@ -124,8 +124,11 @@ Service clients should apply the robustness principle: http://martinfowler.com/bliki/TolerantReader.html["TolerantReader"] post), i.e. ignore new fields but do not eliminate them from payload if needed for subsequent {PUT} requests. -** Be prepared that {x-extensible-enum} return parameters (see <<112, rule 112>>) may deliver new values; - either be agnostic or provide default behavior for unknown values, and do not eliminate them. +** Be prepared that "extensible enum" return parameters (see <<112, rule 112>>) + may deliver new values; + either be agnostic or provide default behavior for unknown values, and + do not eliminate them if needed for subsequent {PUT} requests. + (This means you can't simply map it to a limited enumeration type like a Java `enum`.) ** Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions. Note also, that status codes are extensible. Default handling is how you would treat the corresponding {x00} code (see @@ -289,46 +292,90 @@ level data structures, since they don't support compatible, future extensions. [#112] -== {SHOULD} use open-ended list of values (`x-extensible-enum`) for enumeration types +== {SHOULD} use open-ended list of values (via `examples`) for enumeration types JSON schema `enum` is per definition a closed set of values that is assumed to be -complete and not intended for extension. This closed principle of enumerations -imposes compatibility issues when an enumeration must be extended. To avoid -these issues, we recommend to use an open-ended list of values instead -of an enumeration unless: +complete and not intended for extension. This means, extending the list of values of +`enum` is considered an incompatible change, and needs to be aligned with all consumers +as other incompatible changes. + +To avoid these issues, we recommend to use `enum` only if 1. the API has full control of the enumeration values, i.e. the list of values does not depend on any external tool or interface, and 2. the list of values is complete with respect to any thinkable and unthinkable future feature. -To specify an open-ended list of values via the {x-extensible-enum} property as follows: +In all other cases, where additional values are imaginable our recommendation is this: + +* Use `examples` with the list of currently known values +* Put "Extensible enum" in the description. + +This indicates that currently these listed values will be possible, but +consumers need to be aware that this list can be extended without notice +(see below for details). [source,yaml] ---- -delivery_methods: +delivery_method: type: string - x-extensible-enum: + examples: - PARCEL - LETTER - EMAIL + description: Extensible enum. The chosen delivery method of the invoice. ---- -*Note:* {x-extensible-enum} is a proprietary extension of the JSON Schema standard that -is e.g. visible via Swagger UI, but ignored by most other tools. - See <<240>> about enum value naming conventions. -Note, {x-extensible-enum} is a different concept than JSON schema `examples` which is -just a list of a few example values, whereas {x-extensible-enum} defines all valid -values (for a specific API and service version) and has the advantage of an extensible -full type-range specification that is validated by the service. +**Important**: + +* API consumers must be prepared for the fact that also other values can be returned + with server responses (or be contained in consumed events), and implement a + fallback / default behavior for unknown new values, see <<108>>. +* API owners must take care to extend these extensible enums in a compatible way, i.e. + not changing the semantics of already existing / documented values. +* For uses in input, API implementations should validate the values and only accept + values listed in `examples`. The list should not be reduced (that would be an incompatible change). +* Before additional values are accepted or returned, API owners should extend + the `examples` list, see <<107>>. + +(Note that the last 3 bullet points do not apply for uses of `examples` _without_ the + "Extensible enum" marker in the description – here any value at all needs + to be expected.) + +=== Historic Note + +Previously (until May 2025), this guideline recommended our own proprietary +{x-extensible-enum} JSON schema extension here, with a similar semantic: + +> This is the *complete* list of values *currently* possible, but consumers must be +prepared for other values in the future. + +Until all existing APIs using this have been updated, API providers and consumers +still need to follow the rules under "Important" above for these cases. + +This completeness semantic would in theory allow some validation by +intermediaries (but that was rarely implemented). +It was visible in a few tools (e.g. Swagger UI), but ignored by most others. + +An open-ended list of values was specified as follows: + +[source,yaml] +---- +delivery_methods: + type: string + x-extensible-enum: + - PARCEL + - LETTER + - EMAIL + description: The chosen delivery method of the invoice. +---- -*Important:* Clients must be prepared for extensions of enums returned with server responses, i.e. -must implement a fallback / default behavior to handle unknown new enum values -- see <<108>>. -API owners must take care to extend enums in a compatible way that does not change the -semantics of already existing enum values, for instance, do not split an old enum value -into two new enum values. Services should only extend {x-extensible-enum} ranges, and only accept -and return values listed in the API definition, i.e. the API definition needs to be updated first -before the service accepts/returns new values -- see also <<107>>. +This rule originated in the time before JSON schema and OpenAPI schema +had the plural `examples` property (OpenAPI schema had singular `example`, +JSON schema had neither). +We also thought we could have some validations (e.g. by our event bus Nakadi), +but the Nakadi team instead decided to not validate `x-extensible-enum`, +and even reject it in schema definitions. diff --git a/chapters/events.adoc b/chapters/events.adoc index 1105ff58..c2b81b99 100644 --- a/chapters/events.adoc +++ b/chapters/events.adoc @@ -952,23 +952,29 @@ maintain compatibility as they will not be in a position to serve versioned media types on demand. For event schema, these are considered backward compatible changes, as -seen by consumers - +seen by consumers: * Adding new optional fields to JSON objects. * Changing the order of fields (field order in objects is arbitrary). * Changing the order of values with same type in an array. -* Removing optional fields. +* Removing optional fields (or rather, stop sending values for the optional + field – actually removing it from the schema is not allowed, as otherwise + it could be added back with an incompatible type). * Removing an individual value from an enumeration. -* Adding new value to a {x-extensible-enum} field (see <<112, rule 112>> and <<108, rule 108>>). +* Adding new values to extensible enum fields (see <<112, rule 112>> and <<108, rule 108>>). These are considered backwards-incompatible changes, as seen by -consumers - +consumers: * Removing required fields from JSON objects. * Changing the default value of a field. * Changing the type of a field, object, enum or array. * Changing the order of values with different type in an array (also known as a tuple). -* Adding a new optional field to redefine the meaning of an existing -field (also known as a co-occurrence constraint). -* Adding a value to an enumeration. Instead, you <<112>>. +* Adding a new optional field to redefine the meaning of a (previously) + existing field (also known as a co-occurrence constraint). +* Adding a value to an `enum` enumeration. Instead, you <<112>>. + +When an incompatible change is required, it needs to be aligned with all consumers. +Only start sending values not covered by the previous schema after all consumers +are prepared to consume the new values. diff --git a/chapters/json-guidelines.adoc b/chapters/json-guidelines.adoc index 025e8822..9179a8bc 100644 --- a/chapters/json-guidelines.adoc +++ b/chapters/json-guidelines.adoc @@ -136,13 +136,15 @@ as if it came from the same hand. Enumerations should be represented as `string` typed OpenAPI definitions of request parameters or model properties. -Enum values (using `enum` or {x-extensible-enum}) need to consistently use -the upper-snake case format, e.g. `VALUE` or `YET_ANOTHER_VALUE`. -This approach allows to clearly distinguish values from properties or other elements. +Enum values (using `enum`, {x-extensible-enum} or extensible enums using `examples`) +need to consistently use the upper-snake case format, e.g. `VALUE` +or `YET_ANOTHER_VALUE`. +This approach allows to clearly distinguish enum values from properties or +other elements. **Exception:** This rule does not apply for case sensitive values sourced from outside -API definition scope, e.g. for language codes from {ISO-639-1}[ISO 639-1], or when -declaring possible values for a <<137,rule 137>> [`sort` parameter]. +of the API definition scope, e.g. for language codes from {ISO-639-1}[ISO 639-1], +or when declaring possible values for a <<137,rule 137>> [`sort` parameter]. [#235] From 6563e9c1a9759cac9ad0e53bdd849ea3ccb17f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=C5=ADlo=20Ebermann?= Date: Wed, 7 May 2025 13:18:04 +0200 Subject: [PATCH 02/10] Code review suggestions Co-authored-by: Thomas Frauenstein --- chapters/compatibility.adoc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 3230ab08..0453719f 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -128,7 +128,7 @@ Service clients should apply the robustness principle: may deliver new values; either be agnostic or provide default behavior for unknown values, and do not eliminate them if needed for subsequent {PUT} requests. - (This means you can't simply map it to a limited enumeration type like a Java `enum`.) + (This means you cannot simply implement it by using a limited enumeration type like a Java `enum`.) ** Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions. Note also, that status codes are extensible. Default handling is how you would treat the corresponding {x00} code (see @@ -297,7 +297,7 @@ level data structures, since they don't support compatible, future extensions. JSON schema `enum` is per definition a closed set of values that is assumed to be complete and not intended for extension. This means, extending the list of values of `enum` is considered an incompatible change, and needs to be aligned with all consumers -as other incompatible changes. +like other incompatible changes. To avoid these issues, we recommend to use `enum` only if @@ -308,10 +308,10 @@ To avoid these issues, we recommend to use `enum` only if In all other cases, where additional values are imaginable our recommendation is this: -* Use `examples` with the list of currently known values +* Use `examples` with the list of (currently known) values * Put "Extensible enum" in the description. -This indicates that currently these listed values will be possible, but +This indicates that only the listed values are currently possible, but consumers need to be aware that this list can be extended without notice (see below for details). @@ -335,13 +335,13 @@ See <<240>> about enum value naming conventions. fallback / default behavior for unknown new values, see <<108>>. * API owners must take care to extend these extensible enums in a compatible way, i.e. not changing the semantics of already existing / documented values. -* For uses in input, API implementations should validate the values and only accept - values listed in `examples`. The list should not be reduced (that would be an incompatible change). -* Before additional values are accepted or returned, API owners should extend +* API implementations should validate the values provided with the input payload and only accept + values listed in `examples`. The list should not be reduced for inputs (that would be an incompatible change). +* Before additional values are accepted or returned, API owners should update the API description and extend the `examples` list, see <<107>>. (Note that the last 3 bullet points do not apply for uses of `examples` _without_ the - "Extensible enum" marker in the description – here any value at all needs + "Extensible enum." prefix in the description – here any value needs to be expected.) === Historic Note @@ -378,4 +378,4 @@ JSON schema had neither). We also thought we could have some validations (e.g. by our event bus Nakadi), but the Nakadi team instead decided to not validate `x-extensible-enum`, -and even reject it in schema definitions. +and even reject it in event type schema definitions. From c02f13c291563d50282d25505fa28d77d7ddd3ad Mon Sep 17 00:00:00 2001 From: Paul Ebermann Date: Wed, 29 Oct 2025 14:58:28 +0100 Subject: [PATCH 03/10] Update PR for october, mention old versions --- chapters/compatibility.adoc | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 0453719f..49292da3 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -301,7 +301,7 @@ like other incompatible changes. To avoid these issues, we recommend to use `enum` only if -1. the API has full control of the enumeration values, i.e. the list of values +1. the API owner has full control of the enumeration values, i.e. the list of values does not depend on any external tool or interface, and 2. the list of values is complete with respect to any thinkable and unthinkable future feature. @@ -326,7 +326,8 @@ delivery_method: description: Extensible enum. The chosen delivery method of the invoice. ---- -See <<240>> about enum value naming conventions. +See <<240>> about enum value naming conventions – these apply here too. + **Important**: @@ -341,12 +342,16 @@ See <<240>> about enum value naming conventions. the `examples` list, see <<107>>. (Note that the last 3 bullet points do not apply for uses of `examples` _without_ the - "Extensible enum." prefix in the description – here any value needs - to be expected.) + "Extensible enum." prefix in the description – here any value fitting the rest + of the schema needs to be expected.) + +Note that the plural `examples` on schemas was only introduced with OpenAPI 3.1 (with an update of the JSON schema version referenced). +APIs defined with older OpenAPI versions can't use this format, and instead need to use +the {x-extensible-enum} described below↓. -=== Historic Note +=== Historic Note: `x-extensible-enum` -Previously (until May 2025), this guideline recommended our own proprietary +Previously (until October 2025), this guideline recommended our own proprietary {x-extensible-enum} JSON schema extension here, with a similar semantic: > This is the *complete* list of values *currently* possible, but consumers must be @@ -376,6 +381,6 @@ This rule originated in the time before JSON schema and OpenAPI schema had the plural `examples` property (OpenAPI schema had singular `example`, JSON schema had neither). -We also thought we could have some validations (e.g. by our event bus Nakadi), -but the Nakadi team instead decided to not validate `x-extensible-enum`, -and even reject it in event type schema definitions. +We (the API guild) also thought we could have some validations (e.g. by our +event bus Nakadi), but the Nakadi team instead decided to not validate +`x-extensible-enum`, and even reject it in event type schema definitions. From 84fb411b9f15d1bd09efaaf4c84cb5e8111f98be Mon Sep 17 00:00:00 2001 From: Paul Ebermann Date: Wed, 29 Oct 2025 15:14:15 +0100 Subject: [PATCH 04/10] apply a few editorial suggestions --- chapters/compatibility.adoc | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 49292da3..08e4edd0 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -124,10 +124,10 @@ Service clients should apply the robustness principle: http://martinfowler.com/bliki/TolerantReader.html["TolerantReader"] post), i.e. ignore new fields but do not eliminate them from payload if needed for subsequent {PUT} requests. -** Be prepared that "extensible enum" return parameters (see <<112, rule 112>>) +** Be prepared that <<112, extensible enum>> return parameters may deliver new values; either be agnostic or provide default behavior for unknown values, and - do not eliminate them if needed for subsequent {PUT} requests. + do not eliminate them when passed to subsequent {PUT} requests. (This means you cannot simply implement it by using a limited enumeration type like a Java `enum`.) ** Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions. Note also, that status codes are extensible. Default handling is @@ -351,19 +351,12 @@ the {x-extensible-enum} described below↓. === Historic Note: `x-extensible-enum` -Previously (until October 2025), this guideline recommended our own proprietary -{x-extensible-enum} JSON schema extension here, with a similar semantic: +Previously (until October 2025), this guideline recommended a proprietary +{x-extensible-enum} JSON schema extension, with a similar semantic: > This is the *complete* list of values *currently* possible, but consumers must be prepared for other values in the future. -Until all existing APIs using this have been updated, API providers and consumers -still need to follow the rules under "Important" above for these cases. - -This completeness semantic would in theory allow some validation by -intermediaries (but that was rarely implemented). -It was visible in a few tools (e.g. Swagger UI), but ignored by most others. - An open-ended list of values was specified as follows: [source,yaml] @@ -377,10 +370,18 @@ delivery_methods: description: The chosen delivery method of the invoice. ---- +Until all existing APIs using this have been updated, API providers and consumers +still need to follow the rules under "Important" above for these cases. + This rule originated in the time before JSON schema and OpenAPI schema had the plural `examples` property (OpenAPI schema had singular `example`, JSON schema had neither). -We (the API guild) also thought we could have some validations (e.g. by our -event bus Nakadi), but the Nakadi team instead decided to not validate -`x-extensible-enum`, and even reject it in event type schema definitions. +The completeness semantic would in theory allow some validation by +intermediaries (but that was rarely implemented). +It was visible in a few tools (e.g. Swagger UI), but ignored by most others. + +After the team responsible for Nakadi (our Zalando-internal event bus) decided +to not validate x-extensible-enum, but instead even reject it in event type +definitions, we (the API guideline maintainers) revisited this rule to +recommend `examples` instead. From 4b1b823692717c4c47cfeeb4e3dc5e0137ab6c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=C5=ADlo=20Ebermann?= Date: Wed, 12 Nov 2025 17:23:32 +0100 Subject: [PATCH 05/10] Apply suggestions from code review editorial: comma, word order Co-authored-by: Thomas Frauenstein --- chapters/compatibility.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 08e4edd0..2a63176c 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -351,8 +351,8 @@ the {x-extensible-enum} described below↓. === Historic Note: `x-extensible-enum` -Previously (until October 2025), this guideline recommended a proprietary -{x-extensible-enum} JSON schema extension, with a similar semantic: +Previously (until October 2025) this guideline recommended a proprietary +JSON schema extension {x-extensible-enum} with a similar semantic: > This is the *complete* list of values *currently* possible, but consumers must be prepared for other values in the future. From f0fd38ed74ac0231f7d65eb9339fd1dd685e703f Mon Sep 17 00:00:00 2001 From: Paul Ebermann Date: Wed, 12 Nov 2025 17:36:50 +0100 Subject: [PATCH 06/10] remove semantic replication. --- chapters/compatibility.adoc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 2a63176c..5293d336 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -352,12 +352,8 @@ the {x-extensible-enum} described below↓. === Historic Note: `x-extensible-enum` Previously (until October 2025) this guideline recommended a proprietary -JSON schema extension {x-extensible-enum} with a similar semantic: - -> This is the *complete* list of values *currently* possible, but consumers must be -prepared for other values in the future. - -An open-ended list of values was specified as follows: +JSON schema extension {x-extensible-enum} with the same semantic. +The example above would be specified as follows: [source,yaml] ---- From aafbe0479d989ec70d643c285d287309d92bc30d Mon Sep 17 00:00:00 2001 From: Paul Ebermann Date: Wed, 12 Nov 2025 17:40:27 +0100 Subject: [PATCH 07/10] split bullet point --- chapters/compatibility.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 5293d336..7a06d2c4 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -336,8 +336,9 @@ See <<240>> about enum value naming conventions – these apply here too. fallback / default behavior for unknown new values, see <<108>>. * API owners must take care to extend these extensible enums in a compatible way, i.e. not changing the semantics of already existing / documented values. -* API implementations should validate the values provided with the input payload and only accept - values listed in `examples`. The list should not be reduced for inputs (that would be an incompatible change). +* API implementations should validate the values provided with the input payload + and only accept values listed in `examples`. +* The list should not be reduced for inputs (that would be an incompatible change). * Before additional values are accepted or returned, API owners should update the API description and extend the `examples` list, see <<107>>. From 33f3769d6d2421140278d17a46568726218bb9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=C5=ADlo=20Ebermann?= Date: Wed, 12 Nov 2025 17:42:05 +0100 Subject: [PATCH 08/10] reword "rules apply for legacy" Co-authored-by: Thomas Frauenstein --- chapters/compatibility.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 7a06d2c4..2ebc9f50 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -367,8 +367,7 @@ delivery_methods: description: The chosen delivery method of the invoice. ---- -Until all existing APIs using this have been updated, API providers and consumers -still need to follow the rules under "Important" above for these cases. +The "important" rules above apply in an analog way for API providers and consumers using {x-extensible-enum}. This rule originated in the time before JSON schema and OpenAPI schema had the plural `examples` property (OpenAPI schema had singular `example`, From a7ac21dbf1811d77fe5e6db3981318398705139a Mon Sep 17 00:00:00 2001 From: Thomas Frauenstein Date: Wed, 12 Nov 2025 18:44:54 +0100 Subject: [PATCH 09/10] Apply suggestion from @ePaul MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paŭlo Ebermann --- chapters/compatibility.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 2ebc9f50..bc3a85cd 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -309,7 +309,7 @@ To avoid these issues, we recommend to use `enum` only if In all other cases, where additional values are imaginable our recommendation is this: * Use `examples` with the list of (currently known) values -* Put "Extensible enum" in the description. +* Add `[Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112).` as a standard prefix to the description. This indicates that only the listed values are currently possible, but consumers need to be aware that this list can be extended without notice From 322ed8bae225a0fc894d9512c2a0048e0e966fd0 Mon Sep 17 00:00:00 2001 From: Thomas Frauenstein Date: Wed, 12 Nov 2025 18:53:30 +0100 Subject: [PATCH 10/10] Update chapters/compatibility.adoc --- chapters/compatibility.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index bc3a85cd..0cdc3ae9 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -323,7 +323,7 @@ delivery_method: - PARCEL - LETTER - EMAIL - description: Extensible enum. The chosen delivery method of the invoice. + description: [Extensible enum](https://opensource.zalando.com/restful-api-guidelines/#112). The chosen delivery method of the invoice. ---- See <<240>> about enum value naming conventions – these apply here too.