二重定義を片方へ寄せる挙動不変リファクタを、AI委譲で安全に終わらせる
同じ表が直書きと導出で2箇所にある状態を片方へ寄せる時、テストが緑でも終わっていない。着手前に実データのJSONを保存して機械比較する型と、手写しfixtureが順序違いで偽failを出す罠、導出に変えた後の縮退の決め方まで。
約3.3万トークンの節約 (API料金換算で約50円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「二重定義を片方へ寄せる挙動不変リファクタを、AI委譲で安全に終わらせる」は、開発プロセスカテゴリのAI指示書(MDファイル)です。同じ表が直書きと導出で2箇所にある状態を片方へ寄せる時、テストが緑でも終わっていない。着手前に実データのJSONを保存して機械比較する型と、手写しfixtureが順序違いで偽failを出す罠、導出に変えた後の縮退の決め方まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.3万トークン(API料金換算で約50円)・79%のトークンを節約できます。
- カテゴリ
- 開発プロセス
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4.2万トークン
- この巻物使用時
- 約9,000トークン
- 節約量
- 約3.3万トークン (約50円)
- 更新日
- 2026-09-20
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/md-e952b44f/raw を読み込んで、この指示書どおりに実装して"
中身
同じ表が2箇所にある状態を、AIに委譲して「挙動を1ビットも変えずに」片方へ寄せる
対象: コーディングAI(Claude Code / Codex / Cursor など)に挙動不変のリファクタを任せたい人。 「片方だけ古くなる二重定義」を消す作業は、テストが通っただけでは終わっていない。実データでの 出力一致まで機械で確かめる手順を書く。
何が問題か
同じ知識が2箇所にあると、片方を更新した人がもう片方を知らずに必ずズレる。典型:
- ツールAは定数を直書き、ツールBは同じ内容を実行時にAPIから導出
- 本体は一覧を持ち、テストはその一覧を手写しした fixture を持つ
寄せる作業そのものは単純だが、危ないのは「単純だからテストが緑なら良い」と判断すること。 導出に変えると順序・型・欠損時の挙動が静かに変わる。出力が変われば下流の集計が狂う。
手順
1. 着手前にベースラインを2種類取る
# (a) テストの現状(件数を記録する。"全部緑" では後で比較できない)
node --test "test/*.test.mjs" > baseline-tests.txt
node --test "tools/*.test.mjs" >> baseline-tests.txt
# (b) 実データでの出力。JSON を吐くフラグがあるならそれを保存する
<対象ツール> --json > before.json
(b) が要る。テストは「作者が想像した入力」しか通らないが、リファクタが壊すのは 本番データの端(欠損・空配列・想定外の名前)だから。
2. 差分を機械比較するスクリプトを先に書く
後で書くと「だいたい合っている」で済ませてしまう。着手前に書く。
import fs from 'node:fs';
const [a, b] = process.argv.slice(2).map(p => JSON.parse(fs.readFileSync(p, 'utf8')));
const KEYS = ['total', 'sold', 'blocked', /* 比較する全項目を明示的に並べる */];
let diffs = 0;
// 配列の要素数・キーの有無・値を、キー名を出しながら比較する
// (deepEqual 一発ではなく「どの項目が変わったか」を印字する。原因の切り分けが変わる)
process.exit(diffs === 0 ? 0 : 1);
assert.deepEqual 一発でも一致は分かるが、どの項目がどう変わったかが出ないと
「意図した変更」と「事故」を切り分けられない。
3. 委譲の指示書に「変えない物」を列挙する
読み取り専用のブリッジ越しにAIを使う場合、対象ファイルの全文を指示書に同梱する (AIがファイルを読めない環境では、パスだけ書いても推測で書かれるか、作業が止まる)。 指示書には最低限これを書く:
- 出力してよいファイル名(それ以外は「変更しない・出力しない」と明示)
- 変更しない関数・出力・終了コードの一覧
- 期待する検証(AIは実行できないので「期待値」として書く)
4. 落とし穴: 「既存の手書きの表はそのまま残せ」と書くな
これで1回踏んだ。テスト内の期待値が手書きの配列で、導出結果と中身は同じだが順序が違った。
// 手書き(人が書いた見やすい順)
['a1', 'a2', 'a3', 'b1', 'c1', 'c2']
// 導出(Object.keys は整数風キーを数値昇順で返す)
['a1', 'a2', 'a3', 'c1', 'c2', 'b1']
集合としては同一。実データの出力も完全に一致した。落ちたのはテストの期待値だけ。 つまり実装は正しいのに fail が出る=偽 fail で、原因の切り分けに時間を取られる。
順序に意味が無い一覧の期待値は、正本から導出して書く。
// ✗ 手写し
assert.deepEqual(resolved['全部入り'], ['a1', 'a2', 'a3', 'b1', 'c1', 'c2']);
// ✓ 導出(正本が増えても勝手に追随する)
assert.deepEqual(resolved['全部入り'], Object.keys(SOURCE));
assert.equal(Object.keys(SOURCE).length, 6); // 件数だけは固定して「空でも通る」を防ぐ
件数の assert を1行足すのが要点。導出だけにすると、正本が空になっても通ってしまう。
5. 導出に変えたら「取れなかったとき」を決める
直書きは失敗しないが、導出はAPI呼び出しなので失敗する。既に同じ導出をしている別のツールと 同じ縮退にそろえる(片方が例外で落ち、もう片方が黙って0件になる、が最悪)。
let table = {};
try {
const res = await fetchSource();
const resolved = derive(res, known);
table = resolved.table;
if (resolved.unknown.length) {
// 解決できなかった物は黙って捨てず、必ず名前を出す
console.error(`構成を解決できない項目があります(数えません): ` +
resolved.unknown.map(u => `${u.id}=${u.name}`).join(', '));
}
} catch (e) {
console.error(`定義を取得できなかった。この分類は数えない: ` + String(e?.message ?? e).slice(0, 120));
}
「取れなかった」と「0件だった」は別物。ログで区別できるようにしておくと、後日の 「なぜ数が合わないのか」が1分で終わる。
6. 受け入れ判定(この5つが揃うまで完了と言わない)
| 判定 | コマンド | 合格 |
|---|---|---|
| 構文 | node --check <各ファイル> | エラー無し |
| テスト | node --test … | ベースラインの件数以上・fail 0 |
| 実データ | node compare.mjs before.json after.json | 差分 0 |
| 仕様外の改変 | git status --short | 指示したファイル以外 0 件 |
| 残骸 | ls *.bak-* | AIブリッジが作るバックアップを消したか |
4番目が重要。委譲したAIは頼んでいないファイルを直すことがある。件数で数える。
なぜ「テストが緑」で終わらせてはいけないか
このリファクタで実際に起きたこと:
- テストは緑になった(新テストも足した)
- しかしテスト内の期待値だけが順序違いで落ち、実データの出力は完全に一致していた
もし逆(テストが緑・実データが不一致)だったら、テストだけ見て出荷していた。 テストは自分が想像した入力しか守ってくれない。 挙動不変を名乗るなら、 本番の入力で before/after を機械比較する1回が要る。
使い回せる形
baseline-tests.txtとbefore.jsonを作業ディレクトリに残す(後で「本当に同じだったか」を再確認できる)- 比較スクリプトは汎用にして次回も使う
- 指示書・ベースライン・結果を1つのフォルダにまとめてコミットする。 次に同じ二重定義を見つけた人が、同じ手順をなぞれる
よくある質問
+「二重定義を片方へ寄せる挙動不変リファクタを、AI委譲で安全に終わらせる」とは何ですか?
同じ表が直書きと導出で2箇所にある状態を片方へ寄せる時、テストが緑でも終わっていない。着手前に実データのJSONを保存して機械比較する型と、手写しfixtureが順序違いで偽failを出す罠、導出に変えた後の縮退の決め方まで。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4.2万トークンかかりますが、この巻物を使えば約9,000トークンで済みます。差し引き約3.3万トークン(API料金換算で約50円)・79%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
スマホ(Remote Control)から即相談できる Claude Code タブを VS Code に毎朝自動で用意する
自作VS Code拡張で公式Claude Codeのコマンド(editor.openLast/newConversation/renameSessionTab)を叩き、名前付きタブをN本自動補充。夜間はWM_CLOSE→再起動で毎朝揃える。--bg/ターミナル経路・タブ0でのnewConversation・SendKeys再読み込みが失敗する実測付き
夜間ジョブ異常を通知で終わらせず自動修復→AI修理PR→人へ引き渡す閉ループ
監視の『検知して通知』の後段に、決定的Playbook→AIコーダーの隔離worktree修理PR→持ち越し→人への3要素引き渡し、を足す実装指示書。argvで指示を渡すな等の実測の落とし穴つき
ドキュメント駆動開発プロセス CLAUDE.md — 作るものを固めてから書かせる
「AIが暴走して意図と違うものを作る」を根絶する開発プロセス指示書。UI仕様→機能設計→実装の順をAIに強制し、1ファイルごとに承認ゲートを挟む。受託開発・チーム開発向け。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア