Upgrade to Pro — share decks privately, control downloads, hide ads and more …

claudeをサブドライバーに<br>システム移行した体験

Avatar for Naka Sho Naka Sho
August 17, 2026
64

 claudeをサブドライバーに<br>システム移行した体験

オンラインくじ型のECサービス拡大のため、システム移行しました。

作業は claude code に任せ、**人は判断と、違和感を出す役**に回りました。
運転席に座るのは人のまま、**claude は交代でハンドルを握るサブドライバー**。
ハンドルは渡すが、**行き先と経路は人が決める**。

Avatar for Naka Sho

Naka Sho

August 17, 2026

Transcript

  1. はじめに 本日話すこと オンラインくじ型のECサービス拡大のため、システム移行しました。 作業は claude code に任せ、人は判断と、違和感を出す役に回りました。 運転席に座るのは人のまま、claude は交代でハンドルを握るサブドライバー。 ハンドルは渡すが、行き先と経路は人が決める。

    今日話す範囲 フォーカスするのは移行当日(8/7 20:56 〜 8/8 09:45)。 前段の local / dev / test / stg 環境の構築は、ベースは人が手で作っています。 claude / codex に任せたのは、そこで出たコーナーケースの修正だけです。 2
  2. 抱えていた問題 抱えていた問題 1. 環境問題 a. local/dev/test/stg環境がなかった prod環境しかないので、本番一発勝負 b. コード管理が適切に行われていない git管理されていないので、サーバー上でvimやsftpで修正

    c. WordPressとStreamlitの2つに分かれた管理画面 レンタルサーバーとStreamlit Cloudの2つ 2. アプリ問題 a. パフォーマンス問題 平均topページ 900ms 詰まるガチャ つまり、継続的な開発ができない、ユーザー満足度が低い 5
  3. 抱えていた問題 抱えていた問題の解消後のイメージ 旧 右にするためにやったこと local / dev / test /

    stg 環境を作成 GitHub にコードを上げて管理 1つのサーバーに管理画面を同居 DB を分離してパフォーマンス改善 新 6
  4. 抱えていた問題 移行スケジュール 日程 7/10 - 7/17 7/17 - 7/24 7/24

    - 8/7 8/8 内容 local環境構築 dev/test環境構築 stg/prod環境構築 移行 確認 現行環境(共用レンタルサーバー)の再現 テスト環境の確認、動作確認 stg/本番の確認、ツールなどの調整 移行当日 スピード感出して移行する 7
  5. 環境問題 local/dev/test/stg環境 ブランチ運用はGitHubフローに従う。mainにマージしたものはそのままprodへ反映して良いというシンプルな運用。 local — 各開発者が個別に使う環境。ローカルなのでどんな確認でも何でもできる。 dev1〜dev5 — 各開発者が個別に使う環境。日々の開発と自分のブランチの動作確認のベースになる。 test

    — mainの状態を確認する環境。特定の人には紐付かない共用環境。 stg — 本番DBに接続している環境。本番のデータでなければ確認できないときに使う。扱いに気をつける。 prod — 本番環境。tagを指定してリリースする。 8
  6. 環境問題 GitHubフローとTAG採番 (マージで⾃動実⾏) GitHub Actions ⼈がやること feature ブランチ Pull Request

    main にマージ タグを採番 v4 prod 通知 デプロイしてください 戻すとき git checkout v4 リリースノートを タグに埋め込む Chat (タグのcheckoutだけ) git checkout v3 post-checkout deploy.log に追記 フック 通知 戻すと v4 → v3 Chat v3 → v4 / タグは手で打たない — mainへのマージごとに自動採番され、リリースノートもタグに入る 番号は「祖先の最大タグ+そこからのコミット数」で決まる。マージ順と番号の大小が一致する(飛ぶことはある) prodはタグのcheckoutだけ — 戻すのも前のタグをcheckoutするだけ。前進も後退も同じ経路で deploy.log と通知に残る 9
  7. 環境問題 local環境 1. 現行サーバーにsshして apache version, DBのバージョンを確認し、ローカルにコードをすべて落とす 2. docker-compose.yml で同一バージョンを落として動作確認する

    問題 backupファイルが多すぎてどれが本物かわからない 現行サーバー側の既存の修正が先行する 現行で動いているものをとりあえず落として動かす。移行後、問題あれば現行サーバー側から取得する ※ ここから stg / prod までの環境構築は、ベースを人が手で作業している。claude / codex を使ったのは、詰まったコーナーケースの修正だ け。 10
  8. 環境問題 dev, test, stg, prod環境 1. docker-compose.dev.yml , docker-compose.prod.yml でそれぞれインフラ用に配置する

    問題 DBサーバーは? 本番のみDBサーバー用意する。dev環境は予算の関係でDB分けない MariaDB だったので、マネージドDBが使えなかった サーバーインストールは? 柔軟性を重視し、dockerで管理する。オーバーヘッドはほぼない 別問題で apache だとパフォーマンスに問題あり apache → nginx + php-fpm に構成変更 Cloudflareの導入 11
  9. 環境問題 CloudflareをPRO化 問題 メンテナンス時のDNSの切り替えの伝播 VPNによる社内と一般の接続制限 不正攻撃に対する対策 対策 メンテナンス画面を Cloudflare Snippets

    でエッジから返す(DNSを触らない) 切り替え・解除はルールのON/OFFで即時。伝播待ちゼロ、 no-store で古い503も残らない 社内/一般の出し分けを Snippet Rule の式(VPN固定IP)で判定 「一般=メンテ画面 / VPN=新オリジン」を同時に成立させ、本番ホスト名のまま確認できる 攻撃対策を Cloudflare 側へ集約 管理画面への国外アクセス遮断、REST API への不正リクエスト遮断 ログイン試行のレート制限、Super Bot Fight Mode によるボット対策 14
  10. 移行 移行手順を作成 前提条件 Cloudflare 導入済み VPN 設定済み 1Password 利用可能 手順

    — Cloudflare Snippets で、一般 / VPN の向き先を切り替えていく 段階 移行前 移行中 〃 移行後 一般 旧環境 メンテナンス メンテナンス 新環境 VPN メンテナンス メンテナンス 新環境 新環境 同時にやること — 画像ファイル / DB dump を新環境へ移行 機能テスト、一通りの正常系を確認 — 15
  11. 移行 移行リハーサル 手順は記載したので、claude code で実行する。 リハーサルでメンテナンスにするわけには行かないので、 VPN経由の場合のメンテナンスと新環境サーバーの切り替えを実施させる。 そうすることで、一般も同じことができるはず。 操作させるために 1Password

    に以下を登録する sshキー(移行前、移行後の鍵を登録して操作) Cloudflare APIキー(DNSの切り替え、メンテナンスの切り替え) テストユーザー情報(会員サイトでログインをさせる) 鍵を渡すと、指示が「何をするか」だけで済む。 ログイン方法も切り替え手順も毎回説明しなくてよくなり、 DNSの伝播・画面表示・ログの記録という結果の確認まで自分で取りにいくようになる。 16
  12. 移行 移行当日 シナリオ 1. 最終リハーサル (待機) 2. データ移行 3. 社内確認

    4. 公開 時刻 20:56 – 01:00 01:00 – 02:00 02:00 – 02:54 02:54 – 05:38 05:38 – 09:45 入力 24 – 6 26 34 操作 709 – 422 292 238 確認 本番と同じ手順を通しで実行し、手順書の間違いを潰す Cloudflare が 02:00 に自動でメンテナンスへ切り替わるのを待つ 停止確認 → cron 停止 → 画像 → DB → RENAME で切替 VPN 経由で新本番を検証。出てきた不具合を片端から潰す 公開してログの監視 操作の 68%(709回)が移行前のリハーサルに費やされている。移行本体の 422 回に対し、人が指示したのは 6 回。 17
  13. 移行 1. 最終リハーサル 時刻 20:56 22:53 23:28 ユーザー入力 移行リハーサルを最後に行いたいで す。旧環境を読み取りで新環境に画

    像・DB を挿入してください。DB は 新しいスキーマ名で、完了後に DB 名をリネームで入れ替えてくださ い。 今日のリハのスキーマは削除してく ださい MAINTENANCE_START を本番の一 般側でも設定できますか?8/8 2:00 に自動でメンテナンスページに切り 替わるように Cloudflare に事前に 設定できますか? Claude の操作 画像 rsync 1.80GB → DB ダンプ 259 秒 → 新スキーマへ投入 35 分 14 秒 RENAME TABLE でスワップ 384ms / 94ms、合計 44 分 74 テーブル中 67 が行数完全一致、差分 7 件はすべて稼働中の書き込みで説明 WordPress が再起動なしで新 DB を掴むこと、全導線 200 を確認 指定された 1 つだけ削除 もう一方は残し、「取り消せない操作は明示されたものに限定しました」と申告 22:55、言われて初めて 2 つ目を削除 開始時刻を判定式に埋めた Cloudflare Snippet を事前投入 旧環境側には一切触らず、変更はエッジのルールのみ 結果 — 02:00 に人が何かを押す必要がなくなった 18
  14. 移行 2. データ移行 時刻 02:00 02:06 02:54 ユーザー入力 時間になったのでメンテナンスに なったのを確認しました。一般ユー

    ザのアクセスがとまっていることの 確認を進めてください cron 止めて、画像、DB の移行を進 めてください。細かい手順は以下を 確認して(バックログのリンク) 社内確認をするので VPN を新環境 に向けてください(cron も起動して ください) Claude の操作 停止を 3 層で証明。① Cloudflare が 503 ② オリジン通過 0 件 ③ 旧本番 DB の書き込み系 5 テーブルを 145 秒監視して増分 0 crontab 6 ジョブを退避して削除、 DISABLE_WP_CRON 投入。画像 rsync は全 8 パス rc=0 / 転送 0 件 (前夜のリハで同期済み) 事故1 sed のクォートが shell に食われ、 wp-config.php が PHP8 で落ちる形に。直前のバックアッ プから 1 分で復元し Python で書き直し ダンプ完了 172 秒 / 1.5GB。投入前に 74 テーブル・除外 15 テーブルの混入 0 件を検証 インポート完了 34 分 44 秒 → RENAME TABLE でスワップ 380ms / 97ms。退避側にも VIEW を作り、 切り戻せる状態を維持 74 テーブル / 81,215,834 行が移行元と完全一致。ドリフト 0、超過 0 Cloudflare のルールを切替。VPN → 新環境、一般 → メンテ画面。新環境側 nginx のログに実 IP で記録 されることまで確認。ここまで 49 分 19
  15. 移行 3. 社内確認 データが入り終わってからが長い。移行本体の 3 倍の時間を、新環境で初めて表面化した不具合の切り分けに使った。 時刻 ユーザー入力 03:06 課金ができない

    03:36 差分がない状態ということですか? 03:39 アイテム画像が出ない (実ユーザーからの報告) インデックス当てたら早くなる? 02:59 04:05 PR #75 / #74 の改修を本番に入れて いるので、旧環境側の修正を取り入 れてください Claude の操作 事故2(03:11) 旧環境版 functions.php で上書きし関数が消滅、トップが 6 分間 500。「差分 0 行」 を「上書きしても失うものがない」と誤読 revert して 94 関数を突き合わせ、両版に固有だった 6 関数ずつを手で合成(03:29) 犯人はメンテページ自身。 /charge はサーバーが Cloudflare 経由で自サイトを叩くため、自分の 503 を食っていた → ルール修正で 200 「一致しているのはコードだけです」と訂正。test環境に TURNSTILE_SECRET の配信漏れ=会員登録 が必ず失敗する状態だったことを自己申告 移行ではなく2 日前の最適化 PR が原因。添付の guid に旧サイト構成の URL が残存。添付 117 件 / ア イテム 567 件を修正 退避 DB(4,163 万行)で先に実測してから本番へ。4,128 万行の全走査 6.3 秒 → 0.147 秒。 LOCK=NONE で無停止、実行中もトップは 200(04:13 完了) 20
  16. 移行 4. 公開 時刻 ユーザー入力 Claude の操作 05:38 では一般公開を進めてください 05:41

    1. エラーが発生していないか 2. 旧 環境側に人が流れていないか 今一般ユーザのアクセス数は秒速ど れくらいですか? 実顧客の問い合わせ 「10万円振り込んだが反映されな い」 監視終了で DNS の A レコードを 旧環境 → 新環境へ、同時にメンテナンスルールを無効化。「メンテのまま」も 「旧本番へ流入」も起きないよう連続実施。05:45 に全導線 200 公開後の 5xx 0 件(500 は 10 件あるがすべて 03:14 の自分の事故のもの)。旧 DB は 02:03 以降 1 行も書 き込まれていないことを 2 回測って確認し、 .htaccess で旧本番を 503 に固定 公開の瞬間に 0.02 → 4.4 req/秒(約 60 倍)。負荷試験の限界 31.6 req/秒に対し 7 倍の余裕。待ってい た利用者が戻ってきた 読み取りだけで原因を特定し、課題に起票して補填まで実行 → 次ページ 05:47 08:42 09:45 ありがとうございました 21
  17. 移行 42秒の穴 旧環境の最終書き込み 01:59:26 / 失われた入金確定 02:00:08 銀行振込のポイント付与は、決済代行からの webhook 受信が唯一の起点。

    メンテナンスの 503 に当たって消えた 翌朝 08:42、実顧客から「深夜 2 時少し前に 10 万円振り込んだが反映されない」 補填は SQL の直 INSERT ではなく、webhook ハンドラと同じ関数を wp-cli から実行(冪等チェック込み)。 568pt → 106,568pt 09:33、移行後はじめて本物の webhook が着弾。経路が生きていることを確認 止めるのは自分たちのトラフィックだけじゃない。 「メンテ明けに未着 webhook を拾う」を手順に入れる。 22
  18. 移行 claudeはなにをやったのかをまとめる 移行本体は 6 回しか喋らない リハーサルは 手順書のテスト 自己申告が いちばんの価値 先回りさせない

    02:00 から 02:54 のあいだ、人間が入力したのはたった 6 回です。「確認して」「cron 止めて移行して」「VPN を向けて」 ——それだけで 422 回の操作が走りました。 そうできたのは前日までに全部決めてあったからです。当日に判断が残っていたら、この密度では回りません。 5 時間前のリハで見つかったのは、性能の問題ではなく手順書の間違い 4 件でした。3 日前に廃止した .env を読む手 順、終了コードを見ない rsync、止まっていない書き込み。 本番の実測値がリハとほぼ一致したことが、手順が正しく機能した証拠になりました。 この夜、Claude は自分から何度も報告しています。「トップページを 6 分間 500 にしました、原因は私の判断ミスです」 「一致しているのはコードだけです」「その Warning は私のプローブです」「www が 503 と報告しましたが私の計測ミス でした」。 作業量より、作業ログの正直さのほうが効きます。深夜に一人で判断するとき、頼れるのは相手の報告だけなので。 「バックアップ DB を削除して」に対し、Claude は指定された 1 つだけを消し、もう一方は「取り消せない操作は明示さ れたものに限定しました」と残しました。 親切な先回りが最悪の結果になるのが移行作業です。不可逆な操作は明示されたぶんだけ——この線引きを最初に合意して おく価値があります。 23
  19. まとめ 抱えていた問題を解消 1. 環境問題 a. local/dev/test/stg環境がなかった prod環境しかないので、本番一発勝負 → local, dev,

    test, stg, prod 作成 b. コード管理が適切に行われていない git管理されていない → GitHub移行、GitHubフローで開発 c. WordPressとStreamlitの2つに分かれた管理画面 レンタルサーバーとStreamlit Cloudの2つ → 同一サーバーに移行 2. アプリ問題 a. パフォーマンス問題 平均topページ 900ms → 450ms (CDN配信・nginx/php-fpm分離・インデックス追加の合算) 詰まるガチャ → 新規システムに切り替え中 24