**The brief.** [Exercise 4](@/AgenticEngineering/Introduction/Source/Exercise4)'s order calculator **works** — and it is ugly: the same logic copied twice, unexplained numbers hard-coded in the middle of it, names that tell you nothing. Nobody touches it until it is **pinned** — until what it really does is written down as checks that run. First *characterize* it: capture six of its behaviours by *running* the code and recording each result as a check (that record is your spec — the outcome you want, written so it runs). Then *refactor* — tidy the code without changing what it does — and let the equivalence table prove the output never moved.
This is Module 1's discipline at working size — one step past Predict the Behaviour: there you pinned someone else's function; here you pin and then change, and the pins — your running record of what the code does — are what make the change safe. It is the exact move Module 4 — Refactoring turns into a profession.
The legacy code
The module's own Exercise 4, distilled to the function that matters:
// ugly but works — in prod, no tests
static decimal LegacyTotal((string N, decimal P, int Q)[] basket)
{
decimal x = 0;
foreach (var i in basket) x += i.P * i.Q;
if (x > 1000) x = x - x * 0.05m;
if (x > 2000) x = x - x * 0.02m;
return x;
}
Two ifs, three unexplained hard-coded numbers, one variable called x — and at least three behaviours in there that most readers get wrong on the first guess.
The rules
- Characterize first. The
Pinnedcolumn in the workbench is pre-filled with plausible guesses — three are wrong. Run, read theLegacycolumn (that is the truth), and correct the pins until the legacy agrees with every pin. Do not touch the baskets; only the pins move. - Then refactor. Rewrite the logic in
Total— named constants, one clear method, honest names. TheRefactoredcolumn must agree with the pins too: ✓ identical, all six rows. - The behaviours worth your attention: is the first threshold
>or>=? Does the secondifread the gross total — or the already-discounted one? Is a 3,000 basket a flat 7% off — or something subtler?
Your workbench
When every row says ✓ you have done a true refactor: the shape changed, and the behaviour — including the quirky sequential discount — provably did not.
Pinned and tamed — or stuck on a wrong pin? Reveal the solution — the full battery with each surprise explained, and a clean refactor that stays byte-identical.