A quote has stages
Validate input, calculate a subtotal, format the result and print it. Before refactoring, write expected behavior for one valid case and one invalid case. The restructuring must not change them accidentally.
function validateQuantity(quantity) {
if (!Number.isInteger(quantity) || quantity <= 0) {
throw new Error("Invalid quantity");
}
}
function calculateSubtotal(unitPriceCents, quantity) {
return unitPriceCents * quantity;
}
function formatQuote(subtotalCents) {
return `Quote: ${subtotalCents} cents`;
}
function prepareQuote(unitPriceCents, quantity) {
validateQuantity(quantity);
if (!Number.isSafeInteger(unitPriceCents) || unitPriceCents < 0) {
throw new Error("Invalid price");
}
const subtotalCents = calculateSubtotal(unitPriceCents, quantity);
if (!Number.isSafeInteger(subtotalCents)) throw new Error("Amount too large");
return formatQuote(subtotalCents);
}
console.assert(prepareQuote(500, 2) === "Quote: 1000 cents");
Number.isSafeInteger ensures integer arithmetic stays within JavaScript's safe integer range at the checked point. The coordinator establishes preconditions before calling the small calculation. Not every internal function needs duplicate validation if its boundary is explicit.
Guided refactor procedure
Capture behavior, extract one responsibility, rerun checks, then extract another. Renaming, moving files and changing business rules at the same time makes a mismatch harder to explain.
Independent lab
Make prepareQuote return a numeric subtotal instead of formatted text. Move formatting to its caller. Explain whether this is a behavior-preserving internal refactor or a change to its public contract.
Correction
It changes the function's return contract. Update its caller and contract tests intentionally. The underlying numeric pricing rule can remain the same. A change can improve design while still requiring coordinated caller changes; do not call every rewrite a harmless refactor.
Review
A clean program exposes its decisions at the right boundaries. Do not pursue purity at the expense of useful behavior: the final program still needs an entry point that connects calculations to observable output.