fix: mark removed schema properties as backward incompatible

IN
3h ago
+683-14

What

Removing a property from a request or response schema is now reported as backward incompatible. Adding properties remains backward compatible.

The verdict is gated by two new config flags (both enabled by default), so it can be tuned per direction like the other schema rules:

  • incompatible.request.properties.decreased (REQUEST_PROPERTIES_DECREASED)
  • incompatible.response.properties.decreased (RESPONSE_PROPERTIES_DECREASED)

Removal of a previously required response property is now governed by RESPONSE_PROPERTIES_DECREASED. RESPONSE_REQUIRED_DECREASED is unchanged where it is still meaningful: a property that keeps existing but moves from required to optional in a response.

Why

Previously only removing a required property from a response schema was incompatible. Removing an optional property - or any property from a request schema - was reported as backward compatible, so clients depending on the deleted property went unnoticed.

For renames, no rename heuristic is attempted (too risky to guess intent); a renamed property surfaces as the missing old property, which is now incompatible, so a rename is no longer silently reported as backward compatible.

Fixes #673, and addresses the schema-property part of #611 (metadata/description changes being reported as compatible is a separate topic). Related context: #724 discusses configurable compatibility rules, #264/#490 already confirmed that adding optional properties must stay compatible (kept as is), and #940 applied the same directional gating pattern to request-body requiredness.

How

ChangedSchema.compatibleForRequest() / compatibleForResponse() now check missingProperties through the new BackwardIncompatibleProp entries, mirroring the directional pattern already used by ChangedEnum (REQUEST_ENUM_DECREASED) and ChangedOneOfSchema (REQUEST_ONEOF_DECREASED / RESPONSE_ONEOF_INCREASED).

Notes on scope:

  • Properties that are readOnly in a request context or writeOnly in a response context were already filtered out of missingProperties by SchemaDiffResult.filterProperties, so they remain unflagged.
  • A discriminator property-name change short-circuits the property diff in ComposedSchemaDiffResult, so that path is unaffected.

Tests:

  • SchemaBCTest: flag tests for request property removal, response property removal, response required-property removal, plus response property addition (compatible).
  • SchemaDiffTest: model tests - property removed -> incompatible, property added -> compatible, required property removed -> incompatible. testAnyOfDiff/testAllOfDiff now expect incompatible because their fixtures remove a property, and addPropertyHandlingTest compares the pair in the direction that actually adds the property (its fixtures date back to the module split; 1 -> 2 was a removal).
  • Several BC fixtures (bc_schema_pattern_changed, bc_schema_type_changed, bc_request_schema_props_put_increased, bc_response_schema_props_required_decreased, bc_schema_changed_but_compatible) incidentally dropped optional properties from Widget; those incidental removals are now incompatible, so the fixtures were cleaned up to keep testing only what they meant to test.
  • AsciidocRenderTest.validateAsciiDocChangeFile asserts the exact render output for a removed-property spec; the expected output now shows Broken compatibility, the missing property line, and the WARNING footer.

CLI check on a minimal spec pair (GET/POST returning a schema with foo):

  • removing foo: Schema: Broken compatibility, Missing property: foo (string), exit code 1 with --fail-on-incompatible (was Backward compatible, exit 0)
  • renaming foo -> bar: reported via Missing property: foo, exit 1
  • adding bar: API changes are backward compatible, exit 0
  • both new flags set to false via --config-file: back to Backward compatible, exit 0

Summary by cubic

Removing a property from a request or response schema is now reported as backward incompatible, so clients that depend on the deleted property no longer go unnoticed. Previously only removing a required response property was flagged; removing optional properties or any request property was reported as compatible.

Adding properties stays compatible, and no rename heuristic is attempted, so renames surface as missing properties.

  • New config flags REQUEST_PROPERTIES_DECREASED and RESPONSE_PROPERTIES_DECREASED gate the verdict per direction, both enabled by default.
  • RESPONSE_REQUIRED_DECREASED now only covers properties that remain but move from required to optional.

Fixes #673 and addresses the schema-property part of #611.