Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion ipa/general/0111.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ For resources where all fields are server-owned, see

:::

### Optional Fields with Server Defaults

Assigning a server-side default in place of a client-omitted value during
resource creation is an anti-pattern because it creates hybrid ownership — the
field is client-owned but the server is effectively choosing a value. This makes
it harder for state-driven clients and downstream tooling to tell whether a
returned value was client-set or server-filled.

- Optional request fields **must** be returned by the server with the same value
(or absence of value) the client provided.
- Legacy fields that retain this anti-pattern **must** be annotated per
[IPA-131](0131.mdx) so declarative tooling can handle the hybrid ownership.
- Booleans are exempt: optional booleans **must** default to `false` per
[Boolean Values](#boolean-values), avoiding a tri-state (true / false / unset)
that is hard for both API producers and consumers to reason about.
- Booleans **must not** be annotated with the extension described in
[IPA-131](0131.mdx).

### Effective Values

There are instances where a service will allocate, generate, or calculate a
Expand Down Expand Up @@ -73,7 +91,7 @@ the server may scale it up or down based on load.

### Boolean Values

- Booleans **should** default to `false`
- Optional boolean fields **must** default to `false`
- Many serialization systems won’t distinguish `false` values from unset
values which introduces complications when a boolean defaults to `true`

Expand Down
47 changes: 43 additions & 4 deletions ipa/general/0124.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,49 @@ reduced when clients need to modify the lists.
- A good rule of thumb is 100 elements
- If repeated data has the chance of being too large, the API **should** use a
sub-resource instead
- [Client-owned](0111.mdx#single-owner-fields) repeated fields **should** be
respected by the server
- The server **should not** modify the order of elements or remove duplicates,
without explicit documentation that the server will do so
- [Client-owned](0111.mdx#single-owner-fields) repeated fields **must** be
respected by the server.
- The server **must not** modify the order of elements or remove duplicates,
unless the field is explicitly declared as a set (see List vs Set below).

### List vs Set

Every array property **must** be classified as either a **list** or a **set** so
that clients and downstream tooling can reason correctly about order and
equality.

- **List** — order is meaningful. Elements may repeat. This is the default
classification when the semantic is not explicitly declared.
- **Set** — order is not meaningful. Elements are unique (no duplicates).

API producers **must** explicitly declare set-like arrays using the
`x-xgen-array-semantic` extension (see [IPA-131](0131.mdx)) in the OpenAPI spec:

```yaml
tags:
type: array
x-xgen-array-semantic: set
items:
type: string
```

If the extension is omitted, the array is treated as a list.

#### List (ordered) guarantees

- For [client-owned](0111.mdx#single-owner-fields) lists, the server **must**
preserve the client-provided order of elements on round-trip (see above).
- For all lists (client-owned or server-owned), the server **must** return
elements in a deterministic, stable order across repeated GET responses for
the same resource state. Unstable ordering causes false drift in declarative
clients.

#### Set (unordered) guarantees

- The server **must not** return duplicate elements.
- The server **may** return elements in any order; the order of elements in a
response is not part of the resource state.
- Clients **must** treat equality without regard to order.

## Update Strategy

Expand Down
97 changes: 97 additions & 0 deletions ipa/general/0131.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
id: 131
state: experimental
---

# IPA-131: Declarative Tooling Extensions

Declarative clients such as Infrastructure-as-Code providers consume the OpenAPI
spec to generate typed resources. Some design decisions that affect how a field
should be handled by these clients are not expressible through the standard
OpenAPI schema vocabulary — for example, whether an array is ordered or whether
the server populates an optional field when the client omits it. Encoding these
decisions as OpenAPI extensions under the `x-xgen-*` namespace lets declarative
clients react to them mechanically and removes the need for per-resource manual
overrides. The `x-xgen-*` namespace is specific to MongoDB API tooling. The
registry is intentionally narrow: an extension is added when it expresses a
design decision that declarative clients need to react to mechanically and that
the standard OpenAPI vocabulary cannot express.

## Guidance

- Extensions that affect the behavior of declarative or autogenerated clients
**must** live under the `x-xgen-*` prefix. Unregistered `x-xgen-*` keys are
not guaranteed to be honored by downstream tooling.
- Extensions **should not** duplicate information already expressible through
standard OpenAPI keywords (e.g. `readOnly`, `writeOnly`, `default`). They are
intended for cases where existing OpenAPI vocabulary is insufficient to convey
the semantic.

## Registered Extensions

### `x-xgen-array-semantic`

- **Purpose.** Distinguishes arrays whose element order is part of the resource
state (lists) from arrays where it is not (sets). OpenAPI's `type: array` does
not carry this semantic.
- **Referenced by.** [IPA-124 — Repeated Fields](0124.mdx#list-vs-set).
- **Placement.** On any array-typed property.
- **Allowed values.** `list` | `set`. When the extension is absent, the array is
treated as a list.
- **Example:**

```yaml
tags:
type: array
x-xgen-array-semantic: set
items:
type: string
```

### `x-xgen-server-computed-when-client-omitted`

- **Purpose.** Annotate legacy fields that cannot be remediated away from the
optional-plus-server-default pattern, so that declarative tooling can
reconcile the hybrid ownership. Without this marker, tooling sees the
server-assigned value on the response and interprets the field as
client-owned, causing spurious drift. The extension is strictly an escape
hatch for legacy APIs; new APIs are expected to follow one of the clean
ownership patterns documented in IPA-111.
- **Referenced by.**
[IPA-111 — Optional Fields with Server Defaults](0111.mdx#optional-fields-with-server-defaults).
- **Placement.** On any property that exhibits the pattern. **Must not** be
applied to boolean properties — booleans are governed by
[IPA-111 Boolean Values](0111.mdx#boolean-values), which requires optional
booleans to default to `false`.
- **Allowed values.** Boolean. `true` declares the field as subject to the
optional-plus-server-default pattern. Absence of the key is equivalent to
`false`.
- **Example:**

```yaml
retentionDays:
type: integer
x-xgen-server-computed-when-client-omitted: true
```

### `x-xgen-server-computed-immutable`

- **Purpose.** Annotate server-owned fields whose value is computed by the
server at resource creation and does not change on subsequent reads for the
same resource state. The signal allows declarative tooling to treat the value
as stable (safe to cache, no drift polling required) and to omit the field
from Update request bodies.
- **Referenced by.** None — pure tooling hint. The underlying pattern is already
expressible via `readOnly: true`, but `readOnly` alone does not convey
stability across reads.
- **Placement.** On any `readOnly` property that exhibits the pattern.
- **Allowed values.** Boolean. `true` declares the field as server-computed at
creation and stable thereafter. Absence of the key is equivalent to `false`.
- **Example:**

```yaml
resourceFingerprint:
type: string
readOnly: true
x-xgen-server-computed-immutable: true
```