Big Refactoring Planner
by @quochungto
Plan and execute architectural-scale refactoring campaigns that take weeks to months — the four named patterns for large-scale structural restructuring from...
Example 1: Tease Apart Inheritance — Deal Hierarchy
Situation: A codebase has a Deal hierarchy with subclasses ActiveDeal, PassiveDeal, TabularActiveDeal, and TabularPassiveDeal. Every time a new deal type is added, tabular and non-tabular subclasses must both be added. Every time a new presentation style is added, active and passive subclasses must both be added. The team needs to add a chart-based presentation style.
Pattern confirmed: Tease Apart Inheritance — same adjective prefix ("Tabular") appears in both branches; the hierarchy is growing combinatorially.
Two-dimensional grid:
| Active Deal | Passive Deal |
-------------|-------------|--------------|
(single) | X | X |
Tabular | X | X |
Decision — which job stays: Deal type (Active/Passive) has more code; presentation style (single/tabular) has less. Extract presentation style.
Campaign milestones:
Milestone 1 (Day 1-2): Apply Extract Class at Deal superclass — create PresentationStyle. Add presentation instance variable to Deal. Tests pass.
Milestone 2 (Day 3-5): Create TabularPresentationStyle and SinglePresentationStyle subclasses of PresentationStyle. Initialize presentation in TabularActiveDeal and TabularPassiveDeal constructors to TabularPresentationStyle. Tests pass.
Milestone 3 (Day 6-10): Apply Move Method in each tabular subclass — move presentation-related methods to TabularPresentationStyle. When a subclass is empty, delete it. Apply Move Method in single subclasses similarly. Tests pass after each deletion.
Milestone 4 (Day 11-12): Apply Pull Up Method/Field to simplify each hierarchy now that they are separated. Tests pass.
Stopping condition: TabularActiveDeal and TabularPassiveDeal are deleted. Adding ChartPresentationStyle requires only one new class.
Example 2: Separate Domain from Presentation — Order Window
Situation: An OrderWindow class (400 lines) handles all GUI rendering and also contains SQL queries for fetching product prices, customer discount calculations, and order total computation. The team cannot unit test pricing logic because it is embedded in GUI event handlers.
Pattern confirmed: Separate Domain from Presentation — SQL statements in a window class, business calculation in event handlers.
Purpose: Make pricing logic unit-testable without a running GUI.
Campaign milestones:
Milestone 1 (Day 1): Create Order domain class linked from OrderWindow. Create OrderLine for grid rows. Tests pass (no behavior moved yet).
Milestone 2 (Day 2-5): Examine each field. customerCodes field is not displayed — apply Move Field directly to Order. Display fields (customerName, amount) — apply Duplicate Observed Data: add mirrored fields to Order with sync. Tests pass.
Milestone 3 (Day 6-15): Apply Extract Method to isolate SQL calls and pricing calculations in OrderWindow. Apply Move Method to transfer each extracted operation to Order. Drive all SQL toward Order. Tests pass after each move.
Observable stopping signal: OrderWindow no longer imports java.sql. Pricing methods exist on Order and can be unit tested without instantiating the GUI.
Example 3: Extract Hierarchy — Billing Scheme (Variant A)
Situation: BillingScheme has flags for disability status, lifeline status, and business type. Every method contains conditional logic checking these flags. Adding a new billing category requires editing conditional logic in eight methods.
Pattern confirmed: Extract Hierarchy, Variant A — variations not fully clear upfront; flags static during object lifetime.
First variation picked: disability scheme.
Campaign milestones:
Milestone 1: Apply Replace Constructor with Factory Method on BillingScheme. Create DisabilityBillingScheme subclass. Factory method returns DisabilityBillingScheme when disability flag is set. Tests pass.
Milestone 2: Copy createBill to DisabilityBillingScheme. Simplify — remove branches that check disabilityScheme() since the subclass is always in disability context. Tests pass.
Milestone 3: Continue for other methods with disability conditionals. Pick next variation (lifeline). Create LifelineBillingScheme. Repeat. Tests pass after each subclass iteration.
Milestone 4: When all variations are subclassed, declare BillingScheme abstract. Delete conditional method bodies from the superclass. Tests pass.
Stopping condition: Adding a new billing category requires creating one new subclass of BillingScheme — no edits to existing classes.
clawhub install bookforge-big-refactoring-planner