公開・本番移行学習記事全11節Operate
コードの切戻しとデータの復元を分けて書き、運用担当へ引き渡す
この記事で身につくことコードを旧版に戻すことと、データをバックアップへ戻すことの違い、戻せる範囲と戻らない変更、引渡しの意味を説明でき、切戻しの演習を再現して「実際に戻せた」ことを確かめられる。
記事内目次(23項目)
この記事の会社・人物・データはすべて架空の設定です。実在する会社や案件とは関係ありません。この記事の切戻しは、11-02 で配置したローカルの配置サンドボックスに対して実際に行った演習であり、実際の運用環境での切戻しではありません(第7節)。「新版」として配置する
approval_v0.1.1-broken.jsは、配置後の確認で不一致が出る流れを再現するための説明用の失敗例で、実際に開発した新版でも、実際のAI出力でもありません。
この記事は何の話か。11-02 で配置したアプリに問題が出た場合に備えて、コードを旧版に戻す手順とデータをバックアップへ戻す手順を分けて書き、コードの切戻しをローカルで実際に演習して「戻せること」を確かめたうえで、初期安定を確認して運用担当へ引き渡す記録を作る話です。
なぜ必要か。配置した後に問題が見つかっても、戻す手順が決まっていなければ、混乱した対応になってしまいます。手順が書いてあるだけでは「戻せる」とは言えず、一度も試していない手順は、いざというときに動かないことがあります。コードとデータは戻し方が違うため、分けて準備しておく必要があります。
この記事を読むとできるようになること。
- コードを旧版に戻すことと、データをバックアップへ戻すことの違いを説明できる
- 切戻し手順(戻す範囲、戻らない変更の扱い)を書き、引渡し記録を作れる
- 切戻しの演習を実行し、切戻し後の確認結果から「コードは戻った・データは戻っていない」を読み取れる
- 切戻しを可能にする準備の手間と、切戻しできない場合の影響を比較して判断できる
読む順路。この記事は3つの深さで読めます。全部を順に読む必要はありません。
- 基本だけ理解する:第1節→第3節(コードの切戻しとデータの復元の違い、「手順がある」と「戻せることを確認した」の違い)→第4節(判断すること)→第5.1〜5.2節(切戻し手順と戻らない変更の扱い)→第6節の切戻し手順・引渡し記録の表→第9節。コードを読まなくても、何を戻し、何が戻らず、誰が判断し、何を引き渡すかが分かります。
- 作って確かめる:上に加えて第5.3節(演習コード)→第5.4節(演習の設計:実行前に見るもの・失敗したとき・戻せるか・誰が)→第6節の実行結果→第7節(確かめたこと・確かめていないこと)。
- 設計判断を深める:上に加えて第8節(切戻しの準備の手間と切戻しできない場合の影響、適用限界)。
1. 今回の業務課題と完成するもの
この記事でやることは、11-02で配置したアプリ(v0.1.0-sandbox)について、問題が出た場合の切戻し手順(コードを旧版に戻す範囲、その間に作られたデータの扱い)を書き、その手順をローカルの配置サンドボックスで実際に演習し、初期安定を確認して運用担当へ渡す引渡し記録を作ることです。
11-03では初期利用の確認記録を作りましたが、問題が起きた場合にどう戻すかはまだ扱っていませんでした。この記事では、それを扱います。
この記事を読み終えると、次のものが手元にできます。
- 切戻し手順・引渡し記録(戻す対象、手順、戻らない変更の扱い、判断者、引渡し内容、受け取り担当)
- 切戻し演習コードと、その実行記録(新版配置→不一致→切戻し→切戻し後の確認)
2. 前提知識と読む順路
この記事で使う言葉を先に説明します。
切戻し
一言でいうと、配置した新しいコードを、その前の版(旧版)に戻すことです。戻す先の旧版を、新版を置く前に退避しておく必要があります。
データの復元
一言でいうと、データを、バックアップを取った時点の状態に戻すことです。コードの切戻しとは別の作業です(12-04で演習します)。
戻らない変更
一言でいうと、切戻し・復元をしても元に戻せない変更です。たとえば、新版が動いている間に新しく作られた依頼データは、旧版のコードに戻してもそのまま残ります。消すのか、別に保存して後で確認するのかは、コードの切戻しとは別に決めます。
配置後の確認(健全性確認)
一言でいうと、配置した直後に「置いたものが意図どおりか」を確かめる決まった検査です。11-02 では、アプリの動作・配置元との同一性・データの件数・版の5項目を確認しました。この記事では同じ検査を、新版の配置後と切戻し後に行います。
これがないと、問題が起きたときに、コードだけ戻してデータの扱いを忘れる、あるいはその逆のミスが起きる可能性があります。
読む順路:配置は11-02、初期利用は11-03、復旧方針は05-04が該当します。この記事の引渡し記録は、12-01(稼働状況・業務結果・費用を把握する)の運用開始点、12-03(記事C)の復旧手順の前提になります。データの復元は12-04で演習します。
3. 理解する:コードの切戻しとデータの復元の違い
3.1 別々に考える理由
コードの切戻し(旧版のプログラムに戻すこと)と、データの復元(バックアップの時点に戻すこと)は、別の作業です。コードだけ戻してデータはそのまま、ということもあれば、その逆もあります。11-02 の配置サンドボックスでは、コードは app/approval.js と VERSION、データは data/requests.json で、置き場所も変更のされ方も違います。コードは配置作業でしか変わりませんが、データは利用者の操作で刻々と変わります。だから、コードは「配置前の版に戻す」、データは「バックアップの時点に戻す」という別々の基準で戻します。
3.2 「手順がある」と「戻せることを確認した」は違う
切戻し手順は、書いただけでは「戻せる」保証になりません。旧版が退避されていなかった、退避したファイルが違った、戻したあとの確認をしていなかった——どれも手順書の上では見えません。この記事では、11-02 の配置サンドボックスに対して、新版を置き、配置後の確認で不一致を検出し、旧版へ戻し、戻したあとに 11-02 と同じ検査をやり直す、という一連の流れを実際に実行し(第6節)、その結果から「コードは戻った・データは戻っていない」を読み取ります。演習で確認できたのはローカルの配置サンドボックスの範囲だけで、実際の運用環境で戻せるかは別に確認が必要です(第7節)。
3.3 よくある誤解
「コードを戻せば、データも自動的に戻る」。コードを旧版に戻しても、その間に作られたデータ(依頼・履歴)は、特に何もしなければそのまま残ります。第6節の演習では、切戻し後もデータの確認2項目が不一致のまま(REQ-NEW-001 が残っている)です。データを戻すには、別の作業(12-04)が必要です。
「切戻しさえできれば、それで終わり」。切戻し後、実際に安定して動いているかを確認し、その結果を運用担当へ引き渡すところまでが、この記事の範囲です(第5.2節)。
「演習で戻せたから、本番でも戻せる」。演習で確認したのは、ローカルの配置サンドボックスで、旧版の退避→新版配置→切戻し→確認が成立することまでです。実際の運用環境では、置き場所・権限・利用者の操作が違います(第7節の「確かめていないこと」)。
4. 判断すること
切戻しを行うときに人が判断することは、次の5つです。
| 判断すること | なぜ判断が必要か | 仮置きしてよいか | 間違えた場合の影響 |
|---|---|---|---|
| いつ切り戻すか(止める条件) | 配置後の確認で「コード」の項目に不一致があれば止めて戻す。データの項目だけの不一致は、切戻しの理由ではなく復元・扱いの判断に回す | 不可。条件は配置の前に決めておく(11-01の止める条件と同じ考え方) | 兆候が出ても「様子見」で新版を使い続ける。逆に、データの不一致だけでコードを戻してしまう |
| 何を戻し、何を戻さないか | コード(app/approval.js・VERSION)は旧版へ戻す。データ(data/requests.json)はコードの切戻しでは触らない | 不可。範囲を混ぜると「何が戻ったか」が分からなくなる | 新版の稼働中に作られた依頼が黙って消える、または戻したつもりのコードが残る |
| 戻らない変更をどう扱うか | 新版稼働中に増えたデータ(演習では REQ-NEW-001)は残る。破棄するか、別に保存して後で確認するか | 可(この記事では「切戻しの判断時に個別に決める」と仮置き。実際には業務担当者と決める) | 利用者が出した依頼を無断で消す、または新版でしか扱えない形のデータが旧版で読めなくなる |
| 何を証拠として残すか | 退避した旧版の指紋(sha256)、新版の指紋、切戻し後の指紋が退避した旧版と同一であること、切戻し前後の健全性確認の結果 | 不可。「戻した」と書くだけでは戻せた証拠にならない | 戻したはずのファイルが違っていても気づけない |
| 誰が切り戻すと判断し、誰に引き渡すか | 切戻しの判断は運用管理者(架空)。演習では Claude Code が判断者役。引渡しの受け取りは運用担当(総務の担当者1名、架空) | 可(架空の体制で仮置き) | 判断者が決まっておらず、切戻しが遅れる。引渡し先が無く、運用開始点が不明になる |
勝手に決めてはいけないこと:実際の運用環境で切戻し操作を行ったかのように書きません。この記事はローカルの配置サンドボックスでの演習です(第7節)。「新版」の失敗例を、実際に開発した新版や実際のAI出力として扱いません。05-04の復旧方針にない手段を、この記事で新たに作りません。データの扱い(破棄・保存)を、演習の実施者が決めたことにしません。
AIに任せる範囲:演習コードの下書き、実行して結果をそのまま返すこと、退避・配置・切戻しの機械的な操作、指紋の比較はAIに任せられます。任せてはいけないのは、止める条件、戻らない変更の扱い、「切り戻す」という判断そのもの、そして引渡しの受け取り担当の決定です。
5. 作って確かめる:切戻し手順・引渡し記録を作り、切戻しを演習する
5.1 コードの切戻し手順と、データの復元・戻らない変更の扱い
コードの切戻し
| 項目 | 内容 |
|---|---|
| 戻す対象 | 11-02 で配置したアプリ(app/approval.js、版:v0.1.0-sandbox)と版の記録(VERSION) |
| 手順 | (1) 新版を置く前に、現在の版を releases/<版>/approval.js へ退避し、指紋(sha256)を記録する。(2) 新版を配置し VERSION を更新する。(3) 11-02 と同じ健全性確認を行い、コードの項目に不一致があれば止める。(4) 退避した旧版で app/approval.js を上書きし VERSION を戻す。(5) 健全性確認をやり直し、戻したファイルの指紋が退避した旧版と同一であることを確かめる |
| 確認方法 | 11-02 と同じ健全性確認(healthCheck())を、コード(アプリの動作・配置元との同一性・版)とデータ(移行データとの同一性・件数)に分けて読む |
データの復元と、戻らない変更
| 項目 | 内容 |
|---|---|
| 戻す対象 | 配置サンドボックス内のデータ(data/requests.json) |
| 手順 | バックアップ(05-04の復旧方針、12-03(記事C)の復旧手順)の時点のデータに戻す。演習は12-04 |
| 戻らない変更 | 新版が動いていた間に新しく作られた依頼・履歴データは、バックアップの時点には存在しない。コードの切戻しでも残る。どう扱うか(破棄する、別途保存して後で確認する)は、切戻しを判断する時点で個別に決める |
5.2 初期安定の確認と引渡し
| 項目 | 内容 |
|---|---|
| 初期安定の確認 | 切戻し後の健全性確認(コードの項目)が一致し、11-03の初期利用の確認記録に止めて戻す条件への該当がないことを確認する |
| 判断者 | 運用管理者(架空の仮定)。演習では Claude Code が判断者役 |
| 引渡し内容 | 配置手順・実行記録(11-02)、初期利用の確認記録(11-03)、この記事の切戻し手順と演習の実行記録 |
| 受け取り担当 | 運用担当(総務の担当者1名、架空の仮定、10-03 と同じ想定) |
5.3 切戻し演習コード
演習コードは examples/11-04-rollback-handover/ を正本とします。11-02 の正本(examples/11-02-deploy-record/deploy.js)が配置した deploy_target/ に対して操作し、11-02 の healthCheck() をそのまま使います。
rollback.js(退避・配置・切戻し・確認):
// 教材演習用:ローカルの配置サンドボックス(11-02 が配置した deploy_target/)で、コードの切戻しを演習する。
// [1] 現在の版(旧版)を releases/<版>/ に退避する
// [2] 新版(説明用の失敗例 approval_v0.1.1-broken.js)を配置し、版を更新する
// [3] 11-02 と同じ健全性確認を行い、不一致なら「止める」
// [4] 退避した旧版へ切り戻し、版を戻す
// [5] 健全性確認をやり直し、一致することを確かめる
// データ(data/requests.json)はコードの切戻しでは触らない。新版の稼働中に加わったデータは残る(戻らない変更)。
// 実際の外部サービスへの配置ではない。Node.js標準機能(node:fs、node:path、node:crypto)のみを使用する。
const fs = require("node:fs");
const path = require("node:path");
const crypto = require("node:crypto");
const { healthCheck, SANDBOX_DIR } = require("../11-02-deploy-record/deploy.js");
const APP_FILE = path.join(SANDBOX_DIR, "app", "approval.js");
const DATA_FILE = path.join(SANDBOX_DIR, "data", "requests.json");
const VERSION_FILE = path.join(SANDBOX_DIR, "VERSION");
const RELEASES_DIR = path.join(__dirname, "releases");
const NEW_VERSION = "v0.1.1-sandbox";
const NEW_APP_SOURCE = path.join(__dirname, "approval_v0.1.1-broken.js");
function sha256(file) { return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); }
function currentVersion() { return fs.readFileSync(VERSION_FILE, "utf8").trim(); }
// [1] 旧版の退避:切戻し先が無いと戻せないので、新版を置く前に必ず行う
function keepCurrentRelease() {
const version = currentVersion();
const dir = path.join(RELEASES_DIR, version);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(APP_FILE, path.join(dir, "approval.js"));
return { version, dir, sha256: sha256(path.join(dir, "approval.js")) };
}
// [2] 新版の配置:コードだけを差し替え、版を更新する。データは触らない
function deployNewVersion() {
fs.copyFileSync(NEW_APP_SOURCE, APP_FILE);
fs.writeFileSync(VERSION_FILE, NEW_VERSION + "\n");
return { version: NEW_VERSION, sha256: sha256(APP_FILE) };
}
// 新版の稼働中に、利用者の操作でデータが1件増えた、という想定(戻らない変更の例)
function simulateUsageDuringNewVersion() {
const data = JSON.parse(fs.readFileSync(DATA_FILE, "utf8"));
data.push({ id: "REQ-NEW-001", requester: "田中", title: "新版の稼働中に作られた依頼(架空)", content: "説明用", approver: "鈴木", state: "提出済み", submittedAt: "2026-09-21" });
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
return data.length;
}
// [3] 配置後の確認:11-02 の healthCheck をそのまま使う(アプリの動作、配置元との同一性、件数、版)。
// コードの切戻しで戻るのは「アプリ」と「版」の項目だけなので、結果を「コード」と「データ」に分けて返す。
function checkAfterDeploy() {
const results = healthCheck();
const isCode = (r) => r.check.startsWith("アプリ") || r.check.startsWith("版");
return { code: results.filter(isCode), data: results.filter((r) => !isCode(r)) };
}
// [4] 切戻し:退避した旧版を戻し、版を戻す
function rollbackTo(version) {
const src = path.join(RELEASES_DIR, version, "approval.js");
if (!fs.existsSync(src)) throw new Error(`切戻し先の版が退避されていません: ${version}`);
fs.copyFileSync(src, APP_FILE);
fs.writeFileSync(VERSION_FILE, version + "\n");
return { version, sha256: sha256(APP_FILE) };
}
module.exports = { keepCurrentRelease, deployNewVersion, simulateUsageDuringNewVersion, checkAfterDeploy, rollbackTo, currentVersion, sha256, APP_FILE, DATA_FILE, RELEASES_DIR, NEW_VERSION, SANDBOX_DIR };
approval_v0.1.1-broken.js(「新版」という設定の説明用の失敗例。状態の比較に打ち間違いがあり、配置後の確認で不一致になる):
// 「新版」という設定の説明用の失敗例。実際に配置した新版でも、実際のAI出力でもない。
// 状態の比較に打ち間違い(「提出済」)があり、提出済みの依頼をすべて承認できないと判定してしまう。
// 切戻し演習で「配置後の確認で不一致が出たら止めて旧版へ戻す」流れを再現するために使う。
function canApprove(state, isAssignedApprover, isSelf) {
if (state !== "提出済") return false; // 打ち間違い(説明用の不備):正しくは「提出済み」
if (!isAssignedApprover) return false;
if (isSelf) return false;
return true;
}
module.exports = { canApprove };
run.js(手順0〜6を順に実行):
// 教材演習用:新版配置 → 配置後の確認で不一致 → 旧版へ切戻し → 切戻し後の確認、を順に実行する。
// 先に 10-02 と 11-02 を実行し、配置サンドボックスを作っておく。
const fs = require("node:fs");
const path = require("node:path");
const { keepCurrentRelease, deployNewVersion, simulateUsageDuringNewVersion, checkAfterDeploy, rollbackTo, currentVersion, sha256, APP_FILE, DATA_FILE, RELEASES_DIR, NEW_VERSION, SANDBOX_DIR } = require("./rollback.js");
if (!fs.existsSync(path.join(SANDBOX_DIR, "MANIFEST.json"))) {
console.log("11-02 の配置が見つかりません。先に examples/10-02-migration-plan と examples/11-02-deploy-record で node run.js を実行してください");
process.exit(1);
}
fs.rmSync(RELEASES_DIR, { recursive: true, force: true });
const print = (group, results) => { console.log(` ${group}:`); for (const r of results) console.log(` ${r.check}: 期待=${JSON.stringify(r.expected)} 実際=${JSON.stringify(r.actual)} 判定=${r.ok ? "一致" : "不一致"}`); return results.every((r) => r.ok); };
const printBoth = (c) => { const a = print("コード", c.code); const b = print("データ", c.data); return { code: a, data: b }; };
console.log(`手順0:切戻し前の確認。現在の版 ${currentVersion()}、データ ${JSON.parse(fs.readFileSync(DATA_FILE, "utf8")).length} 件`);
{ const c = printBoth(checkAfterDeploy()); if (!c.code || !c.data) { console.log(" 現在の配置の健全性確認が一致しないため、演習を始めない"); process.exit(1); } }
console.log("手順1:現在の版(旧版)を退避する");
const kept = keepCurrentRelease();
console.log(` ${kept.version} → ${path.relative(__dirname, kept.dir)}/approval.js(sha256: ${kept.sha256.slice(0, 12)}…)`);
console.log(`手順2:新版 ${NEW_VERSION}(説明用の失敗例)を配置する。データは触らない`);
const deployed = deployNewVersion();
console.log(` app/approval.js を差し替え(sha256: ${deployed.sha256.slice(0, 12)}…)、VERSION=${currentVersion()}`);
console.log("手順3:新版の稼働中に、利用者の操作でデータが1件増えた(想定)");
console.log(` データ ${simulateUsageDuringNewVersion()} 件(REQ-NEW-001 を追加)`);
console.log("手順4:配置後の確認(11-02 と同じ健全性確認)");
const afterNew = printBoth(checkAfterDeploy());
if (afterNew.code) {
console.log(" コードの確認がすべて一致した(この演習では不一致になる想定なので、想定外)");
process.exit(2);
}
console.log(" コードの確認に不一致がある → 止めて、旧版へ切り戻すと判断する(判断者:運用管理者。演習では Claude Code)");
console.log(`手順5:旧版 ${kept.version} へ切り戻す(コードと版だけ。データは戻さない)`);
const back = rollbackTo(kept.version);
console.log(` app/approval.js を戻した(sha256: ${back.sha256.slice(0, 12)}…、退避した旧版と${back.sha256 === kept.sha256 ? "同一" : "不一致"})、VERSION=${currentVersion()}`);
console.log("手順6:切戻し後の確認");
const afterRollback = printBoth(checkAfterDeploy());
const data = JSON.parse(fs.readFileSync(DATA_FILE, "utf8"));
console.log(` データ ${data.length} 件。REQ-NEW-001 は${data.some((r) => r.id === "REQ-NEW-001") ? "残っている(戻らない変更。扱いは切戻しの判断時に決める)" : "無い"}`);
console.log("");
if (afterRollback.code && back.sha256 === kept.sha256) {
console.log(`結論:新版 ${NEW_VERSION} で不一致を検出し、旧版 ${kept.version} へ切り戻して、コードの確認(3項目)が一致した。データの確認(2項目)は不一致のまま=コードの切戻しではデータは戻らない。REQ-NEW-001 の扱いは別に決める(12-04 の復元は別の作業)`);
process.exit(0);
}
console.log("結論:切戻し後のコードの確認が一致しない");
process.exit(1);
5.4 演習の設計
| 項目 | 内容 |
|---|---|
| 何のための操作か | 「旧版を退避→新版を配置→確認で不一致→旧版へ切戻し→再確認」が実際に成立すること、コードの切戻しではデータが戻らないことを、ローカルで確かめる |
| 実行前に見るもの | 今いる場所が examples/11-04-rollback-handover/ か。10-02と11-02を順に実行し、../11-02-deploy-record/deploy_target/MANIFEST.json があるか。手順0の健全性確認が5件とも一致するか(一致しなければ演習を始めない) |
| コマンド | node run.js |
| 期待結果 | 手順4でコードの項目3件が不一致→「止めて、旧版へ切り戻す」。手順6でコードの項目3件が一致、データの項目2件が不一致のまま、REQ-NEW-001 が残る。終了コード0 |
| 結果の読み方 | 手順5の「戻した」ファイルの指紋が手順1で退避した指紋と同一か(6dc21fe173b6…)。手順6の「コード」が一致し「データ」が不一致であること=コードの切戻しでデータは戻らない |
| 失敗したとき | 配置が無い・手順0で不一致(終了コード1)→ 10-02・11-02を先に実行する。手順4でコードが一致(終了コード2)→ 新版の失敗例が想定と違う、approval_v0.1.1-broken.js を確認する。手順6でコードが不一致(終了コード1)→ 退避した旧版が違う。releases/ を確認する |
| 元に戻せるか | 演習後、配置サンドボックスのコードと版は v0.1.0-sandbox に戻っている。データには REQ-NEW-001 が残る(戻らない変更の例)。データ3件の状態に戻すには、11-02 の node run.js をもう一度実行して配置先を作り直す。releases/ はこのフォルダに残る |
| 業務運用では誰が実施/判断するか | 実施:配置担当(開発者役)。切り戻す判断:運用管理者(架空)。戻らない変更の扱い:業務担当者と決める。引渡しの受け取り:運用担当。この演習ではすべて Claude Code |
6. 実行結果(切戻し演習と、切戻し手順・引渡し記録)
6.1 切戻し演習の実行結果
examples/11-04-rollback-handover/ で、10-02と11-02を実行して配置サンドボックスを作り直したあとに実行した stdout です(整形・省略なし。stderr への出力はありませんでした)。
$ node run.js
手順0:切戻し前の確認。現在の版 v0.1.0-sandbox、データ 3 件
コード:
アプリの健全性(canApprove): 期待=true 実際=true 判定=一致
アプリが08-01の正本と同一(sha256): 期待=true 実際=true 判定=一致
版の記録: 期待="v0.1.0-sandbox" 実際="v0.1.0-sandbox" 判定=一致
データ:
移行データが10-02の出力と同一(sha256): 期待=true 実際=true 判定=一致
移行データの件数: 期待=3 実際=3 判定=一致
手順1:現在の版(旧版)を退避する
v0.1.0-sandbox → releases/v0.1.0-sandbox/approval.js(sha256: 6dc21fe173b6…)
手順2:新版 v0.1.1-sandbox(説明用の失敗例)を配置する。データは触らない
app/approval.js を差し替え(sha256: c50b61d635ab…)、VERSION=v0.1.1-sandbox
手順3:新版の稼働中に、利用者の操作でデータが1件増えた(想定)
データ 4 件(REQ-NEW-001 を追加)
手順4:配置後の確認(11-02 と同じ健全性確認)
コード:
アプリの健全性(canApprove): 期待=true 実際=false 判定=不一致
アプリが08-01の正本と同一(sha256): 期待=true 実際=false 判定=不一致
版の記録: 期待="v0.1.0-sandbox" 実際="v0.1.1-sandbox" 判定=不一致
データ:
移行データが10-02の出力と同一(sha256): 期待=true 実際=false 判定=不一致
移行データの件数: 期待=3 実際=4 判定=不一致
コードの確認に不一致がある → 止めて、旧版へ切り戻すと判断する(判断者:運用管理者。演習では Claude Code)
手順5:旧版 v0.1.0-sandbox へ切り戻す(コードと版だけ。データは戻さない)
app/approval.js を戻した(sha256: 6dc21fe173b6…、退避した旧版と同一)、VERSION=v0.1.0-sandbox
手順6:切戻し後の確認
コード:
アプリの健全性(canApprove): 期待=true 実際=true 判定=一致
アプリが08-01の正本と同一(sha256): 期待=true 実際=true 判定=一致
版の記録: 期待="v0.1.0-sandbox" 実際="v0.1.0-sandbox" 判定=一致
データ:
移行データが10-02の出力と同一(sha256): 期待=true 実際=false 判定=不一致
移行データの件数: 期待=3 実際=4 判定=不一致
データ 4 件。REQ-NEW-001 は残っている(戻らない変更。扱いは切戻しの判断時に決める)
結論:新版 v0.1.1-sandbox で不一致を検出し、旧版 v0.1.0-sandbox へ切り戻して、コードの確認(3項目)が一致した。データの確認(2項目)は不一致のまま=コードの切戻しではデータは戻らない。REQ-NEW-001 の扱いは別に決める(12-04 の復元は別の作業)
終了コード: 0
実行後の配置ファイルの確認(同じ実行記録より):
$ cat ../11-02-deploy-record/deploy_target/VERSION
v0.1.0-sandbox
$ ls releases/v0.1.0-sandbox
approval.js
$ diff ../11-02-deploy-record/deploy_target/app/approval.js ../08-01-minimal-example/approval.js
(出力なし=同一)
$ grep -c REQ-NEW-001 ../11-02-deploy-record/deploy_target/data/requests.json
1
読み方:手順4で「コード」3件が不一致になったので止めて切り戻し、手順6で「コード」3件が一致に戻りました。戻したファイルの指紋 6dc21fe173b6… は手順1で退避した旧版と同一で、diff でも 08-01 の正本と同一です。一方「データ」2件は切戻し後も不一致のままで、REQ-NEW-001 が残っています。これが「コードの切戻しではデータは戻らない」の実測です。
6.2 切戻し手順・引渡し記録
| 戻す対象 | 手順 | 戻らない変更の扱い | 判断者 | 引渡し内容 | 受け取り担当 | 確認状況 |
|---|---|---|---|---|---|---|
コード(app/approval.js、VERSION、v0.1.0-sandbox) | 旧版を releases/<版>/ へ退避→新版配置→健全性確認で不一致なら止める→退避した旧版で上書き・版を戻す→健全性確認の再実行と指紋の照合 | 新版稼働中に作られたデータは、切戻し判断時に個別に破棄/保存を決める | 運用管理者(架空。演習では Claude Code) | 配置実行記録(11-02)、初期利用確認記録(11-03)、切戻し手順と演習の実行記録(本記事) | 運用担当(総務の担当者1名、架空) | 演習で確認(2026-09-21、ローカルの配置サンドボックス) |
データ(data/requests.json) | バックアップ時点への復元(05-04・12-03 の手順、演習は 12-04) | 同上(演習では REQ-NEW-001 が残ることを確認) | 同上 | 同上 | 同上 | この記事では未実施(12-04 で演習) |
7. 確認結果
実行環境:macOS 26.6.2、Node.js v24.18.0(node:fs・node:path・node:crypto のみ使用)、2026-09-21に実行。実行場所は examples/11-04-rollback-handover/。操作した配置は examples/11-02-deploy-record/deploy_target/(11-02 の正本が作ったもの)だけで、外部のサーバー・サービスには接続していません。stdout・stderr・終了コードは evidence_public/phase5b2c1-2026-09-21/11-04/run_log.md に整形なしで記録しています。
完了条件との照合:
| 完了条件 | 期待 | 実測 | 判定 |
|---|---|---|---|
| コードの切戻しとデータの復元が分けて書かれている | 第5.1節に別々の表 | 記載あり | 一致 |
| 戻らない変更の扱いがある | 第5.1節に記載、演習で残ることを確認 | REQ-NEW-001 が切戻し後も残る(手順6) | 一致 |
| 引渡しの受け取り担当が明記されている | 第5.2節・第6.2節 | 記載あり | 一致 |
| 旧版が退避され、切戻し後のファイルが退避した旧版と同一 | sha256 が同一 | 6dc21fe173b6… で同一、diff も出力なし | 一致 |
| 新版の配置後の確認でコードの不一致を検出し、止める | コード3件不一致 | 3件不一致→「止めて、旧版へ切り戻す」 | 一致 |
| 切戻し後の健全性確認(コード)が一致 | コード3件一致、終了コード0 | 3件一致、終了コード0 | 一致 |
| 切戻し後もデータは戻らない | データ2件不一致 | 2件不一致、データ4件 | 一致 |
この記事で確かめたこと:ローカルの配置サンドボックスで、旧版の退避→新版配置→配置後の確認で不一致→旧版への切戻し→切戻し後の確認、が実際に成立すること。コードの切戻しではデータが戻らないこと。
この記事で確かめていないこと:
| 範囲 | 理由 | どこで確かめるか |
|---|---|---|
| 実際の運用環境での切戻し | 演習はローカルの配置サンドボックスのみ。置き場所・権限・利用者の操作が違う | 実際の環境で、同じ5手順を配置担当が行い、運用管理者が判断する |
| データの復元(バックアップ時点へ戻す) | この記事はコードの切戻しのみ | 12-04 |
| 戻らない変更(REQ-NEW-001)の扱い | 破棄か保存かは業務担当者と決める設計判断で、演習の実施者が決めない | 切戻しを判断する時点で個別に決める |
| 実際の運用担当への引渡し | 架空の体制。受け取り担当は実在しない | 実際の運用開始時 |
| 実際に開発した新版での失敗 | 新版は説明用の失敗例 | 2回目以降の実際の配置 |
残る制限:この記事の本文と演習コードはClaude Codeが作成したもので、業務担当者による内容確認は行っていません。演習の実施者・判断者役はどちらも Claude Code で、独立した確認ではありません。
8. 設計を判断する
8.1 切戻しを可能にする準備の手間と、切戻しできない場合の影響
| 選択肢 | 内容 | 適する場合 |
|---|---|---|
| 切戻しを可能にする準備をする(この記事で採用) | 配置のたびに旧版を退避し、切戻し手順を用意し、一度は演習しておく | 問題が起きたときに、速やかに元の状態へ戻す必要がある場合 |
| 切戻しを可能にする準備をしない | 旧版を残さず、問題が起きたら修正して再配置する | 修正・再配置が速く行える、または問題の影響が小さいことが見込める場合 |
この記事の教材演習では、データ変更を伴う配置(依頼・履歴データの投入)であるため、切戻しを可能にする準備をする方針としています。準備の手間は、旧版の退避(ファイル1つのコピー)と演習1回で、第6節のとおり数秒で終わります。データ変更を伴わない配置(表示文言の変更など)であれば、準備の手間を減らす判断もありえます。
8.2 コードだけ戻すか、データも戻すか
| 選択肢 | 内容 | 適する場合 |
|---|---|---|
| コードだけ戻す(この記事の演習) | データは触らず、新版稼働中の変更を残す | 新版の不具合がコードだけで、データの形式や内容を壊していない場合 |
| コードとデータの両方を戻す | バックアップの時点へデータも復元する(12-04) | 新版がデータを壊した、または旧版で読めない形にした場合。新版稼働中の変更は失われるため、扱いを先に決める |
8.3 適用限界
この記事の演習は、11-02 で配置した最初の版(v0.1.0-sandbox)を旧版とし、説明用の失敗例を新版としています。実際に旧版が存在する場面(2回目以降の配置)では、この記事の形式をもとに、実際の旧版と新版の内容に合わせて手順を書き直し、演習をやり直す必要があります。また、この演習はファイルのコピーで配置する環境を前提にしており、外部サービスへの配置では切戻しの操作自体が異なります。
9. 持ち帰る成果物
| 成果物 | 空欄版 | 記入例 |
|---|---|---|
| 切戻し手順・引渡し記録ひな形(戻す対象、手順、戻らない変更の扱い、判断者、引渡し内容、受け取り担当、確認状況) | templates/11-04-rollback-handover_template.md | templates/11-04-rollback-handover_example.md |
| 切戻し演習コード | examples/11-04-rollback-handover/(rollback.js、run.js、approval_v0.1.1-broken.js) | 同上 |
| 実行記録(stdout・stderr・終了コード、整形なし) | — | evidence_public/phase5b2c1-2026-09-21/11-04/run_log.md |
自分の業務に使うときの手順は次のとおりです。
- コードの切戻し手順と、データの復元手順を分けて書く
- 切戻し・復元をしても戻らない変更を洗い出し、扱いを決める
- 新版を置く前に旧版を退避し、切戻し→再確認を一度は実際に演習する
- 初期安定を確認し、判断者を明確にする
- 引渡し内容と受け取り担当を決め、演習の実行記録とともに記録として残す
10. 次工程への受渡し
次へ渡すもの:切戻し手順・引渡し記録、切戻し演習の実行記録。
次工程での使われ方:12-01(稼働状況・業務結果・費用を把握する)では、この引渡し記録を運用開始点とします。12-02(異常時の初動)では、初動の暫定対応で「切戻しが必要か」を判断するときに、この記事の切戻し手順を使います。12-03(記事C)では、この記事のデータ復元の考え方を、実際の復旧手順の前提として使い、12-04でデータの復元を演習します。
前工程へ戻る条件:切戻し手順が11-02の配置内容と食い違う場合(手順0の健全性確認が一致しない場合)は、11-02 に戻って確認します。
11. 根拠と更新情報
- この記事の会社・人物・データはすべて架空の設定です。
- 配置内容は11-02、復旧方針は05-04、復旧手順は12-03、データの復元演習は12-04に基づきます。
- 制作の来歴:本文はClaude Codeが作成しました。第5.3節・第6節の演習コードは
examples/11-04-rollback-handover/を正本とし、11-02 の正本が配置したdeploy_target/に対して、2026-09-21にmacOS 26.6.2・Node.js v24.18.0の環境で実際に実行し、stdout・stderr・終了コードをevidence_public/phase5b2c1-2026-09-21/11-04/run_log.mdに記録しています。 - 2026-09-21(Phase 5B-2C1):初版は切戻し手順を文書として整理しただけで、旧版も存在せず「実際に切戻し操作を行ったものではない」としていた。本更新で、11-02 の配置サンドボックスに対して旧版の退避→新版配置→不一致の検出→切戻し→再確認を実際に行う演習コードを追加し、実測結果を第6節に載せた。「手順がある」ことと「実際に復旧できることを確認した」ことを分け、後者はローカルの演習の範囲に限ることを第7節に明記した。
- 未確認事項:実際の運用環境での切戻し操作、データの復元、戻らない変更の扱いの決定、実際の運用担当への引渡しは、この記事の時点では行っていません。
