---
title: "JSON Schema Backward-Compatibility Diff"
description: "Diff two JSON Schemas: is the change backward-compatible, or a breaking one that needs a new message version? Runs in your browser."
url: https://sade.dev/en/tools/schema-compat/
lang: en
author: "Muhammet Şafak"
published: 2026-06-18
updated: 2026-09-06
section: Tool
tags: ["schema","reliability"]
---

# JSON Schema Backward-Compatibility Diff

> Diff two JSON Schemas: is the change backward-compatible, or a breaking one that needs a new message version? Runs in your browser.

Two JSON Schemas for a message's `data` block — old and new — and a single question: can a
consumer that upgraded to the new schema still read messages produced under the old one? If
yes, ship it. If no, you version the message identity instead of mutating it. This applies
the same rules a backward-compatibility linter would, in your browser.

## What counts as breaking

Compatibility is asymmetric — **loosening is safe, tightening is not**:

- **Safe:** adding an optional field, dropping a `required` constraint, widening an `enum`,
  relaxing a `minimum`.
- **Breaking:** adding a required field (or making an existing one required), retyping a
  field, dropping an `enum` value, raising a `minimum` or `minLength`, closing
  `additionalProperties`. Removing or renaming a field joins that list only where the new
  schema sets `additionalProperties: false` — leave the content model open and an old message
  still carrying the dropped field validates fine.

A rename is a remove plus an add, so under a closed content model it surfaces as breaking —
correctly: a consumer expecting the new field name won't find it in the old messages. Leave
`additionalProperties` open and the diff comes back clean, because it only asks whether old
messages still validate — not whether the consumer can find the field it now reads.

## Why a breaking change means a new URN

You can't change a distributed contract atomically: producers and consumers deploy
independently, and with at-least-once delivery, in-flight messages under the old shape keep
arriving while you roll out the new one. So a breaking change means a **new identity**
(`orders.created.v2`), run in parallel until the old one has no consumers left — never a
silent redefinition of the existing one. The longer argument is in
[Schema Evolution](/en/journal/schema-evolution-without-breaking).

Much the same rules, applied in CI rather than in your browser, are the [BabelQueue schema-validation spec](https://babelqueue.com/docs/spec/1.x/schema-validation) — enforced by the `bqschema` tool in [babelqueue-registry](https://github.com/BabelQueue/babelqueue-registry). The spec is the stricter of the two: it counts a removal or a rename as breaking outright, where the browser diff flags it only under a closed content model.

## Privacy

Both schemas are diffed entirely in your browser. Nothing is sent anywhere.
