Writing Maintainable Code — How to Master Naming in Programming?
September 13, 2025
What happens when a simple naming choice costs your team six months of debugging? Stanford Computer Science Professor John Ousterhout experienced this firsthand during development of a distributed operating system. In his classic book "A Philosophy of Software Design," he shares a painful lesson that transformed how he thinks about variable names.
The bug was mysterious. File data would randomly disappear, with entire blocks of information suddenly becoming zero without any user interaction. How could this happen? What made this particularly frustrating was that the cause wasn't in the complex distributed algorithms or memory management—it was in something as basic as variable naming.
The culprit was a variable called block
. But here's the problem: this single name referred to two completely different concepts. Sometimes block
meant a physical disk block number, other times it meant a logical file block number. When these two uses got mixed up, the wrong memory locations were overwritten, causing data to vanish.
How might we solve this naming confusion? What would you rename these variables to make their purpose crystal clear?
Ousterhout's solution was elegantly simple: fileBlock
and diskBlock
. With this change, the distinction became impossible to miss. Code handling file logic used fileBlock
, while disk operations used diskBlock
. No more confusion, no more six-month debugging sessions.
This story illustrates why naming deserves our careful attention. Poor naming doesn't just make code harder to read—it creates expensive bugs that can take months to track down. Let's explore the principles that can help us avoid such costly mistakes.
The Core Principle: Precision Above All
What makes a name truly effective? The answer lies in precision. A precise name eliminates ambiguity and communicates intent at first glance.
Here's a useful test: if someone reads your variable or function name without seeing any documentation, implementation details, or usage context, can they accurately predict what it does? If the answer is no, your name probably lacks precision.
Imprecise names often try to cover too much ground. Consider the block
example—its meaning was too broad, leaving room for multiple interpretations. When names have interpretive wiggle room, they invite misuse.
Let's examine some common imprecise names and how to improve them. Many developers use generic terms like result
for return values or count
for numeric calculations. While these seem reasonable, they're often too vague to be helpful.
Instead of result
, ask yourself: "Result of what operation?" Instead of count
, consider: "Count of what items?" Incorporating these specifics into your names—like sortedUserList
or activeConnectionCount
—makes them far more precise.
Revealing Data Types Through Names
Different types of variables benefit from naming patterns that reveal their nature immediately. Consider these problematic examples:
const error = true;
const status = false;
These names don't clearly indicate they're boolean values. Someone reading this code might assume error
represents an error object or that status
is a string enumeration like "pending" | "completed" | "failed"
.
How can we make the boolean nature obvious? Prefixes like is
or has
work wonderfully:
const hasRegistrationError = true;
const isAccountActive = false;
Now the boolean nature is unmistakable, and other developers can understand the code without examining the implementation.
Precision in Function Names
Functions require the same precision as variables. The common advice to use "verb + noun" patterns is helpful but insufficient. Consider this function:
function handleUsers(users: User[]): User[] {
return users.filter((user) => user.isActive);
}
What does "handle" actually mean here? The name gives us no clue about the specific operation being performed. After examining the implementation, we see it's filtering for active users. A more precise name would be filterActiveUsers
.
Rather than following naming formulas mechanically, focus on capturing the function's essential purpose. Does your function name accurately reflect what the function accomplishes?
Scope Determines Naming Detail
How precise should our names be? Should we always avoid short names like i
and j
in loops?
The answer depends on scope and impact radius. The further apart a variable's declaration and usage points are, the more descriptive its name should be. Variables with wide influence across a system demand maximum precision, while those with narrow, local scope can be more concise.
For a loop index used only within a single for
statement, i
is perfectly acceptable:
for (let i = 0; i < array.length; i++) {
// i is only used here
}
But for the block
variable in Ousterhout's example, which was used throughout the operating system, maximum precision was essential.
When Naming Feels Impossible
What happens when you struggle to find a precise name for a function or variable? This difficulty often signals a deeper design problem.
If you can't easily name something, it might be trying to do too much. Functions that handle multiple unrelated responsibilities resist simple naming because no single name can capture their diverse purposes.
As we discussed in our article on high cohesion, well-designed functions have clear boundaries and focused responsibilities. When functions do one thing well, naming them becomes straightforward.
Ousterhout puts it perfectly: "If it's hard to find a simple name for a variable or method that creates a clear image of the underlying object, that's a hint that the underlying object may not have a clean design."
This observation points to an important connection between naming and system design. Good names don't just describe existing code—they can guide us toward better architectural decisions.
Building Better Systems Through Better Names
The relationship between naming and design quality runs deeper than mere documentation. When we struggle to name something precisely, we're receiving valuable feedback about our system's structure.
Consider what happens when you try to name a function that handles user authentication, sends email notifications, and updates database records. The struggle to find an appropriate name reveals that this function violates the single responsibility principle.
This is why experienced developers pay close attention to naming challenges. They understand that difficult naming often indicates opportunities for refactoring and improvement.
Support ExplainThis
If you found this content helpful, please consider supporting our work with a one-time donation of whatever amount feels right to you through this Buy Me a Coffee page, or share the article with your friends to help us reach more readers.
Creating in-depth technical content takes significant time. Your support helps us continue producing high-quality educational content accessible to everyone.