diff --git a/ipa/general/0116.mdx b/ipa/general/0116.mdx
index 3774f12..d09d817 100644
--- a/ipa/general/0116.mdx
+++ b/ipa/general/0116.mdx
@@ -13,58 +13,1462 @@ change.
## Guidance
-- Existing client code written against a revision **must not** be broken by
- minor changes to the service
-- API producers **must** consider syntax breaking changes
-- API producers **must** consider semantic breaking changes
-- API producers **must** not break backwards compatibility with a change without
- issuing a new API version
-- API producers **should** leverage extensible design where applicable
-- New functionality **may** be added to existing versions, granted there are no
- incompatibilities introduced
+
+
+
+
+Existing client code written against a published version **must not** be broken
+by minor changes to the service.
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ nickname: # newly added optional field
+ type: string
+```
+
+
+ A new optional response field is additive. A client that ignores unknown
+ fields keeps working unchanged, so the existing contract still holds.
+
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ # name was removed from the response
+```
+
+
+ Removing a field a client already reads breaks code that depends on it, even
+ though the change looks small from the producer side.
+
+
+
+
+
+
+ Take the previously published version of the spec and the proposed new
+ revision of the same version.
+
+
+ Diff the two: for every operation, compare request and response schemas,
+ parameters, status codes, and media types.
+
+
+ Classify each difference as additive (new optional element) or
+ subtractive/altering (removed, renamed, retyped, newly required).
+
+
+ Report any subtractive or altering change as a break of the existing
+ contract.
+
+
+
+
+
+
+
+
+
+An API producer **must** consider syntax breaking changes before publishing a
+revision.
+
+
+
+
+
+```yaml
+# Two versions side by side; the v2 path adds an optional query parameter only.
+paths:
+ /orders:
+ get:
+ parameters:
+ - name: status
+ in: query
+ required: false
+ schema:
+ type: string
+```
+
+
+ The revision was reviewed against the prior shape and limited to an additive,
+ optional parameter, so no caller's existing request becomes invalid.
+
+
+
+
+
+
+```yaml
+paths:
+ /orders:
+ get:
+ parameters:
+ - name: status
+ in: query
+ required: true # was optional in the published version
+ schema:
+ type: string
+```
+
+
+ The revision shipped without comparing against the published shape. Promoting
+ an existing parameter to required rejects requests that omit it.
+
+
+
+
+
+
+ Locate the previously published version and the proposed revision of the
+ same major version.
+
+
+ For each operation, inspect the wire-level shape: parameters, request body
+ schema, response schema, status codes, media types, and required flags.
+
+
+ Confirm a documented comparison exists and that every shape difference was
+ classified as breaking or non-breaking.
+
+
+ Report any revision that altered the wire shape without that comparison
+ having been done.
+
+
+
+
+
+
+
+
+
+An API producer **must** consider semantic breaking changes before publishing a
+revision, even when the wire shape is unchanged.
+
+
+
+
+
+```yaml
+paths:
+ /jobs/{jobId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [PENDING, RUNNING, DONE, FAILED]
+ description: >-
+ Lifecycle state. The set of values and the meaning of each
+ value is stable across this version.
+```
+
+
+ The meaning of each value is fixed for the life of the version, so a client
+ that branches on `status` keeps interpreting responses correctly.
+
+
+
+
+
+
+```yaml
+paths:
+ /jobs/{jobId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [PENDING, RUNNING, DONE, FAILED]
+ description: >-
+ DONE now means "accepted for processing" rather than
+ "completed". The shape is identical to the prior version.
+```
+
+
+ The shape is unchanged but the meaning of `DONE` flipped, so existing logic
+ that treats `DONE` as finished now misbehaves. Detecting this requires
+ understanding server behavior, not just the spec.
+
+
+
+
+
+
+ Identify operations whose wire shape is unchanged between the published
+ version and the revision.
+
+
+ For each, read the implementation and changelog to find changed behavior:
+ altered field semantics, changed defaults applied server-side, reordered or
+ filtered results, changed side effects.
+
+
+ Assess whether existing client logic that relied on the prior behavior would
+ now produce wrong outcomes.
+
+
+ Report any behavioral change that alters the meaning of a response without a
+ corresponding new version.
+
+
+
+
+
+
+
+
+
+Backwards compatibility **must not** be broken by a change without issuing a new
+API version.
+
+
+
+
+
+```yaml
+# A breaking rename is delivered under a new version, leaving the old one intact.
+paths:
+ /v1/users/{userId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserV1"
+ /v2/users/{userId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserV2"
+```
+
+
+ The incompatible shape lives in a new version. Callers on the old version are
+ untouched and migrate on their own schedule.
+
+
+
+
+
+
+```yaml
+# The published v1 response shape is changed in place.
+paths:
+ /v1/users/{userId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UserV2" # breaking change in v1
+```
+
+
+ An incompatible change was applied to a published version. Every caller breaks
+ at once with no opt-in and no migration path.
+
+
+
+
+
+
+ Diff each published version against its proposed revision and collect every
+ breaking difference.
+
+
+ For each breaking difference, confirm it is introduced under a new version
+ identifier and that the prior version retains its old behavior.
+
+
+ Report any breaking change applied in place to an already published version.
+
+
+
+
+
+
+
+
+
+An API **should** leverage extensible design where applicable, so that future
+additions do not force a breaking change.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Notification:
+ type: object
+ properties:
+ channel:
+ type: string
+ enum: [EMAIL, SMS] # open-ended set, callers tolerate new values
+ details:
+ type: object
+ additionalProperties: true
+```
+
+
+ Modeling `channel` as an extensible enum and `details` as an open object lets
+ new channels and fields land additively, without an incompatible change.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Notification:
+ type: object
+ properties:
+ emailOnly:
+ type: boolean # bakes in a single channel
+ additionalProperties: false
+ required: [emailOnly]
+```
+
+
+ A boolean and a closed object hard-code today's behavior. Supporting another
+ channel later requires removing or reshaping a required field, which is
+ breaking.
+
+
+
+
+
+
+ Identify schema points where future growth is likely: status and type
+ fields, mode flags, and containers for variable detail.
+
+
+ Check whether those points are modeled extensibly: enums that callers can
+ tolerate adding to, objects rather than booleans for concepts that may grow,
+ and nested objects for groups of related fields.
+
+
+ Flag designs where a foreseeable addition would require a breaking change
+ because the current model is closed.
+
+
+
+
+
+
+
+
+
+New functionality **may** be added to existing versions, provided no
+incompatibilities are introduced.
+
+
+
+
### Syntax Breaking Changes
Changes considered breaking include:
-- New required fields **must not** be added to an existing version of an API
-- Optional fields **must not** be removed from the request or response of an
- existing version of an API version
-- Guaranteed output fields **must not** be omitted (become nullable)
-- Existing API components **must not** be renamed or removed from an existing
- version of an API
-- Default values provided for a field **must not** change
-- Changes to path, query request headers or body content **must not** be made
-- Field names **must not** change or be removed
-- Field types **must not** be changed
-- Status codes **must not** change
-- Operation Ids **must not** be updated or deleted
-- Operation Tags **must not** be updated or deleted
-- HTTP Verbs **must not** be changed or deleted
-- Media type **must not** be changed or deleted
-- Existing resources **must not** be moved to a new URI
-- Existing resources **must not** be removed without previously being marked as
- sunsetting
-- Existing options within enum fields **must not** be changed or removed
+
+
+
+
+New required fields **must not** be added to an existing version of an API.
+
+
+
+
+
+```yaml
+# Added field is optional in the new revision.
+components:
+ schemas:
+ CreateOrderRequest:
+ type: object
+ properties:
+ items:
+ type: array
+ couponCode: # added, not required
+ type: string
+ required: [items]
+```
+
+
+ An added optional field leaves prior requests valid, because omitting it is
+ still accepted.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ CreateOrderRequest:
+ type: object
+ properties:
+ items:
+ type: array
+ couponCode:
+ type: string
+ required: [items, couponCode] # newly required
+```
+
+
+ Marking a new field required rejects every existing request that omits it.
+
+
+
+
+
+
+ For each request schema, diff the `required` array between the published
+ version and the revision.
+
+
+ Flag any entry present in the revision's `required` list but not in the
+ published one.
+
+
+ Report each newly required request field as a breaking change.
+
+
+
+
+
+
+
+
+
+Optional fields **must not** be removed from the request or response of an
+existing API version.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ UserResponse:
+ type: object
+ properties:
+ id:
+ type: string
+ nickname: # optional field retained
+ type: string
+```
+
+
+ Keeping the optional field preserves the shape callers already read and send.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ UserResponse:
+ type: object
+ properties:
+ id:
+ type: string
+ # nickname was deleted
+```
+
+
+ Deleting an optional field breaks callers that read it from responses or set
+ it in requests.
+
+
+
+
+
+
+ For each request and response schema, list its declared properties in the
+ published version and in the revision.
+
+
+ Flag any property present in the published version but absent from the
+ revision.
+
+
+ Report each removed property as a breaking change.
+
+
+
+
+
+
+
+
+
+Guaranteed output fields **must not** be omitted or made nullable in an existing
+version.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ InvoiceResponse:
+ type: object
+ properties:
+ total:
+ type: number
+ required: [total] # always returned, never null
+```
+
+
+ A field guaranteed in the published version stays required and non-nullable,
+ so callers can keep reading it unconditionally.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ InvoiceResponse:
+ type: object
+ properties:
+ total:
+ type: number
+ nullable: true # was always present before
+```
+
+
+ Allowing a previously guaranteed field to be null forces callers to handle a
+ case that never used to occur, breaking code that assumed a value.
+
+
+
+
+
+
+ For each response schema, identify fields that were guaranteed in the
+ published version (present and non-nullable, typically in `required`).
+
+
+ In the revision, confirm each such field is still present, required, and not
+ marked `nullable`.
+
+
+ Report any guaranteed output field that became optional, nullable, or
+ absent.
+
+
+
+
+
+
+
+
+
+Existing API components **must not** be renamed or removed from an existing
+version of an API.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ UserResponse: # name unchanged across the version
+ type: object
+ properties:
+ id:
+ type: string
+```
+
+
+ Stable component names keep `$ref` targets and generated client types intact.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ UserPayload: # renamed from UserResponse
+ type: object
+ properties:
+ id:
+ type: string
+```
+
+
+ Renaming a component breaks every `$ref` to the old name and the generated
+ types clients compiled against.
+
+
+
+
+
+
+ Enumerate the keys under `components` (schemas, parameters, responses, and
+ so on) in the published version.
+
+
+ Confirm each key still exists under the same name in the revision.
+
+
+ Report any component key that was renamed or removed.
+
+
+
+
+
+
+
+
+
+Default values provided for a field **must not** change.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ ListProjectsQuery:
+ type: object
+ properties:
+ pageSize:
+ type: integer
+ default: 100 # unchanged from the published version
+```
+
+
+ A stable default keeps the behavior of requests that omit the field identical
+ to before.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ ListProjectsQuery:
+ type: object
+ properties:
+ pageSize:
+ type: integer
+ default: 20 # was 100
+```
+
+
+ Changing the default silently alters the result of every request that relied
+ on the old default.
+
+
+
+
+
+
+ For each schema property, read its `default` in the published version and
+ the revision.
+
+
+ Flag any property whose `default` value differs between the two versions.
+
+
+ Report each changed default as a breaking change.
+
+
+
+
+
+
+
+
+
+Changes to path, query, request headers, or body content **must not** be made to
+an existing version.
+
+
+
+
+
+```yaml
+paths:
+ /projects/{projectId}/tasks:
+ post:
+ parameters:
+ - name: dryRun
+ in: query
+ required: false # additive only
+ schema:
+ type: boolean
+```
+
+
+ Only an optional query parameter is added; the path and existing body and
+ headers are untouched, so existing requests still succeed.
+
+
+
+
+
+
+```yaml
+paths:
+ /projects/{projectId}/work-items: # path renamed from /tasks
+ post:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required: [priority] # body now demands a new field
+```
+
+
+ Renaming the path and demanding a new body field invalidates requests written
+ against the published version.
+
+
+
+
+
+
+ For each operation, compare the path template, query parameters, request
+ headers, and request body schema between versions.
+
+
+ Classify each difference as additive-and-optional or as an alteration to an
+ existing element.
+
+
+ Report any non-additive change to path, query, request headers, or body.
+
+
+
+
+
+
+
+
+
+Field names **must not** change or be removed.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Order:
+ type: object
+ properties:
+ createdAt: # name preserved
+ type: string
+ format: date-time
+```
+
+
+ A stable field name keeps serialization and deserialization working for
+ existing callers.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Order:
+ type: object
+ properties:
+ creationTime: # renamed from createdAt
+ type: string
+ format: date-time
+```
+
+
+ Renaming a field means existing callers send and read a name the server no
+ longer recognizes.
+
+
+
+
+
+
+ For each schema, list its property names in the published version and the
+ revision.
+
+
+ Flag any name that disappeared or whose value moved under a differently
+ named property.
+
+ Report each renamed or removed field.
+
+
+
+
+
+
+
+
+Field types **must not** be changed.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Product:
+ type: object
+ properties:
+ price:
+ type: number # type unchanged
+```
+
+
+ Holding the declared type steady keeps the value parseable by clients
+ generated against the published version.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Product:
+ type: object
+ properties:
+ price:
+ type: string # was number
+```
+
+
+ Changing the type breaks typed clients and any caller that parsed the value as
+ the original type.
+
+
+
+
+
+
+ For each property present in both versions, read its `type` and `format`.
+
+
+ Flag any property whose `type` or `format` differs between the versions.
+
+ Report each changed field type.
+
+
+
+
+
+
+
+
+Status codes **must not** change.
+
+
+
+
+
+```yaml
+paths:
+ /orders/{orderId}:
+ get:
+ responses:
+ "200": # still returned on success
+ description: OK
+ "404":
+ description: Not found
+```
+
+
+ A stable set of status codes lets callers keep branching on the same outcomes.
+
+
+
+
+
+
+```yaml
+paths:
+ /orders/{orderId}:
+ get:
+ responses:
+ "204": # success code changed from 200
+ description: No content
+ "404":
+ description: Not found
+```
+
+
+ Changing the success status code breaks callers that check for the original
+ code.
+
+
+
+
+
+
+ For each operation, list the response status codes declared in the published
+ version and the revision.
+
+
+ Flag any code that was removed or whose meaning was reassigned (a former
+ success code dropped in favor of another).
+
+ Report each altered or removed status code.
+
+
+
+
+
+
+
+
+Operation ids **must not** be updated or deleted.
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ operationId: getUser # unchanged
+```
+
+
+ A stable `operationId` keeps generated method names and tooling references
+ intact.
+
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ operationId: fetchUser # renamed from getUser
+```
+
+
+ Renaming an `operationId` breaks generated SDK method names and any code that
+ calls them.
+
+
+
+
+
+
+ Collect every `operationId` in the published version.
+
+
+ Confirm each one still exists, unchanged, on the same operation in the
+ revision.
+
+
+ Report any `operationId` that was renamed or deleted.
+
+
+
+
+
+
+
+
+
+Operation tags **must not** be updated or deleted.
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ operationId: getUser
+ tags: [Users] # unchanged
+```
+
+
+ Stable tags keep generated client grouping and documentation structure
+ consistent for callers.
+
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get:
+ operationId: getUser
+ tags: [Accounts] # changed from Users
+```
+
+
+ Changing a tag reorganizes generated SDK namespaces and documentation that
+ callers navigate by.
+
+
+
+
+
+
+ For each operation, list its `tags` in the published version and the
+ revision.
+
+
+ Flag any tag that was renamed or removed from an existing operation.
+
+ Report each altered or deleted operation tag.
+
+
+
+
+
+
+
+
+HTTP verbs **must not** be changed or deleted.
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ get: # verb preserved
+ operationId: getUser
+```
+
+
+ Keeping the verb on a path means existing requests reach the same operation.
+
+
+
+
+
+
+```yaml
+paths:
+ /users/{userId}:
+ post: # was get
+ operationId: getUser
+```
+
+
+ Moving an operation to a different verb makes every request that used the old
+ verb fail to route.
+
+
+
+
+
+
+ For each path, list the HTTP methods defined in the published version.
+
+
+ Confirm each method still exists on the same path in the revision.
+
+
+ Report any verb that was removed or whose operation moved to a different
+ verb.
+
+
+
+
+
+
+
+
+
+Media types **must not** be changed or deleted.
+
+
+
+
+
+```yaml
+paths:
+ /reports/{reportId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/json: # media type preserved
+ schema:
+ type: object
+```
+
+
+ A stable media type keeps content negotiation and client parsing working as
+ before.
+
+
+
+
+
+
+```yaml
+paths:
+ /reports/{reportId}:
+ get:
+ responses:
+ "200":
+ content:
+ application/xml: # was application/json
+ schema:
+ type: object
+```
+
+
+ Changing the media type breaks clients that send the old `Accept` header or
+ parse the old format.
+
+
+
+
+
+
+ For each request body and response, list the media type keys under `content`
+ in the published version.
+
+
+ Confirm each media type still exists in the revision.
+
+
+ Report any media type that was changed or removed.
+
+
+
+
+
+
+
+
+
+Existing resources **must not** be moved to a new URI.
+
+
+
+
+
+```yaml
+paths:
+ /projects/{projectId}/tasks/{taskId}: # path preserved
+ get:
+ operationId: getTask
+```
+
+
+ Keeping the URI stable means existing client requests still resolve to the
+ same resource.
+
+
+
+
+
+
+```yaml
+paths:
+ /tasks/{taskId}: # moved out from under /projects/{projectId}
+ get:
+ operationId: getTask
+```
+
+
+ Moving a resource to a new URI makes every request to the old path return a
+ not-found error.
+
+
+
+
+
+
+ Enumerate every path template in the published version.
+
+
+ Confirm each path still exists in the revision (matching the same operation
+ by `operationId`).
+
+
+ Report any operation whose path template changed.
+
+
+
+
+
+
+
+
+
+Existing resources **must not** be removed without previously being marked as
+sunsetting.
+
+
+
+
+
+```yaml
+paths:
+ /orders/{orderId}:
+ get:
+ deprecated: true
+ responses:
+ "200":
+ headers:
+ Sunset: # announced before removal
+ schema:
+ type: string
+ format: date-time
+```
+
+
+ The resource is marked deprecated and announces a sunset date, giving callers
+ a defined window to migrate before it is removed.
+
+
+
+
+
+
+```yaml
+paths:
+ /invoices/{invoiceId}:
+ # the entire path was deleted in this revision with no prior sunset notice
+ get:
+ operationId: getInvoice
+```
+
+
+ Deleting a live resource that was never marked sunsetting breaks callers with
+ no warning and no migration window.
+
+
+
+
+
+
+ Identify paths present in the published version but absent from the
+ revision.
+
+
+ For each removed path, check the prior version's history for a `deprecated:
+ true` marking and a `Sunset` header announced ahead of removal.
+
+
+ Report any resource removed without a prior sunset announcement.
+
+
+
+
+
+
+
+
+
+Existing options within enum fields **must not** be changed or removed.
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Subscription:
+ type: object
+ properties:
+ tier:
+ type: string
+ enum: [FREE, PRO, ENTERPRISE] # existing options retained
+```
+
+
+ Keeping existing enum values lets callers that send or branch on them keep
+ working.
+
+
+
+
+
+
+```yaml
+components:
+ schemas:
+ Subscription:
+ type: object
+ properties:
+ tier:
+ type: string
+ enum: [FREE, PREMIUM] # PRO renamed, ENTERPRISE removed
+```
+
+
+ Renaming or dropping an enum value rejects requests carrying the old value and
+ breaks callers that match on it in responses.
+
+
+
+
+
+
+ For each enum field, list its values in the published version and the
+ revision.
+
+
+ Flag any value present in the published version but missing from the
+ revision (removed or renamed).
+
+
+ Report each removed or changed enum option. New options added alongside the
+ existing ones are allowed.
+
+
+
+
+
+
+
+
Changes considered non-breaking include:
-- New output fields **may** be added to existing versions
-- Optional input parameters **may** be added to existing versions
-- Updates/Changes to underlying logic that results in different results
- WITHOUT affecting the shape of the response **may** be added to existing
- versions
-- Changes to unstructured human-readable string values **may**
- be added to existing versions (Exceptions for machine codes or formatted
- strings i.e., dates)
-- Response headers **may** be added to existing versions
-- Changes to a resources authorization **may** be added to existing versions
-- New options **may** be added to existing enums
-- Input and output fields **may** be marked as deprecated in existing versions
+
+
+
+
+New output fields **may** be added to existing versions.
+
+
+
+
+
+Optional input parameters **may** be added to existing versions.
+
+
+
+
+
+Updates to underlying logic that produce different results without affecting the
+shape of the response **may** be added to existing versions.
+
+
+
+
+
+Changes to unstructured, human-readable string values **may** be added to
+existing versions, except for machine codes or formatted strings such as dates.
+
+
+
+
+
+
+
+Changes to a resource's authorization **may** be added to existing versions.
+
+
+
+
+
+New options **may** be added to existing enums.
+
+
+
+
+
+Input and output fields **may** be marked as deprecated in existing versions.
+
+
+
+
### Semantic Breaking Changes
-Semantic-based breaking changes capture situations in which behavior of a
+Semantic-based breaking changes capture situations in which the behavior of a
resource is changing without necessarily including a syntax-based breaking
change that would produce a top-level error. These situations often require
intensive data analysis and discovery to fully understand the extent of customer