To change legacy code safely, record its current results and separate structural changes from policy changes. A characterization test describes existing behavior; it does not certify that the business rule is correct.
Mechanism and design choice
Refactoring changes internal structure while preserving observable behavior. When the rule is unclear, record branch boundaries, missing values, and failure paths as well as ordinary inputs. Here the existing function subtracts 30 from a positive amount for customer type B. Primary source
Reproduce the experiment
This constructed example replaces the original conditional with a discount expression. Nine cases cover -1, 0, 1, 29, 30, 31, 100 and another customer type. Expected values are written independently in a table rather than obtained by calling the old function.
This standalone example was run on 2026-10-23 with Apple Swift 6.4 targeting arm64 macOS, in Swift 6 language mode. It needs no network or additional packages.
Download the complete Swift file
Run the command in the download directory. Preconditions stop execution if a check fails. Do not use -Ounchecked: it removes these checks.
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")
Recorded output and interpretation
cases=9 preserved=9 clamp-differences=2
Both implementations match all nine expected results. Clamping negative totals to zero changes the results for amounts 1 and 29. The separate clamp-differences=2 assertion demonstrates a policy change; it does not conceal a failing preservation test.
Scope and limitations
This does not recommend allowing negative charges. Confirm the real discount policy with its owner. The experiment uses small integers and does not cover tax, currency rounding, overflow, or database side effects. Nine cases cannot prove equivalence for every possible input.
What to check next
Inspect callers and stored data to extend the observations. If negative totals must be rejected, agree new expectations and make that a separate change. If a database prevents isolated tests, continue with the storage-injection experiment.
Sources and verification
- Definition of RefactoringMartin Fowler · Original author source · Checked October 23, 2026Supports: Source for the implementation contract and explanation. The output was checked separately using the experiment included here.

