Skip to content

Schema Compatibility

The library includes a JSON Schema compatibility checker for detecting breaking changes between schema versions. This is useful when evolving APIs to ensure that updated schemas remain compatible with existing data.

Experimental

This API is experimental and subject to change in future versions.

Compatibility Types

Type Meaning
Backward compatible Data written with the original schema can be read using the updated schema
Forward compatible Data written with the updated schema can be read using the original schema
Fully compatible Both backward and forward compatible

Quick Checks

Use the boolean convenience methods for simple pass/fail checks.

import io.apitomy.datamodels.jsonschema.compat.JsonSchemaCompatibilityChecker;

String original = """
    {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "age": { "type": "integer" }
      },
      "required": ["name"]
    }
    """;

String updated = """
    {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "age": { "type": "integer" },
        "email": { "type": "string" }
      },
      "required": ["name"]
    }
    """;

// Adding an optional property is backward compatible
boolean backwardOk = JsonSchemaCompatibilityChecker.isBackwardCompatible(original, updated);
System.out.println("Backward compatible: " + backwardOk); // true

// Check forward compatibility
boolean forwardOk = JsonSchemaCompatibilityChecker.isForwardCompatible(original, updated);

// Check full compatibility (both directions)
boolean fullyOk = JsonSchemaCompatibilityChecker.isFullyCompatible(original, updated);
import { JsonSchemaCompatibilityChecker } from '@apitomy/data-models';

const original = JSON.stringify({
    type: 'object',
    properties: {
        name: { type: 'string' },
        age: { type: 'integer' },
    },
    required: ['name'],
});

const updated = JSON.stringify({
    type: 'object',
    properties: {
        name: { type: 'string' },
        age: { type: 'integer' },
        email: { type: 'string' },
    },
    required: ['name'],
});

// Adding an optional property is backward compatible
const backwardOk = JsonSchemaCompatibilityChecker.isBackwardCompatible(original, updated);
console.log('Backward compatible:', backwardOk); // true

// Check forward compatibility
const forwardOk = JsonSchemaCompatibilityChecker.isForwardCompatible(original, updated);

// Check full compatibility (both directions)
const fullyOk = JsonSchemaCompatibilityChecker.isFullyCompatible(original, updated);

Detailed Diff

Use checkBackwardCompatibility() to get a DiffContext with all detected differences, including which are compatible and which are not.

import io.apitomy.datamodels.jsonschema.compat.DiffContext;
import io.apitomy.datamodels.jsonschema.compat.Difference;

DiffContext result = JsonSchemaCompatibilityChecker.checkBackwardCompatibility(
        original, updated);

if (result.foundAllDifferencesAreCompatible()) {
    System.out.println("All changes are backward compatible.");
} else {
    System.out.println("Incompatible changes found:");
    for (Difference diff : result.getIncompatibleDifferences()) {
        System.out.println("  " + diff.getDiffType() + " at " + diff.getPathUpdated());
    }
}
import { JsonSchemaCompatibilityChecker, DiffContext } from '@apitomy/data-models';

const result = JsonSchemaCompatibilityChecker.checkBackwardCompatibility(
    original, updated);

if (result.foundAllDifferencesAreCompatible()) {
    console.log('All changes are backward compatible.');
} else {
    console.log('Incompatible changes found:');
    result.getIncompatibleDifferences().forEach(diff => {
        console.log(`  ${diff.getDiffType()} at ${diff.getPathUpdated()}`);
    });
}

You can also get just the incompatible differences directly:

Set<Difference> breaking = JsonSchemaCompatibilityChecker.getIncompatibleDifferences(
        original, updated);
const breaking = JsonSchemaCompatibilityChecker.getIncompatibleDifferences(
    original, updated);

Common Breaking Changes

The checker detects a wide range of schema differences. Here are some common examples:

Change Backward Compatible?
Adding an optional property Yes
Adding a required property No
Removing a required property Yes
Widening a type (e.g., integernumber) Yes
Narrowing a type (e.g., numberinteger) No
Increasing maxLength / maximum Yes
Decreasing maxLength / maximum No
Decreasing minLength / minimum Yes
Increasing minLength / minimum No
Adding an enum member Yes
Removing an enum member No
Setting additionalProperties: false No

Supported Versions

The compatibility checker currently supports JSON Schema Draft 4, Draft 6, and Draft 7. Modern versions (2019-09, 2020-12) are detected but flagged as unsupported in the diff context via hasUnsupportedFeatures().