# スキーマ発展
## 定義
スキーマ発展(schema evolution)とは、アプリケーションの機能変更に伴ってデータのスキーマ(構造・型定義)が時間とともに変化していく現象、およびそれに新旧のコード・データが混在する環境で安全に対処する設計手法を指す。ローリングアップグレード(段階的ロールアウト)やクライアント側アプリの更新遅延により、新旧のコードとデータ形式が同時に存在することは避けられない。これに耐えるには、新しいコードが古いコードの書いたデータを読める**後方互換性(backward compatibility)**と、古いコードが新しいコードの書いたデータを読める**前方互換性(forward compatibility)**の両方をエンコーディング形式が保証する必要がある。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Encoding and Evolution" 冒頭)
## 前方互換性が難しい理由
後方互換性は新コードの作者が旧データ形式を知っているため比較的実装しやすい。一方、前方互換性は旧コードが「まだ知らないフィールド」を無視しつつ、その値を保持し続ける必要がある。レコードをモデルオブジェクトへデコードする実装が未知フィールドを明示的に保持しない場合、旧コードがレコードを読み込み・更新・書き戻す過程で新フィールドの値が失われる、という具体的な失敗モードが生じる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Encoding and Evolution" 冒頭、図5-1)
## エンコーディング形式ごとの規則
- **[[Protocol Buffers]]**: フィールドをタグ番号(整数)で識別する。フィールド名の変更は自由だがタグ番号の変更・再利用は不可。新フィールドは新規タグの割り当てで前方互換(未知タグは型注釈をもとにスキップ)・後方互換(欠落フィールドはデフォルト値で補完)を両立する。削除タグは `reserved` で予約する。
- **[[Apache Avro]]**: タグ番号を持たず、エンコード時の writer's schema とデコード時の reader's schema をフィールド名で突き合わせて解決する。互換性を保てるのはデフォルト値を持つフィールドの追加・削除のみ。`null` を許すには union 型の先頭分岐に置く必要がある。
- **JSON Schema / XML Schema**: 検証規則(範囲制約・正規表現等)を重ねられる分、開かれた/閉じられたコンテンツモデルの選択やスキーマ間参照の解決が複雑になり、互換性を保った発展のさせ方自体が難問になりやすい。
(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Field tags and schema evolution", "Schema evolution rules")
## データフロー様式ごとの要求
スキーマ発展に求められる互換性の向きは、データがどう流れるかで変わる。データベース経由では読み手・書き手の双方向互換性が必要(「data outlives code」であり、5年前のデータが元の形式のまま残り続ける)。RPC/REST サービス経由では、サーバーが先にアップグレードされる前提を置けるため、リクエストは後方互換・レスポンスは前方互換のみで足りる。イベント駆動アーキテクチャでは送信者・受信者の双方の互換性が必要になる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Modes of Dataflow")
## 横断的知見
- **「data outlives code」という同一の問題意識が、レコードのスキーマ発展(DDIA)とバイナリファイルのバージョン管理(Database Internals)という2つの異なる粒度で独立に扱われている**: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] は、データベース経由のデータフローでは古いデータがそのままの形式で残り続けるため読み手・書き手双方向の互換性が必要になると述べる。[[@2021__OReillyJapan__詳説 データベース - Chapter 3 ファイルフォーマット]] §3.9 は、ストレージエンジンが「現行フォーマットに加えて後方互換性のための過去のフォーマット」を複数サポートする必要があると述べ、直面したファイルがどのバージョンかを判別する3方式(ファイル名プレフィックス・別ファイル・ヘッダ内マジックナンバー)を挙げる。前者はレコード単位のフィールド追加・削除にフォーカスするのに対し、後者はファイル全体の物理レイアウトのバージョン判定にフォーカスするが、どちらも「新旧が混在する環境で安全に対処する」という同じ設計要求から生じており、粒度は違えど互換性問題が階層を貫いて繰り返されることを示す。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Encoding and Evolution" 冒頭, [[@2021__OReillyJapan__詳説 データベース - Chapter 3 ファイルフォーマット]] §3.9)
- **Database Internals のファイルバージョン判定方式は、DDIA のフィールドタグ方式に対応する「ファイル全体版」の解決策である**: DDIA が整理する Protocol Buffers のタグ番号方式(未知タグはスキップ、欠落タグはデフォルト値補完)は、レコード内の個々のフィールド単位で新旧互換性を機械的に解決する。Database Internals のヘッダ内マジックナンバー/バージョン番号方式は、ファイル全体を単位として「どのバージョンのリーダーで読むか」を判定する、より粗い粒度の同型の解決策であり、いずれも「識別子を明示的に埋め込み、それを手掛かりに読み手が挙動を切り替える」という共通の設計原理に基づく。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Field tags and schema evolution", [[@2021__OReillyJapan__詳説 データベース - Chapter 3 ファイルフォーマット]] §3.9)
- **DDIA/Database Internals が「フォーマット・エンコーディング単位」で扱うスキーマ発展を、『SREの探求』16章は「変更セットの運用手順単位」で補完する**: DDIA と Database Internals(既出)は、新旧のコードとデータが混在する環境でどのエンコーディング形式・ファイルバージョン判定方式なら安全に読み書きできるかという、データの物理的な表現形式の互換性を扱う。これに対し『SREの探求』16章は、データベースに適用される個々の変更セットにバージョン番号を与え(変更セット適用後にデータベース内の整数値をインクリメント)、デプロイメントシステムがデータベース内を見るだけで現行バージョンを判定できるようにするという、運用上の変更管理プロセスとしてのバージョニングを扱う。前者が「1つのレコード/ファイルがどの世代の形式で書かれているか」を機械的に判定する規則を問うのに対し、後者は「クラスタ全体がどの世代の変更セットまで適用済みか」を問う、異なる粒度・異なる目的のバージョン管理である。しかし両者とも「新旧の形式/状態が同時に存在することを前提に、識別子を明示的に埋め込んで読み手が挙動を切り替える」という共通の設計原理に基づく点で、DDIA の「識別子を埋め込む」原則(既出の横断的知見)は運用層のマイグレーション管理にも通底することが分かる。(Source: [[@2021__OReillyJapan__SREの探求 - Chapter 16 データベースリライアビリティエンジニアリング]] §16.6.1, [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Field tags and schema evolution")
- **前方互換性のない変更(既存データの変更・削除)を安全に展開する具体的手順として、16章はDDIAが理論面で述べる互換性要求を実務のロールバック手続きへ翻訳する**: DDIA は、後方互換性(新コードが旧データを読める)と前方互換性(旧コードが新データを読める)の両方が必要になる理由を、ローリングアップグレードで新旧コードが同時に存在するためだと説明する(既出)。16章はこれを、オブジェクトの新規作成・データ挿入(前方互換性を壊さない変更)は簡単にプロダクションへプッシュできる一方、既存データの変更・削除やオブジェクトの修正・削除(共有オブジェクトのロック・データ完全性の問題等を引き起こしうる変更)は影響分析を経て慎重にデプロイすべきだと述べ、さらにテーブルを削除して元に戻すのではなく名前を変更してデータへのアクセス可能性を保つ、というロールバック時の具体的な互換性維持策を示す。DDIA が理論的に要求する「前方互換性を壊す変更は危険」という原則に対し、16章は「どの操作が前方互換性を壊しうるか」を運用上のパターン分類(共有オブジェクトのロック、リソースの飽和、データ完全性の問題、レプリケーションの破損/停止)として具体化する。(Source: [[@2021__OReillyJapan__SREの探求 - Chapter 16 データベースリライアビリティエンジニアリング]] §16.6.2, §16.6.3.2, [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] "Encoding and Evolution" 冒頭)
## 未解決の問い
- 単一値属性の多値化やテーブル分割のような複雑なスキーマ変更を、前方・後方互換性を保ったまま行う体系的な手法は何か(DDIA 本文が「未解決の研究課題」と述べる Project Cambria 等の Lens ベースアプローチとの関係)。
- 16章の変更セットバージョン番号(クラスタ単位の運用的バージョニング)と DDIA のフィールドタグ/Database Internals のファイルバージョン判定(レコード/ファイル単位の形式バージョニング)は、実運用でどう対応づけられるか。例えばあるデータベースの変更セット番号は、そのデータベースが書き出すレコードのスキーマバージョンと1対1に対応すべきか、それとも独立した番号系列であるべきか、本 concept の現ソース群では検証できていない。
- gRPC(Protocol Buffers ベース)と GraphQL のようなクエリベース API のスキーマ発展規則は、本頁が整理した3規則(Protobuf/Avro/DBスキーマ)とどう異なるか。
- JSON Schema の前方・後方互換性を機械的に検証するツールは、Protocol Buffers/Avro のスキーマレジストリと同等の成熟度に達しているか。
- ファイル全体のバージョン判定(Database Internals §3.9)とレコード単位のフィールドタグ(DDIA)を組み合わせた実装(例: バージョン付きヘッダを持つ Protocol Buffers ファイル)は、どちらの粒度の互換性問題を先に解決すべきか、両者の優先順位を扱った文献はあるか。
## 関連
- ソース: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]] / [[@2021__OReillyJapan__詳説 データベース - Chapter 3 ファイルフォーマット]] / [[@2021__OReillyJapan__SREの探求 - Chapter 16 データベースリライアビリティエンジニアリング]]
- 概念: [[バイナリエンコーディング]] / [[導出データ]] / [[データベースリライアビリティエンジニアリング]]
- エンティティ: [[Protocol Buffers]] / [[Apache Avro]] / [[Apache Cassandra]] / [[PostgreSQL]]
## 出典
- [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 5 Encoding and Evolution]]("Encoding and Evolution" 冒頭, "Field tags and schema evolution", "Schema evolution rules", "Modes of Dataflow")
- [[@2021__OReillyJapan__詳説 データベース - Chapter 3 ファイルフォーマット]](§3.9 バージョン管理 — ファイル名プレフィックス・別ファイル・ヘッダ内マジックナンバー)
- [[@2021__OReillyJapan__SREの探求 - Chapter 16 データベースリライアビリティエンジニアリング]](§16.6: 変更セットのバージョン番号による運用的マイグレーション管理、前方互換性を壊す操作の分類とロールバック手順)