Remove Flag Argument
Intent
Section titled “Intent”Take a function that uses a boolean argument to choose between two distinct behaviours, and replace it with two separate functions — one per behaviour — each with a name that says what it does. The caller stops passing a cryptic true and starts calling a function whose name is the answer.
The smell
Section titled “The smell”A flag argument hides intent at the call site. When you read sendEmail(order, true), the true tells you nothing — you have to open the function to learn that true means “send urgently” while false means “queue for later”. The boolean is doing the job a function name should do. It also tends to grow a forked body: an if (urgent) ... else ... that mashes two unrelated flows into one function, so neither reads cleanly and both must be tested through the same door.
Before → After
Section titled “Before → After”A notification function whose boolean picks between an urgent and a normal path. After, two named functions, each focused.
// Beforefunction notify(message: string, urgent: boolean): void { if (urgent) { console.log(`[URGENT] ${message}`); } else { console.log(`[info] ${message}`); }}
notify("Server down", true);notify("Backup finished", false);
// Afterfunction notifyUrgent(message: string): void { console.log(`[URGENT] ${message}`);}
function notifyInfo(message: string): void { console.log(`[info] ${message}`);}
notifyUrgent("Server down");notifyInfo("Backup finished");# Beforedef notify(message, urgent): if urgent: print(f"[URGENT] {message}") else: print(f"[info] {message}")
notify("Server down", True)notify("Backup finished", False)
# Afterdef notify_urgent(message): print(f"[URGENT] {message}")
def notify_info(message): print(f"[info] {message}")
notify_urgent("Server down")notify_info("Backup finished")// Beforefunc Notify(message string, urgent bool) { if urgent { fmt.Printf("[URGENT] %s\n", message) } else { fmt.Printf("[info] %s\n", message) }}
Notify("Server down", true)Notify("Backup finished", false)
// Afterfunc NotifyUrgent(message string) { fmt.Printf("[URGENT] %s\n", message)}
func NotifyInfo(message string) { fmt.Printf("[info] %s\n", message)}
NotifyUrgent("Server down")NotifyInfo("Backup finished")// Beforefn notify(message: &str, urgent: bool) { if urgent { println!("[URGENT] {message}"); } else { println!("[info] {message}"); }}
notify("Server down", true);notify("Backup finished", false);
// Afterfn notify_urgent(message: &str) { println!("[URGENT] {message}");}
fn notify_info(message: &str) { println!("[info] {message}");}
notify_urgent("Server down");notify_info("Backup finished");Mechanics
Section titled “Mechanics”- Confirm the flag truly selects between behaviours, not just a value. If it only toggles a number or a label, Parameterize Function may fit better than splitting.
- Create a new function for one branch of the flag. Give it a name that captures that branch’s intent, like
notifyUrgent. - Copy the relevant branch’s logic into the new function and drop the dead branch. Run your tests.
- Find every caller that passed the flag with that value and redirect it to the new function, removing the boolean argument.
- Repeat steps 2 through 4 for the other branch.
- Run your tests after each redirection so a failure points at one caller.
- When no caller passes the flag any more, delete the original function.
When to use / trade-offs
Section titled “When to use / trade-offs”Split out a flag argument whenever a boolean parameter changes what the function does, whenever a literal true or false at a call site is unreadable, or whenever the body has forked into two flows that share little. Two named functions make each call self-explanatory and let each path be tested directly.
The cost is more functions in the namespace, and some genuinely shared setup may now live in two places — extract that shared part into a private helper so you do not duplicate it. Be pragmatic, too: a flag that merely flips configuration data, with no behavioural fork, is fine to keep. The smell is specifically a boolean that decides between behaviours.