JSONからSQLiteへのデータ移行で、「データの意味が壊れていない」ことを実務ではどう証明していますか?
【TL;DR】
- 背景: 個人開発WebアプリのデータをJSONからSQLite(約70テーブル)へデータモデルを再設計して移行中。
- 課題: 構造が大きく異なる移行で、「データが欠損・変質せず、業務上の意味が保たれていること」をどう検証・証明すべきか。
- 現在の案: DB制約、Canonical全件比較、独立したReconciliation、業務Invariants、Golden fixtures、検証器自体のNegative / Mutation Testなどを組み合わせることを検討中。
- 知りたいこと: 実際の現場での移行成功の「合格ライン」、全件比較できる場合でも必要な追加検証、検証範囲の重点配分、ハマりやすい失敗談。
開発の背景と進め方
私は今年からアプリを個人開発しています。
プログラミングのコード自体を1から書くわけではありませんが、AI(LLM)をパートナーとして開発を進めています。
自分で「これって壊れないの?」「途中で失敗したらどうなる?」「テストがPASSしても、本当に正しいと言えるの?」といった疑問や懸念を出し、それをAIと一緒に技術的な設計・検証項目へ落とし込む、という進め方をしています。
今回の質問文についても、私が聞きたいことや現在の状況をもとに、AIに整理・構造化を手伝ってもらっています。
【JSONからSQLiteへ移行することになった背景】
現在、JSONベースで保存している既存データをSQLiteへ移行する設計を進めています。
開発当初はJSONでも十分だったのですが、機能やデータの種類が増えるにつれて、複数のJSONをまたぐ整合性管理や更新処理が複雑になってきました。
そのため、単に「JSONファイルが大きくなったからDBへ移す」というより、トランザクション・参照整合性・制約などを持つ構造化されたデータ管理へ移行する必要性が出てきた、というのが今回の背景です。
移行先では、単純な「1 JSON = 1テーブル」の移し替えではなく、データモデル自体を見直しています。
現在のSQLite側の物理設計は約70テーブルで、旧JSONの保存単位とSQLiteのテーブルが1対1で対応しているわけではありません。
親子関係や関連データの分離、外部キー、制約、インデックス、複数テーブルにまたがるトランザクション境界などを含めて再設計しているため、旧JSONと新DBでは構造がかなり異なります。
【今回の前提】
今回対象としているのはWebアプリです。
ユーザーのスマートフォン等の端末内DBをアップデートするMigrationではなく、現在サーバー側でJSONファイルに永続化しているデータをSQLiteへ移行する想定です。
- データ量: 現時点のデータ量は、移行前後の全件比較が現実的に行える規模です。そのため、今回は性能上可能であればサンプリングではなく全件検証する前提で考えています。
- ダウンタイム: 本番切替時に無停止(Zero Downtime)を必須条件としているわけではありません。必要であれば一時的に書き込みを止めて静止点を作り、バックアップを確保した上でMigrationと検証を行う方式も選択肢として考えています。
そのため今回は、「巨大データをどう高速に移すか」「無停止でどう切り替えるか」よりも、
構造の異なる旧JSONと新DBの間で、データの意味・関係・状態・順序などが壊れていないことをどう証明するか
を主な質問にしています。
なお、今回のMigration検証とは別に、既存アプリ側については単体テスト・結合テスト・関連する回帰テストを継続して実施しています。
そのため今回は、「SQLiteへ移した後にアプリが動くか」という確認だけではなく、既存JSON環境で成立していた業務上の状態や振る舞いが、Migration後にも維持されているかを別途検証したいと考えています。
もちろん、既存テストがPASSしていること自体を「元データや仕様が絶対に正しい」という保証にはしていません。
【ここでいう「データの意味が同じ」とは】
今回確認したいのは、単に旧JSONとSQLiteで文字列やフィールドの値が一致することではありません。
例えば、
- 正式なID
- 値
- 親子・参照関係
- 状態
- 履歴
- 順序
- 日時
- NULL / missing / empty の意味
- 金額・数量
- 集計結果
- 業務判断に使用する属性
などについて、
Migration後も、Migration前と業務上同じ事実・関係・状態として解釈できること
を「意味が保存された」と考えています。
つまり、データ構造そのものではなく、
旧JSONから判断できた業務上の事実と、新DBから判断できる業務上の事実が等価であること
を確認したい、という意図です。
【将来のRDB移行について】
SQLiteを将来にわたって唯一の保存方式と決めているわけではありません。
今後、データ量や同時書き込みなどの要件がSQLiteの適性を超える場合には、PostgreSQL等のサーバー型RDBへの移行も選択肢になると考えています。
そのため、今回の設計でも、可能な範囲でSQLite固有の挙動へ必要以上に依存せず、データモデルやMigrationの考え方を特定のDBだけに強く結び付けないよう意識しています。
ただし、今回お聞きしたいのはSQLiteそのものの選定ではなく、
構造の異なるデータストア間でMigrationした際に、「元データの意味が壊れていない」と何をもって判定するか
です。
【「移行成功」を何で証明するか】
ImporterやPreflight(事前検証)の設計を進める中で、一番悩んでいるのが、
「移行後のデータが、移行前と業務上同じ意味を保っていて、欠損・重複・変質していないことを何で証明するか」
です。
Importerがエラーなく完走しただけでは、移行成功とは言えないと思っています。
また、PRAGMA integrity_check や PRAGMA foreign_key_check がPASSしても、それはDB自体の整合性や参照整合性等の検査であって、業務データの意味まで正しいことを保証するものではないと理解しています。
【現在考えている検証】
今のところ、1つの検証方法だけを正解にするのではなく、複数のなるべく独立した観点を組み合わせることを考えています。
1. DB構造・制約の検証
- FK
- UNIQUE
- CHECK
- NOT NULL
- 型
- 必須関係
PRAGMA integrity_checkPRAGMA foreign_key_check
など、新スキーマ側の制約に違反していないかを確認する。
ただし、これは主に「DBとして整合しているか」の検証であり、「旧データの意味が正しく移されたか」とは分けて考えています。
2. Migration前後のCanonical全件比較
旧JSONとSQLiteから、それぞれ比較用のCanonical表現を生成し、全件Diffを取る方法です。
その際、
- キー順序
- 配列・履歴等の順序
- timestamp
- timezone
- precision
- NULL
- missing
- empty string
- 数値表現
- ID表現
- legacy値
などのCanonical化ルールを明示する想定です。
今回のデータ量では全件比較が可能なので、サンプリングだけではなく全件検証を考えています。
3. 件数・集計値・ハッシュ等による独立したReconciliation
Canonical比較だけに依存すると、
Canonical化のルール自体が間違っているのに、100%一致してPASSする
可能性があると考えています。
そのため別経路で、
- source / target件数
- entityごとの件数
- ID集合
- status別件数
- 金額・数量等の合計
- 親子件数
- orphan件数
- duplicate件数
- 必要に応じたhash
などを照合し、欠損・重複・変質を確認する方法を考えています。
4. 業務上のInvariants(不変条件)の検証
単純な値の一致だけではなく、
「この業務状態なら、必ずこの関係が成立していなければならない」
という不変条件も確認する想定です。
例えば一般化すると、
- 完了状態なら必要な履歴が存在する
- 親が存在しない子データは存在しない
- 確定済みデータに必要な関連情報が欠けていない
- 同時に成立してはいけない状態が共存していない
- 集計結果と明細が一致する
などです。
DB制約だけでは表現しにくい、複数テーブルを横断した業務上の整合性も対象になると考えています。
5. Golden fixtures
Migration仕様そのものを検証するため、
「このJSONをMigrationしたら、このSQLite状態になる」
という小さな入力データと期待結果の組を、人間側であらかじめ確定しておく方法も考えています。
特に、
- missing / null / empty
-
"00123"のような先頭ゼロ - timestamp / timezone / precision
- ordering
- duplicate ID
- orphan
- unknown field
- schema version違い
- legacy値
- 境界値
など、変換事故が起きやすいケースを対象にする想定です。
6. 検証器そのもののNegative / Mutation Test
個人的に特に怖いのが、
「ImporterとVerifierが同じ勘違いをしているため、間違ったMigrationなのに綺麗にPASSする」
ケースです。
そのため、
- 1件削除する
- 値を変更する
- IDを重複させる
- 参照関係を壊す
- 順序を変更する
- timestampを変更する
- NULLとemptyを入れ替える
- 集計値を壊す
など、意図的に誤った状態を作り、
Verifierが本当にFAILを検出できるか
も確認した方がよいのではないかと考えています。
【Migration Mapping / Specificationをどこまで独立させるべきか】
もう一つ悩んでいるのが、ImporterそのものをMigration仕様の唯一の正本にしてよいのか、という点です。
ImporterとVerifierが同じ解釈・実装上の誤りを共有していた場合、誤ったMigrationでも整合しているように見えてPASSする可能性があります。
一方で、約70テーブルの詳細な変換仕様をすべて別文書として管理すると、Importerの変更に仕様書が追従できず、二重管理・形骸化するリスクもあると思っています。
そのため、
- 人間が確認すべき重要な意味変換・禁止事項をMigration Contractとして残す
- 具体的な「入力 → 期待出力」はGolden fixturesとして実行可能な形で固定する
- 詳細な変換ロジックはImporter / Verifierとテストで担保する
といった分担も候補として考えています。
実務では、「独立した仕様がない危険」と「仕様書を別管理するコスト・ドリフト」のバランスをどう取っていますか?
また、Mapping Specificationを文書として維持するのか、Golden fixturesやテストコードを実質的なExecutable Specificationとして扱うのか、その線引きも伺いたいです。
【Importer自体の試験について】
データ内容の比較だけでなく、Importerそのものについても、
- 冪等性
- duplicate防止
- partial failure
- rollback
- crash途中からの再実行
- 同じsourceを2回投入した場合
- 一部だけ成功した状態からの再実行
- unknown schema version
- 不正データ
- skip
- quarantine
- 暗黙の補正
などをMigration試験に含めるべきかと考えています。
特に、
「Migration中にImporterがデータを暗黙に補正・skipしたにもかかわらず、正常終了した」
というケースは、エラーで停止するケースより発見しづらいのではないかと考えています。
【約70テーブルをすべて同じ密度で検証するべきか】
今回のSQLite設計は約70テーブルありますが、すべてを同じ密度で検証する必要があるのかについても迷っています。
例えば、
- 金額・数量
- 在庫
- 権限
- 状態遷移
- 履歴・監査
- ID・参照関係
- 複雑な変換を伴うデータ
などは、壊れた場合の業務影響が大きいため、Canonical全件比較やInvariantsまで含めて厳密に検証する。
一方で、比較的単純なマスターデータ等については、件数・ID集合・ハッシュ等を中心に検証する。
といったRisk-basedな重点配分の方が実務的なのではないかとも考えています。
ただし、「マスターだから軽くてよい」「ログだから重要度が低い」と一律には考えず、
- 壊れた場合の業務影響
- 変換の複雑さ
- 復旧の難しさ
- 他データへの波及範囲
などから検証密度を決めるべきなのではないかと思っています。
実際のMigrationでは、どのような基準で「全件・厳密に検証する領域」と「件数・集計・ハッシュ等で確認する領域」を分けていますか?
【最終的なGO / NO-GOをどう決めるのか】
最終的には、例えば、
- Importer完走
- SQLite integrity / FK / constraint PASS
- Canonical全件比較 PASS
- Independent Reconciliation PASS
- Business Invariants PASS
- 必要なApplication Regression PASS
- CUTOVER ALLOWED
のように、複数の検証結果をCutover条件にすることを考えています。
逆に、業務上重要なデータで1件でも説明不能な差異が残れば、CUTOVER BLOCKEDとして止めるイメージです。
ただし、ここまでやると今度は、
Migration本体よりVerification Frameworkの方が巨大になる
可能性もあるため、実務上の線引きが分かりません。
【実務ではどこまでやりますか?(質問)】
実際のMigrationでは、皆さんは何を満たしたら**「移行成功」**と判定していますか?
例えば、
- 全件比較できる規模なら、本当に全件比較するのか
- 全件比較していても、別途Reconciliationを行うのか
- Canonical comparisonとReconciliationをどの程度独立させるのか
- 業務Invariantsをどの粒度まで作るのか
- Golden fixturesのような期待値固定データを用意するのか
- Verifier自体にNegative / Mutation Testを行うのか
- Importerの冪等性・途中失敗・再実行までMigration試験に含めるのか
- Migration Mapping / SpecificationをImporterとは独立して持つのか
- Migration後の照合ログ・レポートを証跡として保存するのか
- 最終的なGO / NO-GOを何を基準に判断するのか
など、実際の現場での**「合格ライン」**が知りたいです。
特に今回のように全件比較が現実的に可能な規模の場合、
「全件比較できるなら全件比較すれば十分」なのか、それとも全件比較できても、それだけでは検証できないものがあるのか
という点を知りたいです。
また、
- **「理屈では正しいけれど、実務ではそこまでやらない」**という線引き
- **「その検証項目では○○が抜けている。実際に事故るのはそこ」**という落とし穴
- **「そこまでVerification Frameworkを作るより、この検証を優先した方がよい」**という優先順位
などもあれば、ぜひ伺いたいです。
【実際にハマった失敗談も知りたいです】
教科書的なBest Practiceだけでなく、
「設計上は問題なさそうだったけれど、実際のMigrationではここで事故った」
という経験談があれば、特に伺いたいです。
例えば、
- 文字コード
- Unicode正規化
- timezone
- timestamp precision
- numeric precision
- 暗黙の型変換
-
"00123"→123のような先頭ゼロ消失 - NULL / missing / empty string
- boolean表現
- 配列・履歴の順序
- duplicate ID
- orphan
- legacy data
- schema version
- unknown fields
- SQLite locking
- transaction境界
- Importerによる暗黙補正
- silent skip
- 再実行時の二重登録
などです。
Migrationを実際に経験して初めて分かるような失敗談も歓迎です。
【最後に】
今回のデータ量では、性能上は全件比較が可能です。
そのため、
「サンプリングで十分か?」
というより、
「全件比較できる状況でも、全件比較だけでは証明できないものは何か?」
を特に知りたいです。
また、約70テーブルすべてを同じ熱量で検証するのではなく、業務上の重要度・変換の複雑さ・復旧の難しさなどに応じて検証密度を変えることも考えています。
逆に、
「そこまで検証するのは考えすぎ。実務ではここまで確認できれば十分」
という線引きも歓迎です。
構造の大きく異なるデータストア間のMigrationを経験された方が、
何を証拠として「このMigrationは成功した」と判断しているのか
実際の運用・失敗経験を含めて教えていただけると助かります。