
Table of contents
Open Table of contents
The Problem
You inherit a codebase and find this:
public class OrderProcessor
{
private bool open;
private bool flag;
private bool done;
private bool status;
public void Process(bool check)
{
if (open && flag)
{
flag = false;
status = true;
}
if (done)
{
// what happens here?
}
}
}Readable? No.
What is open? Is the store open? Should the file open? What is flag? That’s a variable screaming “I’ll fix this later.” And is done a question (“is it done?”) or a command (“mark it done”)?
Boolean names are ambiguous land mines. They look innocent until you have to figure out what they mean during a 3 AM outage.
Ambiguity breeds bugs. A reviewer looks at if (done) and guesses the intent. A maintainer rewrites flag = false and accidentally inverts logic they didn’t understand. if (!flag)… does that mean turn it on? Turn it off?
Boolean names matter because they aren’t abstract labels; they are questions. Your code asks the question, and the boolean value answers “yes” or “no.” If the variable name isn’t a clear question, the answer is meaningless.
The Solution: The 4 Prefixes
You can cover 99% of boolean naming scenarios with just four prefixes. If you stick to these, every boolean becomes a clear, grammatical question.
1. IS: Identity and State
Use is when describing what something is right now. It usually pairs with an adjective.
- Good:
isActive,isDeleted,isEmpty. - Bad:
isAccess(Grammar mismatch. UsehasAccess).
2. HAS: Containment and Features
Use has when describing ownership or inclusion. It pairs with a noun.
- Good:
hasAccess,hasChildren,hasValidationErrors. - Bad:
hasActive(Grammar mismatch. UseisActive).
3. CAN: Capability
Use can to check permissions or potential actions.
- Good:
canEdit,canDelete,canRetry. - Bad:
canAdmin(Vague. UseisAdminorcanAdminister).
4. SHOULD: Intent
Use should for business rules or decisions the system needs to make. This separates “what we can do” from “what we want to do.”
- Good:
shouldRetry,shouldCacheResponse. - Bad:
shouldUser(Incomplete.shouldCreateUser?).
Stick to this list. When you mix them up—like writing isAccess or hasActive—you force the reader to stop and re-parse the sentence in their head. That friction is where bugs get introduced.
| Prefix | Domain | Function | Example |
|---|---|---|---|
| IS | Identity / State | Describes what an object is currently. | isActive, isEmpty |
| HAS | Containment | Describes ownership or feature presence. | hasChildren, hasAccess |
| CAN | Capability | Describes permission or potential action. | canEdit, canRetry |
| SHOULD | Intent / Logic | Describes a business rule recommendation. | shouldCache, shouldRetry |
The “No Negatives” Rule
Here is the most important rule: Never use negation in the variable name.
Avoid names like isNotEnabled, hasNoAccess, or isDisabled.
Why? Because eventually, you will need to check for the opposite state.
if (!isDisabled) forces your brain to calculate a double negative: “If not is disabled…”
Compare that to:
if (isEnabled)
Immediate clarity.
Negative names feel natural when you write them (“I need to check if this is disabled”), but they are a tax on every person who reads the code later. Refactoring tools make this worse; if you invert an if statement, your IDE might helpfully rename isDisabled to isNotDisabled. Now you have code that is actively hostile to human readers.
The Exception:
The only time a negative name is acceptable is when you are mirroring an external API or HTML attribute that is negative by default, like noValidate. Even then, isolate it at the boundary. In your domain logic, always map it to a positive: bool shouldValidate = !request.noValidate.
Context Matters (The “Boolean Trap”)
The rules above work perfectly for properties (state). They fall apart for parameters (function arguments).
This is the “Boolean Trap”:
// Real code from NHibernate
schemaExport.Execute(false, true, false);Quick: What does the second true do? You have no idea. You have to open the library source code to find out.
If you see a method signature like Execute(bool, bool, bool), you are looking at a design bug.
How to fix it:
1. Split the method If the boolean changes the method’s behavior entirely, make two methods.
// Bad
email.Send(message, true); // is true "immediate"? "high priority"?
// Good
email.SendImmediately(message);
email.SendQueued(message);2. Use an Enum If there are modes, name them.
// Bad
file.Write(data, true);
// Good
file.Write(data, WriteMode.Append);3. Use a Config Object If you have multiple flags, pass an object.
// Bad
export.Execute(false, true, false);
// Good
export.Execute(new ExportOptions {
Script = false,
Export = true,
JustDrop = false
});The Antipatterns
If you want to clean up your code, watch out for these specific smells in code reviews.
1. The “Shrug” Variable
Names like flag, done, or check tell you nothing about what is being tracked.
- Bad:
if (check) - Fix:
if (isPaymentVerified)
2. Mismatched Grammar Breaking the prefix rules confuses the mental model.
- Bad:
if (user.hasActive) - Fix:
if (user.isActive)
3. The Double Negative
Any time you see a ! next to a Not or No.
- Bad:
if (!isNotEnabled) - Fix:
if (isEnabled)
4. The Multi-Purpose Boolean A single variable that secretly tracks three different things.
- Bad:
isValid(Actually checks if user exists, has email, AND is active). - Fix: Be explicit.
bool isReadyForBilling = user.Exists && user.HasEmail && user.IsActive;
5. The Drifting Flag A local boolean that gets reused for different logic steps.
- Bad:
bool error = false; if (!Save()) error = true; if (!error && !SendEmail()) error = true; return error; - Fix: Use a Result object or return early. Don’t recycle a boolean bucket.
Conclusion
Naming isn’t about style. It’s about empathy.
When you name a boolean flag, you’re writing for yourself in the moment. When you name it isProcessed, you’re writing for the poor soul who has to fix a bug in this file six months from now. That person is probably you. Be kind to your future self.