Skip to content

Split Phase

When one block of code does two genuinely different things in sequence — say, parse some input and then compute a result from it — split it into two phases. The first phase produces a clear intermediate data structure; the second phase consumes only that structure.

This cures tangled responsibilities inside a single stretch of code. Parsing and calculation get interleaved, so a change to the input format forces you to touch the math, and a change to the math forces you to think about strings. The two concerns share variables and the reader can never focus on one at a time. Naming an intermediate structure draws a clean line between them.

A function parses a raw "name:qty:price" string and computes a total in one breath. Split it: parse into an order object, then price the order.

// Before
function orderTotal(raw: string): number {
const parts = raw.split(':');
const qty = Number(parts[1]);
const price = Number(parts[2]);
return qty > 100 ? qty * price * 0.9 : qty * price;
}
// After
interface ParsedOrder {
name: string;
qty: number;
price: number;
}
function parseOrder(raw: string): ParsedOrder {
const [name, qty, price] = raw.split(':');
return { name, qty: Number(qty), price: Number(price) };
}
function priceOrder(order: ParsedOrder): number {
const base = order.qty * order.price;
return order.qty > 100 ? base * 0.9 : base;
}
function orderTotal(raw: string): number {
return priceOrder(parseOrder(raw));
}
flowchart LR
  R["Raw input<br/>(string)"] --> P1["Phase 1: Parse"]
  P1 --> I["Intermediate data<br/>(structured order)"]
  I --> P2["Phase 2: Calculate"]
  P2 --> O["Result<br/>(price)"]
Two phases joined by an explicit intermediate structure
  1. Extract the second phase (the calculation) into its own function. Run your tests.
  2. Introduce an intermediate data structure and pass it as the argument to that second-phase function.
  3. Examine each value the second phase reads from the first phase. Move each one onto the intermediate structure, one at a time, running tests after each move.
  4. Extract the first phase (the parsing) into its own function that returns the intermediate structure.
  5. Confirm the top-level function is now just first phase feeding second phase, and run the full suite.

Split Phase when a block clearly handles two stages — parse then compute, validate then act, fetch then format — and the stages share little beyond a handoff. The intermediate structure becomes a named contract between them, so each phase can change independently and be tested on its own.

The cost is one extra data type and a small indirection. If the two “phases” are tightly intertwined or trivially short, the seam is artificial — leave the code as one piece.

What joins the two phases after a Split Phase refactoring?
Which pair is a typical candidate for Split Phase?
When is Split Phase NOT worth applying?