Skip to content
Go back

Stop Naming Your Variables "Flag": The Art of Boolean Prefixes

Shallow Focus Flags

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.

2. HAS: Containment and Features Use has when describing ownership or inclusion. It pairs with a noun.

3. CAN: Capability Use can to check permissions or potential actions.

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.”

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.

PrefixDomainFunctionExample
ISIdentity / StateDescribes what an object is currently.isActive, isEmpty
HASContainmentDescribes ownership or feature presence.hasChildren, hasAccess
CANCapabilityDescribes permission or potential action.canEdit, canRetry
SHOULDIntent / LogicDescribes 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.

2. Mismatched Grammar Breaking the prefix rules confuses the mental model.

3. The Double Negative Any time you see a ! next to a Not or No.

4. The Multi-Purpose Boolean A single variable that secretly tracks three different things.

5. The Drifting Flag A local boolean that gets reused for different logic steps.

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.


Share this post on:

Previous Post
Streams Record Truth. Queues Do Work: A .NET Way To Keep Them Straight
Next Post
From Encrypted Messaging to Secure AI: Cryptography Patterns in .NET 10