Javascript is required
Best practicesPublished 2026-07-286 min read

API Contract Breaking Changes: Checking Field Changes

A changed JSON example is not automatically a broken API contract. Compatibility depends on direction, whether the data is a request or response, and what clients accept. This article is limited to server response objects and existing clients; it does not label every runtime payload difference as breaking.

By: Tiny
API ContractBreaking ChangesJSON DiffSchema

1. Scope: compare a response contract first

Removing a field a client uses, changing its type, or making a previously guaranteed field absent are response compatibility risks. Adding an optional field is often safe, but adding a value the server may return to an existing enum can break a client that handles only the old set. Request parameters have the opposite direction in several cases.

2. Minimal reproduction: value change versus contract change

Changing status from active to paused is not merely a value diff when a client accepts only active or disabled. A changed order amount within the same number contract is ordinary data.

const before = { id: "string", status: ["active", "disabled"] };
const after = { id: "string", status: ["active", "disabled", "paused"] };
// Compare declared field constraints, not two live orders with different amounts.

3. Root cause: JSON Diff has no consumer direction

A structural diff can report additions, removals, and replacements, but cannot know request versus response direction or whether clients exhaustively handle an enum. Contract checking adds policy. Here, required means the server guarantees a field and enum means the strings it may return; another protocol may use different rules but must document them.

4. Recommended policy: explicit, reviewable response snapshots

Describe each response field with type, required, and optional enum, keep a baseline under version control, and compare it with a candidate. Do not present an undefined compareJsonSchemas placeholder as implementation. Use a selected schema tool for OpenAPI, oneOf, ranges, and nested structures, while keeping service-specific breaking rules explicit.

5. Complete code: flat response-shape checks

This browser- and Node-compatible TypeScript deliberately supports flat response fields only. It reports removals, type changes, weaker required guarantees, and new possible enum outputs; it is not a full OpenAPI validator.

type ScalarType = "string" | "number" | "boolean" | "null";
type ResponseField = { type: ScalarType; required: boolean; enum?: readonly string[] };
type ResponseShape = Record<string, ResponseField>;
type Finding = { field: string; kind: "removed" | "type" | "required" | "enum"; message: string };
function findResponseBreakingChanges(before: ResponseShape, after: ResponseShape): Finding[] {
  const findings: Finding[] = [];
  for (const [name, oldField] of Object.entries(before)) {
    const newField = after[name];
    if (!newField) { findings.push({ field: name, kind: "removed", message: "field is no longer returned" }); continue; }
    if (oldField.type !== newField.type) findings.push({ field: name, kind: "type", message: oldField.type + " changed to " + newField.type });
    if (oldField.required && !newField.required) findings.push({ field: name, kind: "required", message: "field is no longer guaranteed" });
    if (oldField.enum && newField.enum) for (const value of newField.enum) if (!oldField.enum.includes(value)) findings.push({ field: name, kind: "enum", message: "new response enum value: " + value });
  }
  return findings;
}
console.log(findResponseBreakingChanges(
  { id: { type: "string", required: true }, status: { type: "string", required: true, enum: ["active", "disabled"] } },
  { id: { type: "string", required: true }, status: { type: "string", required: true, enum: ["active", "disabled", "paused"] } },
));

6. Incorrect approaches

Blocking every added field prevents compatible extension. Comparing live example values reports normal data changes, while checking only field presence misses type, required, and enum risks. Applying response rules directly to request fields is also unsafe because direction changes the compatibility analysis.

7. Boundaries: nesting, nullable values, and version policy

Nested objects need paths or recursion, and arrays need an element contract. Nullable, missing, and empty string are distinct states. An added enum value can be a warning instead of a blocker when clients have a default branch. Versioning, deprecation, and migration windows are release policy, not facts inferred by a JSON diff.

8. Verification

Write snapshots for removal, string-to-number, required-to-optional, optional addition, a new response enum value, and ordinary value changes. Assert the intended findings and ensure compatible additions are not falsely blocked. Review the baseline and candidate with the API owner.

9. FAQ

Is every added response field safe? Usually for tolerant clients, but clients that reject unknown fields need coordination.

Why can a new enum value break clients? An exhaustive switch can treat it as an error.

Does this replace OpenAPI tooling? No; it demonstrates reviewable rules for a deliberately small schema scope.

10. Summary

Breaking-change checks are about consumer direction, not coloring every JSON difference red. For responses, examine removal, type changes, weaker guarantees, and new enum outputs. Use reviewable snapshots and focused tests, then rely on suitable tooling for complex schemas.

Sources and further reading

Specifications and references for checking the article’s technical details.

Related articles

Keep exploring

Use the related browser tool to test the workflow with your own sample data.

Open related tool