The Refactoring Catalog: Core Techniques

Clean Code, SOLID & Refactoring

Chapter 8 · The Refactoring Catalog: Core Techniques

Every prior chapter identified a problem. This chapter names the concrete, mechanical moves that fix them — some of which you've already performed without the name attached. Two of the five get their strongest verification yet here; the other three are callbacks to work already done.

Extract Method: One Fix, Every Caller

# BAD — the same calculation duplicated across two functions def print_invoice_bad(order): total = 0 for item in order['items']: total += item['price'] * item['qty'] tax = total * 0.08 return f"Total: ${total:.2f}, Tax: ${tax:.2f}" def print_receipt_bad(order): total = 0 for item in order['items']: total += item['price'] * item['qty'] # duplicated return f"You paid: ${total:.2f}"
Verified directly — a real fix to one duplicated copy leaves the other genuinely wrong
A real business change — a 10% loyalty discount — is applied to only print_invoice_bad. Afterward: print_invoice_bad correctly returns "Total: $90.00, Tax: $7.20"; print_receipt_bad still returns "You paid: $100.00" — the un-fixed copy, silently out of sync with the fixed one.
Verified directly — extracting the calculation makes one fix reach both callers automatically
Extracting the shared logic into calculate_order_total(order), called from both print_invoice_good and print_receipt_good: applying the identical discount to the one shared function makes both correctly return $90.00print_invoice_good and print_receipt_good both reflect the fix, with neither one edited directly.

Extract Variable: Naming an Expression Makes It Debuggable

# BAD def is_eligible_bad(user): if user['age'] >= 18 and user['country'] == 'US' and user['verified']: return True return False # GOOD def is_eligible_good(user): is_adult = user['age'] >= 18 is_us_resident = user['country'] == 'US' is_verified = user['verified'] return is_adult and is_us_resident and is_verified
Verified directly — extracting variables changes nothing about correctness
Across four test users (all-pass, too young, wrong country, unverified): both versions agree on every case, confirmed directly. Extracting the sub-expressions changed nothing about what the function computes.
Verified directly — extraction makes the exact cause of a rejection immediately visible
For a rejected 16-year-old, verified US, verified identity: is_eligible_bad reports only False — no way to tell which condition failed without re-evaluating the whole expression by hand. The extracted version shows is_adult = False, is_us_resident = True, is_verified = True — immediately, unambiguously identifying age as the specific cause.

Move Method & Rename — Already Fully Verified

Move Method/Field: Chapter 5's own Feature Envy fix — moving calculate_total off InvoicePrinter and onto Order, verified reducing direct data access to zero — is Move Method, performed before this chapter named it. Rename: Chapter 2's entire verified case for descriptive naming — the distance_fee/fee comparison, the consistent-vocabulary search results — is the case for this technique specifically. Nothing new to verify; the findings already stand.

Replace Conditional with Polymorphism

# BAD — a type-switch that must be edited for every new type def calculate_shipping_bad(shipping_type, weight): if shipping_type == 'standard': return weight * 0.5 elif shipping_type == 'express': return weight * 1.2 + 5 elif shipping_type == 'overnight': return weight * 2.5 + 15 # GOOD — reusing Design Patterns Chapter 7's own Strategy shape directly class ShippingStrategy: def calculate(self, weight): raise NotImplementedError class StandardShipping(ShippingStrategy): def calculate(self, weight): return weight * 0.5
Verified directly — extending the type-switch requires editing the same existing function; extending the polymorphic version doesn't
Adding a new shipping type, 'international', to calculate_shipping_bad requires rewriting the whole function, inserting a new elif among the existing branches. Adding the equivalent InternationalShipping(ShippingStrategy) class: StandardShipping, ExpressShipping, and OvernightShipping — captured via inspect.getsource() before and after — all confirmed byte-identical, True for all three.
Verified directly — both versions agree on cost for every shipping type
Standard: 5.0 vs 5.0. Express: 17.0 vs 17.0. Overnight: 40.0 vs 40.0. International: 55.0 vs 55.0. All four match — the refactor changed how the code is organized, not what it computes.
This is Chapter 6's Open/Closed, performed as an explicit refactoring step
Chapter 6 measured Open/Closed as a static property of already-designed code. This technique is how you get there from a type-switch that violates it — and it's the exact same shape as Design Patterns Chapter 7's own ShippingStrategy example, reused directly rather than reinvented.

Where This Connects

This chapter's findingWhat it connects to
One shared function fixing both callers automaticallyChapter 5's own duplicated-code finding — Extract Method is the concrete fix for exactly that smell
Extracted variables making a rejection's own cause immediately visibleChapter 2's own naming findings — a named variable is documentation that stays attached to the exact value it describes
Replace Conditional with Polymorphism reproducing Design Patterns' own StrategyDesign Patterns Chapter 7 — the pattern was built there; this chapter shows the mechanical steps that arrive at it from tangled conditional code

Hands-On Exercises

Exercise 1

Add a third function, print_summary_email(order), that also needs the order total, calling this chapter's own shared calculate_order_total. Apply a different real change (a flat $2 handling fee) to the shared function and verify all three callers reflect it correctly.

📄 View solution
Exercise 2

Add a fourth extracted condition to this chapter's own is_eligible_goodhas_valid_payment_method — and a matching check in is_eligible_bad's own compound expression. Verify a user failing only this new condition is immediately identifiable in the extracted version.

📄 View solution
Exercise 3

Add a second new shipping type to this chapter's own polymorphic hierarchy, EconomyShipping, alongside the existing InternationalShipping from this chapter. Verify all four now-existing classes (including InternationalShipping) stay byte-identical, and verify the new type's own cost matches a hand calculation.

📄 View solution

Chapter 8 Quick Reference

  • Extract Method, verified: a duplicated calculation left one caller un-fixed after a real change; the extracted version fixed both callers from one shared function
  • Extract Variable, verified: both versions computed identically, but only the extracted one made a rejected user's exact failing condition immediately visible
  • Move Method & Rename: already fully verified in Chapters 5 and 2 — no new findings needed
  • Replace Conditional with Polymorphism, verified: 3 existing classes stayed byte-identical after a real extension; all 4 shipping types agreed on cost between old and new implementations
  • Next chapter: Technical Debt — naming it, measuring it, and paying it down deliberately