Sonnet 5.5で複数ファイルをリファクタリングする:CLAUDE.mdと検証用プロンプト
Pythonの工数集計を題材に、変更範囲と既存動作を守るCLAUDE.md、作業プロンプト、テストと差分確認を解説。演習一式をダウンロードできます。
Sonnet 5.5にリファクタリングを頼むときは、変更してよいファイル、維持すべき動作、完了時に提出する検証結果を明示します。「コードを整理して」だけでは、その三つをモデルの判断に委ねることになります。継続的なプロジェクトルールを CLAUDE.md に、本日の分割目標を作業プロンプトに置き、最後にコードを独立して確認するのが、この演習の進め方です。
題材は、Pythonで書かれた小さなチケット工数集計です。CSVの解析、集計、コマンドライン処理を分離しつつ、既存の公開インターフェースを維持します。演習パッケージには、初期プロジェクト、10件の契約テスト、作業プロンプト、変更範囲チェック、参考実装が入っています。Python 3.10以上とGitが必要です。オフライン演習に外部パッケージやAPIキーは要りません。
参考実装はOfoxが作成し、2026年10月8日にローカルでテストしました。Sonnetを呼び出して生成した実装ではありません。テスト成功は、このサンプルが指定の条件を満たすことを示します。モデルの成功率、速度、指示への完全な従属性を測定した結果ではありません。モデルで試す場合は、利用権限のあるClaude Codeセッションを使い、その実行結果を別に保存してください。
変更前に動作の契約を決める
初期ファイル ticket_report/report.py はCSVを読み、チケットIDと工数を検証し、チーム別に集計してJSONを出力します。ヘッダーは id,team,hours の順序です。計算にはDecimalを使い、0.1と0.2の合計を二進浮動小数点の近似値に変えません。出力値も文字列のままにし、小数の表記を維持します。
同梱サンプルの実行コマンドです。
python3 -m ticket_report fixtures/tickets.csv
期待する出力は次のとおりです。
{"Billing": "1.50", "Support": "0.75"}
この一例だけで契約が完結するわけではありません。既存の呼び出し側は、引き続き ticket_report.report から parse_rows と summarize をインポートできる必要があります。チーム名の順序はソート済み、不正な行は黙って捨てずエラーにします。ファイルが存在しない場合や引数が間違っている場合は終了コード2とし、stderrにエラーを出し、stdoutに成功結果を出しません。
これらを編集前に決めないと、モデルが検証強化、数値の正規化、パッケージ名やコマンドの変更まで「整理」の一部と解釈することがあります。それ自体に価値があっても、一度に混ぜると既存利用者への影響を追いにくくなります。
| 責務 | 変更前 | 分割後 |
|---|---|---|
| CSV解析と行の検証 | report.py | parsing.py |
| Decimalによる合計と並び順 | report.py | aggregation.py |
| 引数、ファイル、JSON、終了コード | report.py | report.pyに残す |
| 公開関数のインポート | report.py | 同じ場所から再エクスポート |
| テスト、入力例、モジュール入口 | 別ファイル | 変更しない |
この演習はチケットシステム全体の再設計ではありません。任意のCSVに対する完全な検証、悪意あるアップロードへの防御、本番規模の性能は対象外です。境界を限定することで、小さく読める例の中でも複数ファイル間の依存関係を確かめられます。
隔離した環境で基準を記録する
アーカイブを展開し、starter に移動します。同階層の reference は作業リポジトリの外に残してください。参考解答が既存アプリケーションの一部として読み込まれる混乱を避けます。まず基準となる状態をコミットします。
cd sonnet-refactor-kit/starter
git init
git add .
git commit -m "Baseline ticket report exercise"
python3 -m unittest discover -s tests -v
python3 -m ticket_report fixtures/tickets.csv
10件のテストが通るはずです。Gitがユーザー情報を要求したら、普段のプロジェクト方針に従って設定します。記事の他人の情報をコピーしないでください。編集前からテストが失敗する場合は、先に展開方法や実行環境を確認します。正常な基準がなければ、後の失敗をモデルの変更に帰属できません。
テストには、小数計算、空の表、空白とUnicode、重複ID、不正な数値、列順、空のチーム名、三つのCLI結果が含まれます。不正な工数については負数、非有限値、空文字、数値でない文字列をサブケースで検証します。正常系一つだけより、維持すべき性質を具体的に示せます。
実際のリポジトリでも開始コミットと既存の未コミット変更を記録し、作業用ブランチやworktreeで隔離します。他の人の作業を消す全面的なresetは使いません。新規リポジトリにする本例では、範囲チェックが確実な基準と比較できます。
CLAUDE.mdには継続的なルールを書く
Claude Codeのメモリ文書は CLAUDE.md を指示のコンテキストとして説明しています。強制適用される設定ファイルではありません。「テストを変更しない」という指示は有用ですが、アクセス権やレビューの代わりにはなりません。/context で読み込まれたメモリを確認し、意図したプロジェクトファイルが存在するか調べてください。

2026年10月8日に取得した英語の公式文書です。モデルによる作業成功の画面ではありません。
演習には次のファイルが全文で含まれます。コードと対応させてコピーできるよう、指示文は英語のまま掲載しています。
# Ticket report exercise
Run commands from this directory. Python 3.10+; standard library only.
Run `python3 -m unittest discover -s tests -v` before and after changes.
Run `python3 -m ticket_report fixtures/tickets.csv` for the CLI contract.
Preserve the public imports `ticket_report.report.parse_rows` and `summarize`.
Keep Decimal arithmetic, JSON strings, sorted keys, error messages and exit codes.
Only edit report.py or add parsing.py and aggregation.py inside ticket_report/.
Do not change tests/, fixtures/, __main__.py, dependencies or this file.
No network, deployment, commits or unrelated cleanup are part of the task.
If a requirement conflicts with existing behavior, report it before changing behavior.
In the final response list files changed, commands and actual results, and limitations.
These instructions are task context, not a filesystem security boundary.
別のプロジェクトで使うときは、コマンドと保護対象パスを先に置き換えます。存在しないテストコマンドをコピーしても検証にはなりません。一時的な合格条件は毎回のプロンプトに書き、過去の全作業をプロジェクトファイルに積み上げないようにします。子ディレクトリの指示とルートの指示が衝突する場合も、実行前に解消してください。
一回の依頼で作業の全条件を伝える
最初にクライアントで利用モデルを選び、その設定を確認します。Sonnet 5.5のClaude Code設定でアカウントや接続先の確認を説明しています。モデルの利用可否、別名の参照先、プロンプトの品質は別の問題です。回答内の自己紹介は、実際に処理したモデルの証拠にはなりません。
starter を開いたら次のプロンプトを使います。
Refactor ticket_report/report.py without changing behavior.
First read CLAUDE.md and tests/test_contract.py, run the existing tests,
and explain the current contract.
Extract parse_rows to ticket_report/parsing.py and summarize to
ticket_report/aggregation.py.
Keep report.py as the CLI coordinator and re-export both public functions.
Allowed changes: ticket_report/report.py, ticket_report/parsing.py,
and ticket_report/aggregation.py only.
Do not update tests or fixtures to accommodate your changes.
Do not add dependencies or deploy.
After editing run the full test suite and CLI example; inspect the final diff.
Report actual test output, the file list, and remaining limitations.
If blocked, report the exact blocker.
プロンプトは、目標のファイル構成と外から観察できる動作を両方指定しています。「再エクスポート」は重要です。関数を新しいモジュールへ移しただけでは、古いパスを使う呼び出し元が壊れます。CLIの調整役を元の場所に残せば、見た目を整えるために python -m ticket_report の入口まで変えることも避けられます。
Anthropicの Sonnet 5.5向けプロンプトガイドでは、effortが自律的な作業や検証の挙動に影響すると説明し、範囲を明確にすることを勧めています。設定を意識して選んだうえで実際の出力を評価してください。高い設定でもテストを省略できません。effort設定の解説は選択の補助になりますが、本演習の合格条件は変わりません。
モデルが追加の整理を提案した場合は、別作業のメモに残します。無関係な依存関係更新をここで扱わないのは、その更新が悪いからではありません。変更を読みやすくし、回帰が起きた場合に原因を絞るためです。
実装、テスト、変更パスを別々に確認する
セッション後は starter から自分でもコマンドを実行します。「成功しました」という文章だけを受け入れないでください。
python3 -m unittest discover -s tests -v
python3 -m ticket_report fixtures/tickets.csv
python3 ../scope_check.py
git diff --check
git diff HEAD
範囲チェックはHEADからの追跡済みファイルの変更と、Gitに無視されていない未追跡ファイルを調べます。新しく分割した二つのモジュールは未追跡なので、通常の未ステージ差分だけでは見落とすことがあります。許可されるのは ticket_report 内の report.py、parsing.py、aggregation.py だけです。
期待する変更パスはその三つで、範囲外のパスはゼロです。ただし範囲チェックの成功は実装の正しさを証明しません。許可ファイル内の破壊的な変更もパス検査には通ります。反対にテスト成功も、入力例や設定を勝手に書き換えたことの免責にはなりません。既存ファイルの差分に加え、新しいファイルも実際に読みます。
参考実装では report.py が移動した関数をインポートし、引数、エラー、JSONを引き続き処理します。したがって公開インポートは維持されます。初期版と参考版の両方で同じ10テストが成功し、入力例から前述のJSONを得られました。これはローカル参考実装の結果であり、読者のモデル実行結果は異なる可能性があります。
条件を緩めず、失敗箇所を絞って修正する
| 症状 | 確認する境界 | 対処 |
|---|---|---|
| 分割後のインポートエラー | 従来の公開パス | 再エクスポートを復元し、呼び出し元は変えない |
| 浮動小数点の端数や数値型JSON | 計算と出力形式 | Decimalと文字列出力を復元 |
| テストを書き換えると成功する | 合格条件の変更 | 元テストを戻し、実装を修正 |
| 合計は正しいが終了コードが違う | CLI処理 | stderr、stdout、終了コードを比較 |
| 新ファイルが差分に出ない | 未追跡ファイル | 内容を読み、範囲チェックを実行 |
| モデルを選択できない | アカウント、接続先、クライアント | アクセスを解決し、コーディング失敗と混同しない |
全作業をやり直すより、失敗を指定する修正プロンプトが役立ちます。
The original test test_cli_missing_file now fails: expected exit code 2.
Keep the original tests unchanged. Inspect only the allowed files and
restore the prior CLI error behavior. Run all ten tests, the CLI fixture,
and the scope checker again. Report the actual output.
上記は修正のテンプレートです。実際に別のテストで失敗したなら、そのテスト名と観測した出力に置き換えます。範囲外の編集は個別に確認し、今回意図しなかった変更だけを戻します。実プロジェクトで無関係の作業まで失う一括コマンドは避けてください。
処理モデルの証拠、プロンプト、基準コミット、パッチ、テスト出力、修正の履歴をまとめて保存します。SonnetとOpusのコーディング比較へ進む場合も、開始状態と合格条件をそろえます。小さなリファクタリング一回の成功から、モデル全般の優劣は結論できません。
完了とする条件
同じ方法はデータベース処理や整形関数の分離にも使えますが、先に必要な契約を追加します。データベースならトランザクション境界、非同期処理なら例外とキャンセルの動作も維持対象です。この10項目をどんなプロジェクトにも十分なチェックリストとして使わず、旧動作に依存する呼び出し元を調べてから回帰テストを選んでください。
従来のインポートが動き、変更していない10テストが成功し、CLI出力が一致し、許可した三つのパスだけが変わり、モジュールの責務が読み取れれば、この演習を受け入れられます。未検証の条件を記録し、勝手に作業を広げません。得られるのは検証可能な小さな変更と再利用できる指示であり、指示だけで不要な編集を完全に防げるという保証ではありません。
よくある質問
- CLAUDE.mdで範囲外の編集を強制的に禁止できますか?
- できません。指示の文脈を与えるファイルであり、アクセス制御ではありません。必要に応じて権限設定と隔離した作業環境を使い、実際の差分を確認します。
- 参考実装はSonnet 5.5が生成したものですか?
- いいえ。Ofoxが演習と参考実装を作成し、ローカルでテストしました。プロンプトは、利用権限のあるClaude Codeセッションで試すためのものです。
- リファクタリングで維持するのはどの動作ですか?
- 公開インポート、出力の型と順序、計算方法、エラーメッセージ、終了コードです。機能追加や検証ルールの変更は別の作業として扱います。


