I keep tripping over 'true, false, true'
Source: Hacker News
Every so often I open a PR and see something like this:
deployFeature(flag, true, false, true);
I run into it more often than I’d like. Not because it’s complicated—just because I have no idea what I’m looking at. So I click into the function definition, scroll a bit, lose my place, jump back, re‑read the line… and only then does it click. Tiny interruption, still annoying every time.
I’m not reading code anymore, I’m decoding it
Here’s a simpler one:
createUser(user, true, false);
What does that mean? Is the user an admin? Are we sending a welcome email, or skipping validation? I don’t know. At that point I’m not really reading code anymore—I’m decoding it.
There’s a name for this (“flag arguments,” sometimes “boolean blindness”), but I don’t need the term to feel the problem.
I’ve definitely written this before:
createUser(user, true, false); // isAdmin, sendWelcomeEmail
If the function call needs a comment to explain the arguments, the API is probably working against me.
Why this feels fine at the time
Because when I’m writing the function, it feels perfectly reasonable:
function createUser(user, isAdmin, sendWelcomeEmail) {
// …
}
No extra objects, no extra structure—just pass the values in and move on. I’ve done this plenty of times without thinking twice about it. It’s only later, when I’m reading the call site, that it starts to feel off. The convenience usually gets paid for later by whoever has to read it, including me two weeks from now.
What I use now
Most of the time, I just use an object instead:
createUser(user, {
isAdmin: true,
sendWelcomeEmail: false,
});
Now I can actually tell what’s happening without jumping back to the function definition. It also scales naturally:
createUser(user, {
isAdmin: true,
sendWelcomeEmail: false,
skipValidation: true,
});
Trying to stretch positional booleans that far quickly becomes awkward.
Sometimes the boolean is hiding a different action
createUser(user, true);
If true really means “create an admin user,” that’s probably not a flag anymore—it’s a different action. I’ll usually make it explicit:
createAdminUser(user);
createRegularUser(user);
Now there’s not much left to interpret.
To be fair, this isn’t always bad
Sometimes this is completely fine:
toggleMenu(true);
That’s clear enough. This tends to work when:
- the meaning is obvious
- the function is small and local
- there’s only one flag
But once I add a second boolean, readability usually drops pretty fast.
TypeScript doesn’t really save this
TypeScript tells me the values are booleans, but that’s not the problem.
createUser(user, true, false);
The types are technically correct; I still have to remember what the arguments mean.
What helped more for me was switching to options objects:
createUser(user, {
isAdmin: true,
sendWelcomeEmail: false,
});
Or sometimes just replacing the boolean entirely:
createAdminUser(user);
Usually that’s a sign the flag was hiding two different actions anyway.
Same behavior, much easier to read
Before:
fetchData(url, false, true, 3);
After:
fetchData(url, {
useCache: false,
retryOnFail: true,
retries: 3,
});
I’ve also seen real calls like this in production code:
updateSettings(user, true, false, true, false);
At that point I’m back to counting arguments with my finger—same behavior, a lot less mental overhead.
Why this keeps costing me time
Most of the time I’m not writing code; I’m trying to understand it, even if it’s my own code from a few weeks ago. Every time I run into something like:
updateSettings(user, true, false, true, false);
I stop for a second and try to remember what each argument was supposed to mean. It’s a tiny speed bump, but one I seem to hit over and over again.