Refactoring techniqueComposing Methods
Extract Variable
Name a hard-to-read expression or sub-expression by assigning it to a well-named local variable.
- Also known as
- Introduce Explaining Variable
Problem
A condition or calculation packs several ideas into one expression. You have to evaluate it in your head to learn what it checks.
Solution
Assign each meaningful part of the expression to a local variable whose name says what that part means. Then build the original expression from those names.
Before
function canBookRoom(user: User, room: Room, slot: Slot, now: Date): boolean {
return (
(user.role === "admin" || user.teamId === room.ownerTeamId) &&
slot.start.getTime() - now.getTime() >= 15 * 60_000 &&
slot.end.getTime() - slot.start.getTime() <= room.maxBookingMinutes * 60_000 &&
!room.maintenanceWindows.some((w) => w.start < slot.end && slot.start < w.end)
);
}After
function canBookRoom(user: User, room: Room, slot: Slot, now: Date): boolean {
const hasAccess = user.role === "admin" || user.teamId === room.ownerTeamId;
const leadTimeMs = slot.start.getTime() - now.getTime();
const bookedFarEnoughAhead = leadTimeMs >= 15 * 60_000;
const durationMs = slot.end.getTime() - slot.start.getTime();
const withinMaxDuration = durationMs <= room.maxBookingMinutes * 60_000;
const clashesWithMaintenance = room.maintenanceWindows.some(
(w) => w.start < slot.end && slot.start < w.end,
);
return hasAccess && bookedFarEnoughAhead && withinMaxDuration && !clashesWithMaintenance;
}Why
- The return line now reads as the business rule.
- Debuggers and log statements can inspect each named part.
- It is a cheap first step. If a name proves useful beyond this function, promote it to a method later.
Steps
- Pick a sub-expression that has a clear meaning and no side effects.
- Declare a
constwith a descriptive name and assign the sub-expression to it, placed before the first use. - Replace the sub-expression with the variable. Run the tests.
- Repeat for other parts, working from the innermost meaningful pieces outward.
Watch out
- Short-circuit evaluation changes. In the original,
!room.maintenanceWindows.some(...)never ran if access failed. Hoisting it into a variable runs it every time. That is harmless for pure checks but wrong if a part throws, is expensive, or has side effects. Keep such parts inline or extract them as functions. - If the same name would help in other functions, prefer Extract Method or Replace Temp with Query so the logic is shared.
- Don't name things that are already obvious (
const sum = a + b). - The inverse is Inline Temp.
Related
- Fixes Comments that explain an expression and part of Long Method.
- Inverse: Inline Temp.
- Escalate to Extract Method when a name is needed in more than one place.
Agent checklist
- Each extracted part has no side effects, or its evaluation order and count are unchanged.
- Short-circuit guards that protected a later part (null checks, expensive calls) still protect it.
- Variables are
constand named for meaning, not for type. - Tests pass with identical results.
Treats these smells
See also
- Inline TempReplace a temporary variable that holds a simple expression with the expression itself when the name adds nothing.
- Extract MethodMove a coherent fragment of a long function into its own well-named function and call it from the original place.
- Replace Temp with QueryMove the expression behind a temporary variable into its own method so every method in the class can ask for the value.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.