Skip to main content

Move Function, Move Field, and Split Phase

What This Concept Is​

Three relocation moves that fix where code and data live.

Move Function: relocate behavior when ownership and change pressure support a better boundary. Reading another object frequently is a signal to investigate, not a sufficient decision rule.

Move Field: relocate state after defining its owner, identity, lifetime and migration. Shared references and serialized data may require more than changing a declaration.

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];
}

This sketch changes evaluation order: the original looks up the price before parsing quantity; the split version parses quantity first. For ordinary immutable price maps and primitive strings that distinction may be outside the chosen boundary. For effectful lookups or conversions, characterize it. JavaScript parseInt also accepts prefixes such as "2x"; rejecting them is a separate repair. The offline lab preserves lookup-before-conversion explicitly.

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:

  1. Identify dependencies. If the function calls other helpers that also belong in the target, move those first.
  2. Copy the function into the target context. Adjust parameters.
  3. Change the source to delegate (return target.fn(...)).
  4. Run tests.
  5. Migrate callers to call the target directly.
  6. Remove the wrapper only when migration and compatibility evidence permit it; an intentional stable facade may remain.

Check Yourself​

  1. What smell is Move Function fixing? What smell is Move Field fixing?
  2. In Split Phase, why does the intermediate record matter so much?
  3. Why should Move Function leave a delegator temporarily instead of being done in one step?

Mini Drill or Application​

Split price_order in the lab. On input X 2x, the unknown SKU lookup must fail before quantity conversion. Compare a token record that retains quantity text with eager numeric parsing. Keep the public error and lookup trace. Explain when a separately approved validation policy would justify changing that order.

Engineering Extension: Keep error precedence deliberate​

In the lab, X 2x produces KeyError for X before quantity parsing. A typed intermediate that eagerly parses 2x raises ValueError instead. Both designs may be reasonable; only one preserves this baseline. Retaining the token until the pricing phase is an explicit intermediate-contract choice.

Use the behavior contracts, labs and practice feedback to test the claim.

Read This Only If Stuck​

Locate this move through the Refactoring book index. The selective reading guide gives exact consulted local locators and primary documentation. Index links are not exact chapter links.