Three copies of the delivery charge calculation
A courier company's quoting script has grown to 280 lines with no functions. The delivery charge is calculated in three places: the quote screen, the invoice generator and the monthly report. The three copies started identical, but the report copy rounds to the nearest penny after every step while the other two round once at the end, so one shipment shows £12.47 on the quote and £12.45 in the report. A developer must add a Saturday surcharge and proposes copying the quote-screen code a fourth time, arguing it is quicker and will not break the invoice. Testing is done by hand on three real shipments. The surcharge rate lives in a global variable that the report also modifies.
1. Propose a single function to replace the three copies: give its inputs and outputs and state what should be a parameter, what should be a constant, and what should be fixed inside the function. Justify each choice.
Read the worked answer
Signature: delivery_charge(weight_kg, distance_km, service_level, is_saturday, saturday_rate) -> int returning pence.
Parameters - weight, distance, service level, is_saturday: these genuinely vary per shipment, so they must come in from outside.
Constant / config - saturday_rate: one definition, read-only. It must not arrive as a mutable global that the report also changes, because then the invoice total depends on which code ran first - a bug that is invisible in a code review and impossible to reproduce on demand.
Fixed inside, not a parameter - the rounding policy. Rounding is a consistency rule, not a per-shipment property. If each caller passes 'round early' or 'round late', the £12.47/£12.45 divergence is simply re-created in a tidier shape. Pick one policy (compute in integer pence, round once at the end), have finance confirm it, and encode it in one place.
Return integer pence, not a float. Pence make 'the quote disagrees with the report by two pennies' structurally impossible rather than merely unlikely.
2. The developer argues that copying the code a fourth time is safer because it cannot break the invoice. Using the numbers in the setup, rebut this and describe the cheapest evidence that would settle the question.
Read the worked answer
The argument is already disproved by the setup. The three copies were identical and have already diverged, producing a 2p difference on the same shipment - and that happened before anyone added a surcharge. Copying has not preserved the invoice; it has quietly made the invoice disagree with the quote, with nothing failing and both figures looking equally plausible to a user.
A fourth copy makes it worse, not safer. Four places must apply the Saturday surcharge, four places must apply rounding, and a fix to one is a change to none of the others. 'It will not break the invoice' is true only in the sense that the invoice is already wrong in a way nobody notices.
Cheapest evidence (an afternoon, no rewrite): take about ten shipments - including one Saturday, one with fractional weight, one exactly at a service-level boundary, one rounding-sensitive value such as 12.465 - and run each through all three current code paths, tabulating quote vs invoice vs report. The divergence appears in minutes and is concrete rather than argued. The same table then becomes the regression test for all four call sites after extraction, so the developer gets the safety they asked for, from a test rather than from duplication.
3. Name one responsibility that must stay outside the new charge function, say where it belongs, and explain what goes wrong if it is included.
Read the worked answer
Output formatting and printing must stay outside: the currency symbol, the number of decimal places shown, the word 'Free' for a zero charge, and the HTML or layout of the quote screen.
Where it belongs: the presentation layer for each consumer - the quote screen formats for the customer, the report formats for the finance spreadsheet. Both call the same function and get the same pence value.
What goes wrong otherwise: a function that both computes and prints cannot be tested without capturing printed output, so it will not be tested; it cannot be reused by the report, which is exactly how the fourth copy gets written; and any change to wording or layout becomes a change to pricing code, which is the change you least want to make casually.
The same applies to reading the surcharge rate from a file or environment: that is startup/config work. Keeping the function to one job - shipment facts in, pence out - means there is exactly one place where 'what does this shipment cost?' is answered.