Skip to content

Move Statements to Callers

A function that started as a single clear unit can grow until it is doing slightly different things for different callers. When part of its body should vary by caller, pull that part out of the function and into each caller. The function keeps the shared core; the variable behaviour moves to where the variation actually lives.

You notice yourself reaching for a boolean parameter — render(person, includeFooter) — or wishing one particular caller could skip a chunk of the function. That itch means the function is bundling a step that is not universal. The common part and the per-caller part have been welded together. This is the exact inverse of Move Statements into Function: there, identical statements wanted to come in; here, divergent statements want to go out.

A renderPerson that always emits a photo block at the end. A new caller needs the heading but a different footer. Rather than add a flag, we move the photo block out to the callers.

// Before — one caller wants a different ending, tempting a flag
function renderPerson(person: Person): string {
return [
`<p>${person.name}</p>`,
emitPhotoData(person.photo),
].join('\n');
}
const listing = people.map(renderPerson).join('\n');
// After — shared core stays, the varying tail moves to callers
function renderPerson(person: Person): string {
return `<p>${person.name}</p>`;
}
const listing = people
.map((p) => [renderPerson(p), emitPhotoData(p.photo)].join('\n'))
.join('\n');
// a different caller can now end differently
const compact = people.map(renderPerson).join('\n');
  1. Identify the statements that should vary by caller — usually the leading or trailing part of the function, not the middle.
  2. If there are only one or two callers, copy the statements directly into each caller, right next to the call.
  3. With many callers, do it safely: extract the remaining (shared) body into a new function, leave the original calling that new function plus the variable statements, then point callers at the new shared function one at a time.
  4. Run your tests after wiring each caller.
  5. Once every caller holds its own copy of the moved statements, remove them from the original function.
  6. Rename the shared function if its narrower responsibility now deserves a clearer name.

Reach for this when a function is almost right but one part needs to differ per caller, and you would otherwise reach for a flag parameter. Moving the variation out keeps each function focused on a single, consistent job.

The cost is some duplication at the call sites — each caller now repeats the moved statement. That is acceptable when the callers genuinely differ; the duplication is honest. If the statement turns out to be identical everywhere after all, you have gone the wrong way: apply Move Statements into Function to fold it back.

What signals that statements should move to the callers?
With many callers, what is the safe way to move statements out?
Move Statements to Callers is the inverse of which refactoring?
What is the accepted trade-off of moving statements to callers?