Refactoring techniqueSimplifying Conditional Expressions
Replace Nested Conditional with Guard Clauses
Handle special and invalid cases first with early returns so the normal path runs flat at the end of the function.
- Also known as
- Early Return
Problem
A function wraps its main work in several levels of if/else. The normal case is buried at the deepest indentation, and the else branches that handle edge cases are far from the conditions that trigger them.
Solution
Check each special case at the top of the function and return (or throw) immediately. Leave the normal case as unindented code at the bottom.
Before
function issueToken(req: LoginRequest, user: User | null): AuthResult {
let result: AuthResult;
if (user) {
if (!user.locked) {
if (verifyPassword(req.password, user.passwordHash)) {
if (user.mfaEnabled && !req.otp) {
result = { kind: "mfa-required" };
} else {
result = { kind: "ok", token: signJwt(user.id) };
}
} else {
result = { kind: "denied", reason: "bad-credentials" };
}
} else {
result = { kind: "denied", reason: "locked" };
}
} else {
result = { kind: "denied", reason: "bad-credentials" };
}
return result;
}After
function issueToken(req: LoginRequest, user: User | null): AuthResult {
if (!user) return { kind: "denied", reason: "bad-credentials" };
if (user.locked) return { kind: "denied", reason: "locked" };
if (!verifyPassword(req.password, user.passwordHash)) {
return { kind: "denied", reason: "bad-credentials" };
}
if (user.mfaEnabled && !req.otp) return { kind: "mfa-required" };
return { kind: "ok", token: signJwt(user.id) };
}Why
- Each precondition sits next to its outcome, so you read the rules top to bottom.
- The success path is the last line, at the lowest indentation, which is where readers look for it.
- Adding a new rejection rule is one new line, not another nesting level.
Steps
- Pick the outermost condition whose
elsebranch is a special case. Invert the condition, return theelseresult at the top, and unwrap thethenbody by one level. Run the tests. - Repeat for the next level. Keep the original order of checks, because later checks may rely on earlier ones (here
usermust be non-null beforeuser.locked). - When the only remaining code is the normal path, remove the result variable and return directly. Run the tests.
- If several guards return the same value, consider Consolidate Conditional Expression. Keep them separate when the reasons differ.
Watch out
- Do not use guards when both branches are equally normal, such as choosing between two pricing formulas. A plain
if/else(or a ternary) states that the cases are peers. - Cleanup code at the end of the function will be skipped by early returns. Move it into
finally, ausingblock or a wrapper before adding guards. - Inverting conditions is error prone.
!(a && b)is!a || !b; check each inversion with a test. - Timing-sensitive checks (password comparison, user lookup) may need to run in the same order and count for security reasons. Preserve order exactly.
- The inverse direction, wrapping returns back into nested blocks, has no named technique and is rarely useful.
Related
- Shortens Long Method.
- Often applied after Remove Control Flag and Decompose Conditional.
Agent checklist
- The guarded cases are exceptional; the remaining code is the single normal path.
- Every inverted condition is logically equivalent to the original
elsetrigger. - Check order is unchanged, so null guards still protect later checks.
- No cleanup or logging at the function's end is now skipped.
Treats these smells
See also
- Decompose ConditionalMove a complex condition and each of its branches into well-named functions so the decision reads as intent instead of arithmetic.
- Remove Control FlagReplace a boolean variable that steers a loop or function with break, continue or return so control flow is explicit.
- Consolidate Conditional ExpressionMerge a run of separate checks that all lead to the same result into one condition with a name that states the shared rule.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.