1 分

Claude Code PostgreSQL マイグレーション:安全な変更のためのプロンプト

Claude Code を使った PostgreSQL マイグレーションのプロンプト。expand-contract の安全な変更、バックフィル、ロールバック計画、リリース前にステージングで確認すべき項目を解説します。

Claude Code PostgreSQL マイグレーション:安全な変更のためのプロンプト

PostgreSQL のスキーマ変更が危険になるのは何が原因か\n\nPostgreSQL のスキーマ変更は見た目は単純でも、本番のトラフィックや実データにぶつかると問題になります。危険なのは多くの場合 SQL 自体ではなく、アプリのコード、データベースの状態、デプロイのタイミングが一致しなくなることです。\n\n実際の失敗は現実的で痛いものが多いです:古いコードが新しいカラムに触れてデプロイが壊れる、マイグレーションがホットなテーブルをロックしてタイムアウトが増える、あるいは「簡単な」変更が気づかれないままデータを消したり上書きしたりする。何も落ちなくても、間違ったデフォルト、壊れた制約、完成しなかったインデックスのような微妙なバグを出すことがあります。\n\nAI によるマイグレーション生成はさらに別のリスク層を追加します。ツールは有効な SQL を生成できますが、それがあなたのワークロード、データ量、リリースプロセスに対して安全であるとは限りません。テーブル名を推測したり、長時間ロックを見落としたり、ダウンマイグレーションを手短に済ませてしまうこともあります。Claude Code をマイグレーションに使うなら、ガードレールと具体的なコンテキストが必要です。\n\nこの投稿で「安全」と言うときは次の三点を意味します:\n\n- 後方互換性:ロールアウト中に古いバージョンと新しいバージョンが両方動けること\n- 可観測性:進捗を測定し、問題を素早く検出できること\n- 可逆性:プレッシャー下でも実行できるロールバック計画があること\n\n目標はマイグレーションをルーチン作業にすることです:予測可能で、テスト可能で、つまらないほど安定した作業にすることです。\n\n## プロンプトを書く前に守るべき安全ルール\n\nいくつかの譲れないルールから始めてください。これらはモデルを集中させ、ローカル環境でしか動かない変更を出荷するのを防ぎます。\n\n作業を小さく分けること。スキーマ変更、データバックフィル、アプリ変更、クリーンアップはそれぞれリスクが異なります。束ねると何が壊れたか見えにくく、ロールバックもしにくくなります。\n\n破壊的変更よりまず追加を優先すること。カラムやインデックス、テーブルの追加は通常リスクが低いです。名前変更や削除は障害の元です。安全な部分を先に行い、アプリを移行してから古いものを削除します。\n\nアプリがしばらく両方の形を許容するようにすること。コードはロールアウト中、古いカラムと新しいカラムのどちらでも読めるべきです。これで一部のサーバーが新しいコードを実行している間にデータベースが古いまま、というよくある競合を避けられます。\n\nマイグレーションを短いスクリプトではなく本番コードとして扱ってください。たとえ Koder.ai のようなプラットフォーム(Go バックエンドと PostgreSQL、React/Flutter クライアントなど)を使っていても、データベースはすべてに共有されています。ミスは高くつきます。\n\nSQL リクエストの先頭に置くコンパクトなルール集合が欲しいなら、次のようなものを使ってください:\n\n- マイグレーションは一変更ずつ:expand、次に backfill、次にコード切替、最後にクリーンアップ。\n- 長時間のロックを避ける:並行インデックス作成や小さなバッチ更新を使う。\n- すべてのステップにロールバック計画を要求する(バックフィル中止方法含む)。\n- 検証クエリと成功指標(行数、NULL 比率、実行時間)を必須にする。\n- 実行手順書(ランブック):どう実行するか、何を監視するか、誰に連絡するか。\n\n実践例:依存するカラムの名前を直接変える代わりに、新しいカラムを追加してゆっくりバックフィルし、新しいカラムを先に読んで古いカラムにフォールバックするようコードをデプロイし、最後に古いカラムを削除する、という流れにします。\n\n## Claude Code を現実に即したものにするためにプロンプトに含めるべき情報\n\nClaude は漠然としたリクエストからもそれなりの SQL を書けますが、安全なマイグレーションにはコンテキストが必要です。プロンプトはミニ設計書のように扱ってください:現状、壊してはいけないもの、ロールアウトで「安全」が何を意味するかを示します。\n\nまず、必要なデータベース情報だけを貼り付けます。テーブル定義と関連するインデックスや制約(PK、UNIQUE、FK、CHECK、トリガー)を含めてください。関連テーブルがあればそのスニペットも。正確な断片を渡すことでモデルの推測(名前をでっち上げる等)を防げます。\n\n実運用のスケール情報を加えてください。行数、テーブルサイズ、書き込み率、ピークトラフィックは計画を変えます。"200M 行で毎秒 1k 書き込み"は"20k 行で主に読み取り"の場合と異なります。Postgres のバージョンやマイグレーションをどう実行するか(単一トランザクションか複数ステップか)も伝えてください。\n\nアプリがデータをどう使うかを説明してください:重要な読み取り、書き込み、バックグラウンドジョブです。例:"API が email で読み取る"、"ワーカーが status を更新する"、"レポートが created_at でスキャンする"。これらが expand/contract、フィーチャーフラグ、バックフィルの安全性を決めます。\n\n最後に制約と納品物を明示します。シンプルな構成が有効です:\n\n- 現在のスキーマ断片とゴール\n- スケール想定(行数、書き込み/秒、メンテ窓)\n- アプリの依存(クエリ/エンドポイント/ジョブ)\n- ハード制約(ダウンタイム不可、長時間ロック回避、全表書き換え回避)\n- 納品物:SQL と平易な実行計画、検証、ロールバック\n\nSQL と実行計画の両方を要求すると、モデルは順序やリスク、出荷前に確認すべきことを考えるようになります。\n\n## expand/contract を平易に(いつ使うか)\n\nexpand/contract マイグレーションパターンは、変更が進行中でもアプリを壊さないように PostgreSQL を変更する手法です。単一の危険な切り替えをする代わりに、しばらくの間データベースが旧スキーマと新スキーマの両方をサポートするようにします。\n\n要するに:まず安全に新しいものを追加(expand)、トラフィックとデータを段階的に移動し、最後に古い部分を取り除く(contract)。AI 支援の作業ではこのパターンが有効で、途中のややこしい状態を計画させる強制力になります。\n\n### 4 つのフェーズ\n\n実用的な流れは次の通りです:\n\n- Expand:nullable な新しいカラムやテーブルを追加し、必要ならインデックスを追加する。ブロックを避けるために制約は NOT VALID のように段階的に追加する。\n- Compatibility:アプリを更新して古い/新しい両方に対応させる。dual-write(両方へ書く)や read-fallback(新しいものを読んでなければ古いものにフォールバック)など。\n- Backfill:小さなバッチで古いデータを新しい場所にコピーし、チェックポイントと再開方法を用意する。\n- Contract:すべてが新しい経路を使っていると確信できたらルールを厳しくし(NOT NULL にする、制約を VALIDATE する)、古いカラムやテーブルを削除する。\n\nモバイルのようにゆっくり更新されるクライアントやマルチインスタンスデプロイ、あるいはマイグレーションに数分〜数時間かかるリリースではこのパターンを使ってください。\n\n実用的なコツは 2 回のリリースを計画することです。リリース1 で expand と compatibility を行い、バックフィルが未完でも壊れないようにします。リリース2 で新コードと新データが整ったことを確認した後 contract を実行します。\n\n## expand/contract マイグレーション用の安全なプロンプトテンプレート\n\nコピーして角括弧を埋めるテンプレートです。Claude Code に実行可能な SQL、検証方法、現実的なロールバック計画を出させることを目的にしています。\n\n```

You are helping me plan a PostgreSQL expand-contract migration.

Context

  • App: [what the feature does, who uses it]
  • Database: PostgreSQL [version if known]
  • Table sizes: [rough row counts], write rate: [low/medium/high]
  • Zero/near-zero downtime required: [yes/no]

Goal

  • Change: [describe the schema change]
  • Current schema (relevant parts): [paste CREATE TABLE or \d output]
  • How the app will change (expand phase and contract phase):
    • Expand: [new columns/indexes/triggers, dual-write, read preference]
    • Contract: [when/how we stop writing old fields and remove them]

Hard safety requirements

  • Prefer lock-safe operations. Avoid full table rewrites on large tables when possible.
  • If any step can block writes, call it out explicitly and suggest alternatives.
  • Use small, reversible steps. No “big bang” changes.

Deliverables

  1. UP migration SQL (expand)

    • Use clear comments.
    • If you propose indexes, tell me if they should be created CONCURRENTLY.
    • If you propose constraints, tell me whether to add them NOT VALID then VALIDATE.
  2. Verification queries

    • Queries to confirm the new schema exists.
    • Queries to confirm data is being written to both old and new structures (if dual-write).
    • Queries to estimate whether the change caused bloat/slow queries/locks.
  3. Rollback plan (realistic)

    • DOWN migration SQL (only if it is truly safe).
    • If down is not safe, write a rollback runbook:
      • how to stop the app change
      • how to switch reads back
      • what data might be lost or need re-backfill
  4. Runbook notes

    • Exact order of operations (including app deploy steps).
    • What to monitor during the run (errors, latency, deadlocks, lock waits).
    • “Stop/continue” checkpoints.

Output format

  • Separate sections titled: UP.sql, VERIFY.sql, DOWN.sql (or ROLLBACK.md), RUNBOOK.md \n\n二つの補助行(実務で役立ちます):\n\n- 書き込みをブロックするステップには `RISK: blocks writes` とラベルを付け、いつ実行するか(オフピークなど)を示させる。\n- ロックについて不確かなら「この文が ACCESS EXCLUSIVE ロックを取るか不明ならそう書いて、より安全な代替を示して」と強制する。\n\n## よくあるスキーマ操作と安全な SQL を引き出すプロンプトの書き方\n\n小さなスキーマ変更でも、長時間のロックや大規模なテーブル書き換え、途中失敗で害が出ます。Claude Code に依頼するときは、書き換えを避けつつアプリが追いつく間に安全に動く SQL を求めてください。\n\n### デフォルト付きカラムの追加(長時間ロックを避ける)\n\nnullable カラムの追加は通常安全です。非 NULL のデフォルトを即座に設定すると、古い PostgreSQL ではテーブル全体を書き換えてしまうことがあり危険です。\n\n安全な手順は 2 ステップ:まず NULL 許容でデフォルトなしのカラムを追加し、バッチでバックフィルし、その後新規行のデフォルトを設定して NOT NULL を追加します。\n\n即時デフォルトが絶対に必要なら、Postgres のバージョンごとのロック挙動の説明と、想定より長くかかった場合の代替案を要求してください。\n\n### インデックス、FK、制約、削除\n\n大きなテーブルへのインデックスは `CREATE INDEX CONCURRENTLY` を要求してください。これにより読み書きは継続しますが、トランザクション内では実行できない点に注意してください。\n\n外部キーは通常 `NOT VALID` として追加し、後で `VALIDATE` する方が安全です。これで初期変更は速くなり、新しい書き込みには制約が効きます。\n\n制約を厳しくする(NOT NULL、UNIQUE、CHECK)ときは「まずクリーンアップ、次に適用」の流れにしてください。マイグレーションは不整合行を検出して修正し、それから厳格化するべきです。\n\n短いチェックリストをプロンプトに貼るなら:\n\n- ロックと想定実行時間について注記する\n- 大きなインデックスは CONCURRENTLY を使い、トランザクション制約を明示する\n- 新しい FK は NOT VALID で追加し後で VALIDATE する\n- バックフィルと NOT NULL/UNIQUE の適用は分離する\n- オブジェクトはフルリリースサイクル後かつ参照されていないことを確認してから削除する\n\n## ゆっくり・安全に再開可能なデータバックフィルのプロンプト\n\nバックフィルが最も痛く出ることが多く、`ALTER TABLE` よりバックフィル自体の設計が重要です。安全なプロンプトはバックフィルをジョブとして扱います:測定可能で再開可能、かつ本番に優しい。\n\nまず受け入れ基準を決めてください:期待行数、目標 NULL 率、20 件程度のランダム ID 比較などのスポットチェック。\n\n次にバッチ戦略を要求します。バッチはロックを短くし、驚きを減らします。良いリクエストは次を具体的に示します:\n\n- バッチ方法(PK 範囲や created_at の時間窓)\n- 目標バッチサイズ(例:5,000〜50,000 行)\n- ホットなテーブルではバッチ間にスリープを入れるかどうか\n- 各バッチは短いトランザクションにする(巨大な一回のトランザクションは避ける)\n\nバックフィルは冪等であることを要求してください。中断しても再実行して重複や破損を招かない設計にします。典型パターンは「new_col IS NULL の行だけ更新する」や、入力から常に同じ出力が得られる決定的な処理です。\n\nまた、バックフィル中にアプリが正しく動く方法を明確にしてください。新規書き込みが続くなら、アプリ側での dual-write、トリガー、または read-fallback(new を優先してなければ old)を使う必要があります。どれが安全にデプロイできるのかを指定してください。\n\n最後に一時停止と再開を設計に組み込んでください。進捗を示す小さなテーブル(最後に処理した ID を保存する等)と、進捗を報告するクエリ(更新済み行数、最後の ID、開始時刻)を要求します。\n\n例:`users.full_name` を `first_name` と `last_name` から作る場合。安全なバックフィルは `full_name IS NULL` の行だけを ID 範囲で 1,000 行ずつ更新し、最後に処理した ID を記録し、新規登録は dual-write で整合性を保つ、というような設計です。\n\n## 実際に役立つロールバック計画のプロンプト\n\nロールバック計画は単に「下向きのマイグレーションを書く」だけではありません。問題は二つ:スキーマ変更を元に戻すことと、新しいバージョンが動いている間に変わったデータをどう扱うか、です。スキーマのロールバックは可能なことが多いですが、データのロールバックは事前に計画していなければ不可能なことが多いです。\n\nロールバックで何を意味するかを明確にしてください。カラムを削除したり値をインプレースで書き換えたりするなら、「ロールバックでアプリ互換性は戻せるが、元のデータはスナップショットがなければ復元できない」等の現実的な答えを要求してください。誠実な説明が安全性を高めます。\n\n決断を迷わせないためにロールバックトリガーを明確にしてください。例:\n\n- エラー率やレイテンシが 10 分間所定の閾値を超えた\n- 重要クエリのプランが劣化(ホットテーブルでのシーケンシャルスキャン等)\n- バックフィルが N 時間以上遅延している\n- データ検査が失敗(NULL や重複、欠落行)\n- マイグレーションが X 秒以上書き込みをブロックしている\n\n下向き SQL(安全なら)に加え、アプリ設定/コードの切り替え方法、バックグラウンドジョブの停止方法なども含めた完全なロールバックパッケージを要求してください。\n\nこのようなプロンプトがあれば十分です:\n\n Produce a rollback plan for this migration. Include: down migration SQL, app config/code switches needed for compatibility, and the exact order of steps. State what can be rolled back (schema) vs what cannot (data) and what evidence we need before deciding. Include rollback triggers with thresholds. \n\n出荷前に軽い「安全スナップショット」を取っておくと比較が簡単になります:\n\n- 影響を受けるテーブルの行数(および重要なサブセット)\n- サンプルクエリと期待結果の小セット\n- 変更カラムの簡単な集計(sum、min/max)\n- 事前/事後でスポットチェックするための ID リスト\n\nまた、「いつロールバックすべきでないか」も明記してください。例えば、nullable カラムを追加してアプリが dual-write している場合は、ロールバックより前進で修正(ホットフィックス、バックフィル一時停止、再開)の方が安全なことが多いです。\n\n## AI 支援マイグレーションで気をつける一般的なミス\n\nAI は高速に SQL を書けますが、本番データベースを見ているわけではありません。多くの失敗はプロンプトが曖昧でモデルが穴埋めしてしまうことに起因します。\n\nよくある落とし穴は現在のスキーマを省略することです。テーブル定義やインデックス、制約を貼らないと、生成された SQL が存在しないカラムを使ったり、一意性ルールを見落としてバックフィルがロック地獄になることがあります。\n\nまた expand、backfill、contract を一度に出荷するのも危険です。これだと逃げ道がなくなります。バックフィルが長引いたり途中で失敗した場合に、最終状態を期待するアプリが先行してしまうと元に戻せません。\n\nよくある問題点:\n\n- 冪等でなく進捗追跡がないバックフィル\n- データをクリーンにしてから NOT NULL / UNIQUE / FK を追加していない\n- ロックタイムアウトやステートメントタイムアウトがない長時間トランザクション\n- 検証クエリがなく問題がユーザに見つかるまで潜む\n\n具体例:「カラム名をリネームしてアプリを更新する」生成されたプランがリネームとバックフィルを単一トランザクションでやると、長いバックフィルがロックを保持してライブトラフィックを壊します。安全なプロンプトは小さなバッチ、明示的なタイムアウト、最終パス削除前の検証クエリを強制します。\n\n## 出荷前にステージングで確認すること\n\nステージングは小さな開発 DB では出ない問題を見つける場所です:長時間ロック、予期しない NULL、欠落インデックス、見落とされたコードパス。\n\nまず、マイグレーション後にスキーマが計画通りか確認してください:カラム、型、デフォルト、制約、インデックス。表面的な確認では十分ではありません。1 つの欠落インデックスが安全なバックフィルを遅くすることがあります。\n\n次に実データに近いデータセットでマイグレーションを実行してください。理想は本番の最近コピー(機微なフィールドはマスク)です。できない場合でも、少なくとも本番のボリュームやホットスポット(大きなテーブル、広い行、インデックス多用)を模すべきです。各ステップのタイミングを記録して本番の見積もりに役立てます。\n\n短いステージングチェックリスト:\n\n- スキーマが計画通り(カラム、型、制約、インデックス)\n- 実データボリュームでの各ステップの実行時間を記録\n- 互換性のテスト:古いアプリ+新スキーマ、新アプリ+旧スキーマ(計画どおりなら両方動くか)\n- 検証クエリ実行:NULL 率、行数、FK の孤児チェック、サンプル読み取り\n- 実行中にみる運用指標:ロック、デッドロック、タイムアウト、遅いクエリ\n\n最後に、SQL だけでなく実際のユーザフロー(作成、更新、読み取り)をテストしてください。expand/contract が計画なら、最終クリーンアップまで両方のスキーマが動作することを確認します。\n\n## 現実的な例:ユーザを壊さずにカラムを分割する\n\n`users.name` に "Ada Lovelace" のようなフルネームが入っているとします。`first_name` と `last_name` に分けたいが、サインアップやプロフィール、管理画面をロールアウト中に壊せません。\n\nまず expand ステップで安全に始めます:nullable な新カラムを追加し、古いカラムは残し、長時間のロックを避けます。\n\nsql ALTER TABLE users ADD COLUMN first_name text; ALTER TABLE users ADD COLUMN last_name text; ```\n\n次にアプリの挙動を両スキーマに対応するように更新します。リリース1 では新しいカラムがあればそれを読み、なければ name にフォールバックし、新規データは両方に書くようにします。\n\nその後バックフィルを行います。小さなチャンクずつ更新し、進捗を記録して安全に一時停止・再開できるジョブを走らせます。例えば first_name IS NULL の行を ID 昇順で 1,000 行ずつ更新し、更新数をログに残すなどです。\n\n制約を厳しくする前にステージングで検証してください:\n\n- 新規登録が first_namelast_name を埋め、同時に name も設定する\n- 既存ユーザは name のみでも正しく表示される\n- バックフィルは停止・再開しても重複作業をしない\n- 完了後に予期せぬ NULL が残らない\n- users に対する基本クエリが著しく遅くならない\n\nリリース2 で読み取りを新カラムオンリーに切り替えます。制約(SET NOT NULL)や name の削除はさらに後の別デプロイで行うのが望ましいです。\n\nロールバックは地味にしておきましょう。移行中はアプリが name を読み続け、バックフィルは中断可能にしておきます。もしリリース2 を巻き戻す必要があれば、読み取りを name に戻し、新しいカラムは残したまま安定するまで待ちます。\n\n## 次のステップ:プロンプトを繰り返し可能なマイグレーションルーチンにする\n\n各変更を小さなランブックとして扱ってください。目的は完璧なプロンプトではなく、スキーマ・制約・実行計画・ロールバックの正しい詳細を強制するルーチンを作ることです。\n\nすべてのマイグレーション要求で標準化する項目を決めてください:\n\n- 現在のスキーマと正確な変更点(テーブル、カラム、インデックス)\n- 制約とトラフィック事実(テーブルサイズ、書き込み率、許容ダウンタイム)\n- リリース順序(expand、アプリデプロイ、backfill、contract)\n- 進捗の可視化方法(クエリ/メトリクス、期待実行時間)\n- ロールバック手順(何を最初に戻すか、どのデータが残るか)\n\n誰が各ステップを担当するかを事前に決めておくと「誰かがやるだろう」問題を防げます:開発がプロンプトとマイグレーションコード、運用が本番タイミングと監視、QA がステージング検証、そして最終の go/no-go を決める一人を置く、など。\n\nチャットでアプリを作る場合は、SQL を生成する前に順序を書き出しておくと助かります。Koder.ai を使うチームなら Planning Mode にシーケンスを書き残し、スナップショットとロールバック計画を用意しておくことでロールアウト時の被害を小さくできます。\n\n出荷後はコンテキストが新鮮なうちにすぐ contract のクリーンアップをスケジュールし、古いカラムや互換性コードが数ヶ月残らないようにしてください。

よくある質問

Why do PostgreSQL schema changes break production even when the SQL looks simple?

スキーマ変更は アプリのコード、データベースの状態、デプロイのタイミングが一致しなくなったときに 危険になります。

よくある失敗パターン:

  • 古いアプリコードが新しいカラムや制約に触れてクラッシュする
  • マイグレーションが繁忙テーブルに強いロックを取り、リクエストがタイムアウトする
  • 「小さな」変更がデータを静かに上書きしたり削除したりする
  • インデックスや制約の作業が予想より長引き、クエリが遅くなる
What’s the safest default way to change a schema without downtime?

安全なデフォルトは expand/contract アプローチです:

  • Expand: 互換性を保ちながら nullable なカラム/テーブル/インデックスを追加する
  • Compatibility: 両方のスキーマを扱えるようにアプリをデプロイする(読取り/書込みの両対応)
  • Backfill: 小さなバッチでデータを移し、チェックポイントを設ける
  • Contract: フルリリース後に制約を厳しくし、古いフィールドを削除する

この方法ならリリース中も古い・新しいバージョンの両方が動作し続けます。

What extra risks do AI-generated migrations introduce?

モデルは正しい SQL を生成できますが、それが あなたの実際のワークロードに対して安全かどうかは別問題 です。

AI 特有のリスク:

  • テーブル名やカラム名を推測してしまう
  • ロールバック手順を軽視し、“一発で”やってしまう案を出す
  • ロック挙動やトランザクション制限、長時間のインデックス作成を無視する
  • データ変換/削除が起きたときの現実的な復旧計画を書かない

AI の出力は草案として扱い、必ず実行計画(ランブック)と検証・ロールバック手順を要求してください。

What should I paste into my prompt so Claude Code doesn’t guess?

プロンプトにはマイグレーションで依存する事実だけを入れてください:

  • 関連する CREATE TABLE スニペット(インデックス、FK、UNIQUE/CHECK、トリガー含む)
  • Postgres のバージョンとマイグレーションの実行方法(単一トランザクションか複数ステップか)
  • スケール情報:行数、テーブルサイズ、書き込み率、ピークトラフィック
  • アプリのデータ利用方法(重要な読み取り/書き込み/ジョブ)
  • ハード制約(ダウンタイム不可、テーブル全体書き換え禁止、ロック制限など)
  • 求める成果物:UP SQL + 検証クエリ + ロールバック計画 + ランブック

これでモデルの推測を減らし、正しい順序を引き出せます。

Should I combine schema changes and backfills in one migration?

原則として 分ける べきです。

実践的な分割:

  • マイグレーション 1: スキーマの expand(新しいカラム/テーブル、場合によっては NOT VALID 制約)
  • アプリデプロイ: 互換性コード(読取フォールバックや dual-write)
  • バックフィル: 進捗追跡付きのバッチ更新
  • マイグレーション 2: contract(制約検証、NOT NULL 設定、古いカラム削除)

一つに詰め込むと障害発生時にロールバックや原因特定が難しくなります。

How do I add a new column with a default without causing long locks?

推奨パターン:

  1. ADD COLUMN ... NULL(デフォルトなし、速い)
  2. バッチでバックフィル
  3. 新規行用のデフォルトを設定
  4. 検証後に NOT NULL を追加

一部の古い Postgres バージョンでは非 NULL デフォルトの追加がテーブル全体書き換えを引き起こすため注意が必要です。即時デフォルトが必要な場合はロック挙動の説明とフェールバック案を求めてください。

When should I use CREATE INDEX CONCURRENTLY, and what’s the catch?

大きな/ホットなテーブルでは CREATE INDEX CONCURRENTLY を使ってください。注意点:

  • CONCURRENTLY はトランザクションブロック内で実行できません(ツールが非トランザクションステップをサポートする必要あり)
  • 期待される実行時間と監視すべき指標(ロック待ち、クエリ遅延)を明示する

検証ではステージングで EXPLAIN 等を比較してインデックスが使われていることを確認してください。

What’s the safest way to add a foreign key on a large table?

安全な方法はまず NOT VALID で追加し、後で VALIDATE CONSTRAINT を別ステップで走らせることです。

  • 最初の追加は軽くて速い(新しい書き込みには制約が適用される)
  • 重い検証は制御できるタイミングで実行する
How do I prompt for a backfill that won’t melt production and can resume?

良いバックフィルは バッチ化、冪等、再開可能 であるべきです。

実践的要件:

  • プライマリキー範囲や作成日時などでバッチ化する
  • まだ処理が必要な行だけ更新する(例: WHERE new_col IS NULL
  • 各バッチは短いトランザクションにする。必要なら各バッチ間で休止を入れる
  • 進捗を追う(最後に処理した ID、更新行数、開始時刻)
  • バックフィル中もアプリが正しく動く方法(dual-write、トリガー、読取りフォールバック)を用意する

これで本番トラフィックでの耐久性が増します。

What does a realistic rollback plan look like for schema changes?

現実的なロールバック目標は アプリ互換性を素早く復元すること であり、データを完全に元に戻せるとは限らない点を明確にするべきです。

良いロールバック計画に含めるべき事項:

  • DOWN SQL が本当に安全かどうか。安全でなければランブックにする
  • 正確な順序:バックフィル停止→アプリ差し替え→スキーマ操作等
  • ロールバックトリガー(エラー率、レイテンシ、ロック待ち、データ検証失敗など)
  • 何がロールバック可能か(スキーマ)と何が不可能か(データ)の明示

多くの場合、古いフィールドに読み戻す(reads back to old field)方が、新しいカラムを削除して完全復元するより安全です。

Related posts