Kubernetes CRD Design Review
Perform a deterministic design + contract review for Kubernetes CRDs (the generated CRD YAML is the compiled API contract).
Inputs
Accept at least one of:
- CRD YAML (full manifest) or a CRD diff
- A short description of intended API semantics and controller behavior
If key info is missing, ask for it before concluding compatibility/migration:
- Whether a controller exists, what it owns, and whether it writes
status
- Whether this is a new API or a change to an existing API
- Served versions, storage version, and existing clients
- Whether objects already exist in clusters (migration needed?)
- Any GitOps/SSA constraints (patch strategy, desired stable identities)
Workflow (always follow this order)
1) Identify scope
- Identify
group, kind(s), version(s), and whether this is new vs change.
- Identify controller existence and ownership boundaries.
- If reviewing Go types, confirm which generated CRD YAML(s) correspond to them.
2) Contract integrity checks (spec/status + controller operability)
- Spec vs status boundary
spec = user intent / desired state.
status = controller-observed state.
- Flag lifecycle/state-machine fields in
spec if the controller owns transitions.
- Require
subresources.status when a controller exists and writes status
- Conditions +
observedGeneration
- Recommend
status.conditions and status.observedGeneration (Kubernetes conventions; critical for tooling/GitOps correctness).
- Model Conditions as a map keyed by
type, not a chronological list: prefer schema markers (x-kubernetes-list-type: map with x-kubernetes-list-map-keys: [type]) for SSA/GitOps safety.
- Use state-style Condition types (adjectives/past tense:
Ready, Degraded, Succeeded; avoid transition names or phases for new APIs).
- Include one high-signal summary condition (
Ready for long-running; Succeeded for bounded execution).
- For GitOps/status-tool compatibility, check the kstatus shape:
Ready as aggregate current-state signal, Reconciling=True for active progress, Stalled=True for blocked/broken progress, and current observedGeneration.
- Flag
status.phase / status.state when it is the primary lifecycle contract for a new CRD; conditions should be the primary machine-readable readiness contract.
- Ensure each Condition has semantic
True/False/Unknown values with consistent meaning.
- Remember:
status updates via /status subresource use separate RBAC.
See ./references/conditions-and-status.md for deeper semantics and ./references/kstatus-readiness.md for kstatus compatibility.
3) Schema correctness & validation (prevent invalid stored objects)
Use a 3-step validation hierarchy: always exhaust each level before moving to the next.
Step 1: Schema validation (required)
Review the OpenAPI v3 schema (prefer the generated CRD YAML/diff):
- Required fields for true invariants
- Enums for constrained strings
- Defaulting and nullable behavior
- Type constraints, patterns, min/max bounds
- Structural schema: ensure
spec.preserveUnknownFields: false for strict validation and automatic version conversion
- Object references & relationships: Classify the relationship before judging its shape:
fixed-kind, bounded multi-kind, generic-object, or field reference. Prefer explicit
fooRef /
fooRefs objects to a bare fooName string, but a fixed-kind reference normally needs only
{name} (and an explicit namespace only when cross-namespace use is intentional).
- Require a non-empty
name inside a reference object. Put group, kind, or resource in the instance only when the user actually selects a target type; do not default a single allowed value merely as speculative tooling metadata.
- For a fixed-kind
{name} reference, ensure the field name and field documentation identify the target kind and scope. A generic navigation tool needs a separately published relationship map; ordinary CRD structural schema does not encode that mapping.
- When a review concerns editor navigation or resource-graph tooling, see
./references/relationship-metadata.md.
- Omit
namespace unless you explicitly allow cross-namespace references.
- Avoid
apiVersion in references; let the controller map versions.
- Keep UID/resourceVersion in
status, not spec, unless there is an exceptional API requirement.
- Treat cross-namespace references as security-sensitive. Prefer same-namespace references; if cross-namespace is intentional, require explicit scope semantics and consider a ReferenceGrant-style producer opt-in.
dependsOn is an advisable (community) deviation from the fooRefs advise. Use it when you explicitly model a dependency graph and your controller implements full DAG semantics (readiness definition, scope rules, cycle detection). See ./references/object-references.md.
- Use
parentRef only for resources your operator directly manages; do not use it to model general relationships.
- See
./references/object-references.md for schema examples and deeper guidance.
Step 2: CEL validation (before webhooks)
When cross-field invariants or complex constraints cannot be expressed with basic OpenAPI rules, use CEL (x-kubernetes-validations):
- Cross-field invalid combinations (e.g., "field A only allowed if field B is set")
- Exactly-one-of constraints
- Numeric range relationships between fields
- Enum dependencies
Best practice: Write minimal, targeted rules with clear error messages. CEL is stateless, auditable, and version-safe—always prefer it to webhooks.
See ./references/validation-and-cel.md for examples.
Step 3: Webhooks (only if Steps 1–2 are insufficient)
Only recommend webhooks when schema and CEL cannot express the constraint. This is a significant operational decision. Always double-check first:
- Can this be expressed with required fields, enums, or patterns?
- Can this be expressed with CEL (stateless, auditable, version-safe)?
- Is the webhook truly necessary, or is the controller solving it better?
- Webhooks add latency, availability risk, and debugging complexity—operational costs often exceed benefits.
If a webhook is necessary:
- Conversion webhooks: Use only if structural schema conversion insufficient.
- Validation/mutation webhooks: Configure with explicit timeouts, failurePolicy, and namespaceSelectors.
- Validate webhook availability and latency in your rollout plan.
See ./references/versioning-and-migrations.md for conversion strategy and ./references/review-template.md for operational checklist.
4) GitOps/SSA ergonomics
Focus on patchability and stable diffs:
- List semantics for arrays of objects
- If items have stable identity (e.g.,
name, id), prefer map-like lists (x-kubernetes-list-type: map with x-kubernetes-list-map-keys).
- Identify ordering sensitivity and full-array replacement hazards.
See ./references/list-semantics-gitops-ssa.md.
5) Operator UX (kubectl)
Review/add additionalPrinterColumns for operator-facing UX:
- Ready / health signal
- Status message / reason
- Key spec fields
- Never duplicate
AGE (already shown by kubectl).
See ./references/printer-columns.md.
6) Compatibility & migration impact (mandatory)
Always include an explicit compatibility assessment:
- Classify change as non-breaking vs potentially breaking.
- Look beyond removals: tightening validation, type changes, list semantic changes, defaulting changes, semantic behavior shifts.
- If version evolution is involved: plan served versions, conversion webhooks, storage migration, and deprecation playbook.
See ./references/versioning-and-migrations.md.
7) Synthesize output
Follow the output template structure below:
- Rank risks + explain impact.
- List actionable changes with snippets.
- Provide PR-sized improvement plan.
- Include the PR review template (use
./references/review-template.md as the canonical template).
Output format (always use this template)
What’s good
Top risks (ranked)
- … — why it matters: …
- … — why it matters: …
- … — why it matters: …
Recommended changes (actionable)
Compatibility & migration impact (mandatory)
- Breaking? Yes/No
- Why: …
- If breaking or risky:
- Migration / deprecation steps:
- …
- …
- Rollout plan checklist:
- Versioning notes: served versions, storage version, conversion considerations
Minimal improvement plan (PR-sized)
- …
- …
- …
Template reference: Use ./references/review-template.md as the canonical template. Adapt to context while preserving section headings.
1---2name: k8s-crd-design-review3description: Use when reviewing Kubernetes CustomResourceDefinition (CRD) YAML (full manifests or diffs) as the compiled API contract. Topics: schema/validation (3-step hierarchy: schema → CEL → webhooks only if necessary), spec/status boundaries, state/phase vs conditions, conditions/observedGeneration conventions, kstatus-compatible Ready/Reconciling/Stalled readiness, object-reference modeling resource relationships: one-to-one, one-to-many), SSA/GitOps list semantics, printer columns, and compatibility/migration risk (breaking changes, versioning, rollout). Not for Kubebuilder Go-type selection; use kubebuilder-api-design instead.4---56# Kubernetes CRD Design Review78Perform a deterministic design + contract review for Kubernetes CRDs (the generated CRD YAML is the compiled API contract).910## Inputs1112Accept at least one of:1314- CRD YAML (full manifest) or a CRD diff15- A short description of intended API semantics and controller behavior1617If key info is missing, ask for it before concluding compatibility/migration:1819- Whether a controller exists, what it owns, and whether it writes `status`20- Whether this is a new API or a change to an existing API21- Served versions, storage version, and existing clients22- Whether objects already exist in clusters (migration needed?)23- Any GitOps/SSA constraints (patch strategy, desired stable identities)2425## Workflow (always follow this order)2627### 1) Identify scope2829- Identify `group`, `kind(s)`, `version(s)`, and whether this is **new** vs **change**.30- Identify controller existence and ownership boundaries.31- If reviewing Go types, confirm which generated CRD YAML(s) correspond to them.3233### 2) Contract integrity checks (spec/status + controller operability)3435- **Spec vs status boundary**36 - `spec` = user intent / desired state.37 - `status` = controller-observed state.38 - Flag lifecycle/state-machine fields in `spec` if the controller owns transitions.39- **Require `subresources.status` when a controller exists and writes status**40- **Conditions + `observedGeneration`**41 - Recommend `status.conditions` and `status.observedGeneration` (Kubernetes conventions; critical for tooling/GitOps correctness).42 - Model Conditions as a **map keyed by `type`**, not a chronological list: prefer schema markers (`x-kubernetes-list-type: map` with `x-kubernetes-list-map-keys: [type]`) for SSA/GitOps safety.43 - Use **state-style Condition types** (adjectives/past tense: `Ready`, `Degraded`, `Succeeded`; avoid transition names or phases for new APIs).44 - Include one high-signal **summary condition** (`Ready` for long-running; `Succeeded` for bounded execution).45 - For GitOps/status-tool compatibility, check the kstatus shape: `Ready` as aggregate current-state signal, `Reconciling=True` for active progress, `Stalled=True` for blocked/broken progress, and current `observedGeneration`.46 - Flag `status.phase` / `status.state` when it is the primary lifecycle contract for a new CRD; conditions should be the primary machine-readable readiness contract.47 - Ensure each Condition has semantic `True`/`False`/`Unknown` values with consistent meaning.48 - Remember: `status` updates via `/status` subresource use separate RBAC.4950See [`./references/conditions-and-status.md`](./references/conditions-and-status.md) for deeper semantics and [`./references/kstatus-readiness.md`](./references/kstatus-readiness.md) for kstatus compatibility.5152### 3) Schema correctness & validation (prevent invalid stored objects)5354Use a **3-step validation hierarchy**: always exhaust each level before moving to the next.5556#### Step 1: Schema validation (required)57Review the OpenAPI v3 schema (prefer the generated CRD YAML/diff):5859- Required fields for true invariants60- Enums for constrained strings61- Defaulting and nullable behavior62- Type constraints, patterns, min/max bounds63- Structural schema: ensure `spec.preserveUnknownFields: false` for strict validation and automatic version conversion64- **Object references & relationships:** Classify the relationship before judging its shape:65 fixed-kind, bounded multi-kind, generic-object, or field reference. Prefer explicit `fooRef` /66 `fooRefs` objects to a bare `fooName` string, but a fixed-kind reference normally needs only67 `{name}` (and an explicit `namespace` only when cross-namespace use is intentional).68 - Require a non-empty `name` inside a reference object. Put `group`, `kind`, or `resource` in the instance only when the user actually selects a target type; do not default a single allowed value merely as speculative tooling metadata.69 - For a fixed-kind `{name}` reference, ensure the field name and field documentation identify the target kind and scope. A generic navigation tool needs a separately published relationship map; ordinary CRD structural schema does not encode that mapping.70 - When a review concerns editor navigation or resource-graph tooling, see71 [`./references/relationship-metadata.md`](./references/relationship-metadata.md).72 - Omit `namespace` unless you explicitly allow cross-namespace references.73 - Avoid `apiVersion` in references; let the controller map versions.74 - Keep UID/resourceVersion in `status`, not `spec`, unless there is an exceptional API requirement.75 - Treat cross-namespace references as security-sensitive. Prefer same-namespace references; if cross-namespace is intentional, require explicit scope semantics and consider a ReferenceGrant-style producer opt-in.76 - `dependsOn` is an advisable (community) deviation from the `fooRefs` advise. Use it when you explicitly model a dependency graph and your controller implements full DAG semantics (readiness definition, scope rules, cycle detection). See [`./references/object-references.md`](./references/object-references.md).77 - Use `parentRef` only for resources your operator directly manages; do not use it to model general relationships.78 - See [`./references/object-references.md`](./references/object-references.md) for schema examples and deeper guidance.7980#### Step 2: CEL validation (before webhooks)81When cross-field invariants or complex constraints cannot be expressed with basic OpenAPI rules, use CEL (`x-kubernetes-validations`):8283- Cross-field invalid combinations (e.g., "field A only allowed if field B is set")84- Exactly-one-of constraints85- Numeric range relationships between fields86- Enum dependencies8788**Best practice:** Write minimal, targeted rules with clear error messages. CEL is stateless, auditable, and version-safe—always prefer it to webhooks.89See [`./references/validation-and-cel.md`](./references/validation-and-cel.md) for examples.9091#### Step 3: Webhooks (only if Steps 1–2 are insufficient)92Only recommend webhooks when schema and CEL cannot express the constraint. This is a significant operational decision. **Always double-check first:**9394- Can this be expressed with required fields, enums, or patterns?95- Can this be expressed with CEL (stateless, auditable, version-safe)?96- Is the webhook truly necessary, or is the controller solving it better?97- Webhooks add latency, availability risk, and debugging complexity—operational costs often exceed benefits.9899If a webhook is necessary:100- **Conversion webhooks:** Use only if structural schema conversion insufficient.101- **Validation/mutation webhooks:** Configure with explicit timeouts, failurePolicy, and namespaceSelectors.102- Validate webhook availability and latency in your rollout plan.103104See [`./references/versioning-and-migrations.md`](./references/versioning-and-migrations.md) for conversion strategy and [`./references/review-template.md`](./references/review-template.md) for operational checklist.105106### 4) GitOps/SSA ergonomics107108Focus on patchability and stable diffs:109110- List semantics for arrays of objects111 - If items have stable identity (e.g., `name`, `id`), prefer map-like lists (`x-kubernetes-list-type: map` with `x-kubernetes-list-map-keys`).112 - Identify ordering sensitivity and full-array replacement hazards.113114See [`./references/list-semantics-gitops-ssa.md`](./references/list-semantics-gitops-ssa.md).115116### 5) Operator UX (kubectl)117118Review/add `additionalPrinterColumns` for operator-facing UX:119- Ready / health signal120- Status message / reason121- Key spec fields122- Never duplicate `AGE` (already shown by kubectl).123124See [`./references/printer-columns.md`](./references/printer-columns.md).125126### 6) Compatibility & migration impact (mandatory)127128Always include an explicit compatibility assessment:129130- Classify change as non-breaking vs potentially breaking.131- Look beyond removals: tightening validation, type changes, list semantic changes, defaulting changes, semantic behavior shifts.132- If version evolution is involved: plan served versions, conversion webhooks, storage migration, and deprecation playbook.133134See [`./references/versioning-and-migrations.md`](./references/versioning-and-migrations.md).135136### 7) Synthesize output137138Follow the output template structure below:139- Rank risks + explain impact.140- List actionable changes with snippets.141- Provide PR-sized improvement plan.142- Include the PR review template (use [`./references/review-template.md`](./references/review-template.md) as the canonical template).143144## Output format (always use this template)145146### What’s good147148- …149150### Top risks (ranked)1511521. **…** — why it matters: …1532. **…** — why it matters: …1543. **…** — why it matters: …155156### Recommended changes (actionable)157158- **Change:** …159 - **Why:** …160 - **Snippet:**161 ```yaml162 # ...163 ```164165### Compatibility & migration impact (mandatory)166167- **Breaking?** Yes/No168- **Why:** …169- **If breaking or risky:**170 - **Migration / deprecation steps:**171 1. …172 2. …173 - **Rollout plan checklist:**174 - [ ] …175 - [ ] …176 - **Versioning notes:** served versions, storage version, conversion considerations177178### Minimal improvement plan (PR-sized)1791801. …1812. …1823. …183184---185186**Template reference:** Use [`./references/review-template.md`](./references/review-template.md) as the canonical template. Adapt to context while preserving section headings.