Move Function, Move Field, and Split Phase
What This Concept Is
Three relocation moves that fix where code and data live.
Move Function: a function currently on class A references more of class B's state than A's. Move it to B. Typical signal: Feature Envy.
Move Field: a field is used more by functions on another class than its own. Move it there. Same Feature Envy signal, but on data.
Split Phase: a single function is really doing two sequential jobs. Split it into two functions where phase 1 produces an intermediate structure that phase 2 consumes. Typical signal: the early code uses one set of data, the later code uses another.
Why It Matters Here
Extract/Rename/Inline reshape code within its home. Move and Split reshape where the home is. They are the moves that fix module-level coupling, the smells that Cluster 3 of S3M1 pointed at but did not resolve.
Split Phase is the move that unlocks downstream reuse: a parse step, a validate step, and a format step can each be tested in isolation once split.
Concrete Example
Move Function. Feature Envy on overdraftCharge:
// Before
class Account {
get overdraftCharge() {
if (this.type.isPremium) { /* uses this.type.* heavily */ }
else { /* uses this.type.* heavily */ }
}
}
// After
class AccountType {
overdraftCharge(daysOverdrawn) { /* uses this.* */ }
}
class Account {
get overdraftCharge() { return this.type.overdraftCharge(this.daysOverdrawn); }
}
Split Phase. Parsing tangled with pricing:
// Before
const orderData = orderString.split(/\s+/);
const productPrice = priceList[orderData[0].split("-")[1]];
const orderPrice = parseInt(orderData[1]) * productPrice;
// After: phase 1 produces an intermediate, phase 2 consumes it
const orderRecord = parseOrder(orderString);
const orderPrice = price(orderRecord, priceList);
function parseOrder(aString) {
const values = aString.split(/\s+/);
return { productID: values[0].split("-")[1], quantity: parseInt(values[1]) };
}
function price(order, priceList) {
return order.quantity * priceList[order.productID];
}
Now parsing can be tested without a price list; pricing can be tested with hand-made records.
Common Confusion / Misconception
"I moved the function but left a delegating wrapper behind forever." That is fine briefly, but a forgotten wrapper becomes a permanent indirection. Decide: either Inline Function on the wrapper (finish the move) or keep it intentionally (document why).
Split Phase is not Extract Function. Extract Function separates what from how. Split Phase separates sequential concerns by passing a data structure between them. If the two halves share mutable state, you have not split cleanly; you have extracted.
How To Use It
Mechanics for Move Function:
- Identify dependencies. If the function calls other helpers that also belong in the target, move those first.
- Copy the function into the target context. Adjust parameters.
- Change the source to delegate (
return target.fn(...)). - Run tests.
- Migrate callers to call the target directly.
- Inline the delegating wrapper.
Check Yourself
- What smell is Move Function fixing? What smell is Move Field fixing?
- In Split Phase, why does the intermediate record matter so much?
- Why should Move Function leave a delegator temporarily instead of being done in one step?
Mini Drill or Application
Find a function in your code where more than half of its statements reference another module's fields or methods. Move Function: leave the delegator in place, migrate two callers, confirm tests green, then remove the delegator. Total time goal: under 20 minutes.
Video and Lecture References
- Primary lecture: Fowler -- Split Phase (book chapter 1 walkthrough) - the statement/calculation split
- Visual supplement: Refactoring.guru -- Move Method (animated)
Article References
External Exercises
- In a personal project, find a function that mixes parsing and computation. Split Phase it. Add a separate unit test for each phase.
- Refactoring.guru: move method exercises
Depth Path
- Read This Only If Stuck - Fowler chunks 056 (Move Function part 1), 058 (Move Field), 048 (Split Phase)
- Optional deep dive: chunks 007-010 from the book's opening example where Split Phase separates calculation from formatting
Source Backbone
Refactoring is the canonical book backbone for this module. Use these sources after attempting the refactor and tests yourself.
- Refactoring (Fowler) - primary source for refactoring discipline and named moves.
- Clean Code - support for readability and small-function judgment.
- Good Code, Bad Code - support for maintainability tradeoffs.