Skip to main content

Refactor a Mess (Safely)

beginner11 min readLesson 57 of 204

The refactoring loop: characterize behavior first, small steps, compile+test after each โ€” behavior preserved, structure improved.

The loop

  1. Characterize: write tests (or run existing ones) that pin the current behavior โ€” including the ugly parts.
  2. Small step: one structural change โ€” extract a function, rename, replace a magic number.
  3. Compile + tests green. If red, undo the step (it was one step โ€” that is the point).
  4. Repeat until the mess is structure.

Never mix "refactor" and "add a feature" in the same step โ€” the commit that does both is the commit you cannot trust.

A mess, and its dissection

// BEFORE: 60 lines in main โ€” parse, validate, compute, print, all interleaved
int main() { /* read csv line, split, stoi everywhere, if-chains, cout... */ }

Extraction order that always works: constants โ†’ pure functions (parse, compute โ€” no I/O) โ†’ I/O boundary (read/print) โ†’ data type (struct Record). Pure functions first: they are the tests' favorite food.

// AFTER:
struct Record { std::string name; int qty{}; double price{}; };
std::optional<Record> parse_record(const std::string& line);
double record_total(const Record& r);
void print_report(const std::vector<Record>& rows);

Same behavior (your characterization tests still pass), new shape: every unit named, testable, and single-purpose.

The compiler is your refactor partner

Renaming a function: change the declaration, compile, follow every error to its caller. C++'s strictness โ€” the thing that made week one painful โ€” is exactly what makes refactoring safe here: nothing breaks silently.

Now practice

Refactor Practice: Structure RecoveryExtract constants, split a compound predicate into named helpers, and const-correct a signature set.1 challenge ยท ยท ~20 min