draft-07 か 2020-12 か
要点:2020-12 が現行で、$defs、prefixItems、unevaluatedProperties を加えますが、多くのツールは依然 draft-07 しか話しません。ツールチェーン全体が対応するかで選びます。

要点:新規プロジェクトには 2020-12 を使い、バリデータ、コード生成器、OpenAPI ツールのどれかがそれしか対応していない瞬間に draft-07 へ戻し、常に $schema でドラフトを固定しましょう。
短い答え:新規は 2020-12、古いコンポーネントが強制するなら draft-07。
年表
| ドラフト | 年 | 注目点 |
|---|---|---|
| draft-04 | 2013 | $id ではなく id。古い API でなお一般的 |
| draft-06 | 2017 | const、propertyNames |
| draft-07 | 2018 | if/then/else、readOnly、contentMediaType。互換性が最良 |
| 2019-09 | 2019 | $defs が definitions を置換、unevaluatedProperties |
| 2020-12 | 2020 | prefixItems、$dynamicRef。現行の推奨 |
2020-12 が改善するもの
配列のタプルが特殊ケースでなくなる。 draft-07 は「最初の要素が A、二番目が B」を items: [{...}, {...}] と additionalItems で表現します。2020-12 は位置別スキーマに prefixItems、残りに items を使います。
$defs が標準の名前。 2019 以降に書かれたツールは既定でそこを見ます。
unevaluatedProperties が合成と動く。 allOf と組み合わせると、additionalProperties: false は兄弟分岐が定義したフィールドを拒否します。unevaluatedProperties: false はスキーマ全体が実際に評価したプロパティだけを考慮し、これが合成に必要なものです。
draft-07 が正しい答えであるとき
| 状況 | 理由 |
|---|---|
| OpenAPI 3.0 | そのスキーマオブジェクトは draft-04/07 のサブセット |
| 古いバリデータ(ajv 6、一部の Java ライブラリ) | prefixItems や $dynamicRef を知らない |
| プラットフォームがバージョンを強制 | 一部のローコードプラットフォームやゲートウェイが固定 |
| 07 で止まるコード生成器 | 未知のキーワードが静かに無視される |
最後のケースを直接テストしてください。prefixItems のスキーマをバリデータに流します。未知のキーワードを報告せずに文句なしで通るなら、制約を無視しています。最も危険な結果です。
バージョンの混在
常に $schema を宣言する。 既定が異なります。ajv は draft-07 を仮定し、いくつかの Python ライブラリは 2020-12 を仮定します。同じファイルが二か所で異なる検証をされます。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/order.json",
"type": "object"
}
ドラフトをまたいで $ref しない。 振る舞いは未定義です。
バッチでアップグレードする。 一つのリポジトリに二つのドラフトがあると、「ここではどの規則が適用されるか」が部族の知識になります。
FAQ
バリデータが 2020-12 に対応しているか確認するには?
prefixItems を使うスキーマをバリデータに流します。未知のキーワードエラーはドラフト非対応を意味し、静かに通るなら制約を無視しており、その方が悪いです。
一つのプロジェクトが単一のドラフトを使うべきなのはなぜですか?
一つのリポジトリに二つのドラフトがあると、「ここではどの規則が適用されるか」が部族の知識になります。バッチでアップグレードし、すべてのファイルが同じ解釈を共有するようにしてください。
常に $schema を宣言すべきですか?
はい。既定が異なります。ajv は draft-07 を仮定し、いくつかの Python ライブラリは 2020-12 を仮定するので、同じファイルが二か所で異なる検証をされます。