Skip to content

Move Statements into Function

When a line of code is always paired with a call to some function — running just before it or just after it, at every single call site — that line belongs inside the function. Move it in. The function now owns the complete job, and callers shrink to a single call they cannot get wrong.

You find the same preparatory or trailing statement copy-pasted above or below a function call everywhere it appears. Maybe every caller of renderName first writes an opening tag, or every caller of fetchOrder logs the same line afterwards. That repeated statement is part of the behaviour the callers expect, yet it lives outside the function, where each new caller has to remember to add it. Forget once and you have a quiet bug. The duplication is a sign the statement wants to move in.

A function that emits a person’s photo HTML. Every caller writes the same <p> heading line right before calling it. That heading is really part of “render the photo block”, so we move it in.

// Before
function photoData(photo: Photo): string {
return [
`<p>location: ${photo.location}</p>`,
`<p>date: ${photo.date.toDateString()}</p>`,
].join('\n');
}
function renderPerson(person: Person, photo: Photo): string {
return [
`<p>${person.name}</p>`,
`<p>title: ${photo.title}</p>`, // repeated at every call site
photoData(photo),
].join('\n');
}
function emitPhoto(photo: Photo): string {
return [
`<p>title: ${photo.title}</p>`, // repeated here too
photoData(photo),
].join('\n');
}
// After
function photoData(photo: Photo): string {
return [
`<p>title: ${photo.title}</p>`, // moved in — now owned by photoData
`<p>location: ${photo.location}</p>`,
`<p>date: ${photo.date.toDateString()}</p>`,
].join('\n');
}
function renderPerson(person: Person, photo: Photo): string {
return [`<p>${person.name}</p>`, photoData(photo)].join('\n');
}
function emitPhoto(photo: Photo): string {
return photoData(photo);
}
  1. Confirm the candidate statement runs adjacent to the call at every site. If even one caller does it differently, this move is wrong — that variation is real.
  2. If the call site holds other code, first apply Extract Function to isolate exactly the call plus the repeated statement into one helper. Now you only have one place to work.
  3. Move the repeated statement into the target function — at the top if it ran before the call, at the bottom if it ran after.
  4. Run your tests.
  5. Delete the now-redundant statement from each caller, one at a time, running tests after each deletion.
  6. If you created a temporary helper in step 2, inline it once every caller is clean.

Use this when a statement is genuinely part of the function’s responsibility but happens to live outside it. Folding it in removes duplication and makes the function a complete, hard-to-misuse unit.

Do not use it when callers differ in whether or how they run the statement — forcing a shared step onto a function that some callers want to skip just trades duplication for a flag parameter, which is worse. The inverse refactoring is Move Statements to Callers: when a function bundles a step that only some callers want, push it back out.

When is a statement a good candidate to move into a function?
If a call site contains other unrelated code, what should you do first?
What is the inverse of Move Statements into Function?
What is a sign you should NOT move a statement in?