レガシーコードを安全に変更するには、現在の結果を記録し、構造変更と方針変更を分けます。既存動作を記録するテストは、その業務ルールが正しいことの保証ではありません。
仕組みと設計の判断基準
リファクタリングは、外部から観察できる動作を保って内部構造を変える作業です。ルールが不明なら、通常の入力だけでなく分岐の境界、値の欠落、失敗経路を記録します。ここでは顧客区分Bの正の金額から30を引く関数を扱います。 一次資料
再現コード
実験用の割引関数を条件式による実装へ変えます。-1、0、1、29、30、31、100と別の顧客区分を含む9件を比較します。期待値は旧関数の呼び出し結果から作らず、表に独立して記述しました。
この独立したサンプルは2026-10-23に、arm64 macOS向けのApple Swift 6.4をSwift 6言語モードで実行しました。ネットワーク接続や追加パッケージは不要です。
ダウンロード先のフォルダーで実行してください。検証条件を満たさない場合はpreconditionで停止します。検証が削除されるため、-Ouncheckedは使用しないでください。
swift -swift-version 6 legacy-code-maintenance-problems.swift
func legacyPrice(kind: String, amount: Int) -> Int {
if kind == "B" && amount > 0 { return amount - 30 }
return amount
}
func refactoredPrice(kind: String, amount: Int) -> Int {
let discount = kind == "B" && amount > 0 ? 30 : 0
return amount - discount
}
let cases: [(String, Int, Int)] = [
("B", -1, -1), ("B", 0, 0), ("B", 1, -29),
("B", 29, -1), ("B", 30, 0), ("B", 31, 1),
("B", 100, 70), ("A", 100, 100), ("A", 0, 0)
]
var clampDifferences = 0
for (kind, amount, expected) in cases {
precondition(legacyPrice(kind: kind, amount: amount) == expected)
precondition(refactoredPrice(kind: kind, amount: amount) == expected)
let changed = kind == "B" && amount > 0 ? max(0, amount - 30) : amount
if changed != expected { clampDifferences += 1 }
}
precondition(clampDifferences == 2)
print("cases=9 preserved=9 clamp-differences=2")
実行結果と読み取り方
cases=9 preserved=9 clamp-differences=2
両実装は9件すべてで一致しました。負の合計を0に丸めると、金額1と29の結果が変わります。clamp-differences=2は失敗の隠蔽ではなく、その方針変更が既存の契約を2か所で変えることを確認する別の検証です。
適用範囲と注意点
負の請求額を許可すべきという結論ではありません。実際の割引方針は担当者に確認してください。小さい整数だけを使っており、税金、通貨の丸め、オーバーフロー、DBの副作用は未検証です。9件で全入力の同等性も証明できません。
次に確認すること
実際には呼び出し元と保存データを調べて観察例を増やします。負の金額を禁止するなら新しい期待値に合意し、別の変更として適用します。DBがテストを妨げる場合は、保存先の依存性注入へ進んでください。
出典と確認基準
- Definition of RefactoringMartin Fowler · 著者の原文 · 確認日 2026年10月23日根拠: 実装の契約と説明の根拠です。出力は、この記事に掲載した実験で別途確認しました。

