Adding a required field without a default to an existing request schema is an immediate breaking change: every existing consumer who does not send the new field starts receiving 400 errors without any version bump or deprecation notice. iso-25010:2011 compatibility.interoperability and maintainability.modifiability are both violated — the API's stability promise is broken the moment a required field lands in production. The failure mode is insidious because it appears as a client error (400), making it look like the consumer sent a bad request when the real cause is a server-side contract change.
High because adding a required field without a default immediately breaks all existing consumers without a version bump, causing silent client-side failures.
Follow the additive-only rule within a version: every new request field must be optional with a sensible default. In Zod:
// Before -- adding a required field (BREAKING):
const OrderSchema = z.object({
items: z.array(ItemSchema),
shippingMethod: z.string(), // required, no default -- breaks existing clients
})
// After -- additive with default (safe):
const OrderSchema = z.object({
items: z.array(ItemSchema),
shippingMethod: z.string().default('standard'), // optional with fallback
})
The non-breaking rules: new optional request fields with defaults, new response fields (add only, never remove), new endpoints, new optional query parameters. Any change that removes, renames, or makes previously-optional fields required requires a major version bump.
ID: api-design.versioning-evolution.additive-changes
Severity: high
What to look for: Examine recent changes to request schemas (git history of schema files, recent PRs, or changelog entries). Check whether new fields added to request bodies are optional with default values -- meaning existing API consumers who don't send the new field will not break. Look for: new required fields without defaults (breaking change), new required query parameters, removed fields (breaking), or changed field types (breaking). Also check response additions: new response fields should not break consumers that use strict deserialization.
Pass criteria: Count all fields added to request schemas in recent changes. All recent additions to request schemas are optional with sensible defaults -- at least 100% of new fields must have defaults. No new required fields have been added to existing endpoints without a version bump. Response additions are backward-compatible (new fields added, no fields removed or type-changed).
Fail criteria: New required fields have been added to existing request schemas without defaults or a version bump. Or fields have been removed from responses without deprecation. Or field types have changed (string to number, object to array) without a version bump.
Skip (N/A) when: The API has had no changes since initial release (no git history of schema modifications). Or the API is pre-1.0 with an explicit instability warning.
Detail on fail: Identify the breaking change (e.g., "POST /api/orders now requires shippingMethod field (added 2 weeks ago) with no default value. Existing consumers not sending this field get 400 errors. No version bump accompanied this change."). Max 500 chars.
Remediation: When evolving an API, follow the additive-only rule within a version:
// Before -- adding a required field (BREAKING):
const OrderSchema = z.object({
items: z.array(ItemSchema),
shippingMethod: z.string(), // required, no default -- BREAKS existing clients
})
// After -- additive with default:
const OrderSchema = z.object({
items: z.array(ItemSchema),
shippingMethod: z.string().default('standard'), // optional with default
})
Rules for non-breaking changes: new optional request fields with defaults, new response fields (add only, never remove), new endpoints, new optional query parameters.