Naming Things: The Skill Nobody Teaches
There’s a reason the classic joke about naming being one of the two hard problems in computer science (along with cache invalidation and off-by-one errors) has stayed relevant for decades. It’s not that naming is technically difficult. It’s that it requires something that isn’t taught: precise thinking about what a thing actually is.
A name is a claim. user_data claims that this variable holds data about a user. get_stuff claims that this function retrieves something. Both are technically true of almost anything and therefore useless. A name that could apply to anything communicates nothing.
What Names Are For
Names are the primary interface through which developers read code. Most reading happens without running the code: reviewing PRs, debugging, understanding a system before modifying it. During this reading, names are the main signal about what the code does and why.
A well-named codebase reads like prose. You can understand what a function does without reading its implementation because the name, parameters, and return type tell you. You can understand what a class is responsible for without reading every method.
A poorly named codebase requires reading everything. You have to read the implementation to understand what process() processes, read the callers to understand what data contains, read the comments (if they exist) to understand what manager manages.
The cost of poor naming is not aesthetic. It’s time: every developer who reads the code pays the comprehension cost that a clear name would have eliminated.
The Most Common Mistakes
Names that describe type rather than role. userList, dataMap, infoObject - these tell you what data structure is used, not what the thing represents. The type is usually visible from the declaration or the IDE tooltip. What you cannot see is the semantic meaning. pendingPayments, productsByCategory, validationErrors - these tell you what the thing is, not how it’s stored.
Generic action verbs. process, handle, manage, do - these could describe almost any function. calculateMonthlyInterest, validateShippingAddress, applyPromotionDiscount - these describe exactly one function each. If you cannot give a function a specific name, the function probably does more than one thing.
Boolean names that don’t read as predicates. A boolean should answer a yes/no question. active, status, flag don’t. isActive, hasBeenProcessed, requiresReview do. The is/has/should/can prefix is a signal: this is a condition, not a count or a string.
Abbreviations that save characters but not time. usrMgr, cfg, idx, proc - these require decoding on every read. Names in modern codebases are read far more often than they are typed, and autocomplete removes the typing cost. userManager, configuration, index, processor are unambiguous and readable at speed.
Inconsistency within a codebase. When the same concept is called user in one module, account in another, and member in a third, you cannot search for it, you cannot reason about it uniformly, and you can never be sure whether the three terms mean the same thing or different things. Pick one and use it everywhere. The name matters less than the consistency.
Names That Do Work
Good names tend to share certain properties.
They are as specific as they need to be. A function that sorts a list of users by last login date is not sortUsers. It’s sortByLastLogin. A variable holding the count of failed payment attempts is not count. It’s failedPaymentAttempts. Specificity eliminates ambiguity and makes searches productive.
They match the level of abstraction. High-level functions have high-level names: processOrder, sendNotification, generateReport. Low-level functions have specific names: calculateTaxForState, formatDateAsISO8601, retryWithExponentialBackoff. A function named sendNotification that contains retry logic, template selection, and rate limiting is either doing too much or named too abstractly.
They use the domain vocabulary. If your domain uses “invoice” not “bill,” use “invoice.” If your team calls it an “enrollment” not a “registration,” use “enrollment.” Domain vocabulary makes code immediately readable to anyone who knows the business, and it makes conversations between engineers and domain experts frictionless.
They tell you the why, not just the what. users and eligibleUsers both hold users. The second tells you why this particular subset was selected, which is usually the information that matters when reading the code.
Renaming as Understanding
One of the most effective refactoring tools available is renaming. When you rename a function and find that you cannot choose a good name, that’s information: the function does too many things, or it doesn’t have a clear single responsibility, or you don’t fully understand what it does yet.
A function you cannot name is a function you cannot reason about. The naming difficulty is a diagnostic, not a stylistic annoyance.
The reverse is also true: when you understand a piece of code deeply and give it a precise name, you’ve compressed your understanding into something other developers can access without repeating your investigation. A good name is an act of documentation that ages better than a comment.
A Concrete Example
// Before: what does this do?
function process(d, f) {
const res = [];
for (const item of d) {
if (item.s === f) {
res.push(item);
}
}
return res;
}
// After: tells you everything
function filterOrdersByStatus(orders, targetStatus) {
return orders.filter(order => order.status === targetStatus);
}
The second version is not more correct. It’s more readable, more searchable, and self-documenting. The original requires reading the entire implementation to understand what it does. The refactored version communicates its intent in the signature.
The Practical Standard
A name is good when a developer reading it for the first time can accurately predict what it does or contains without reading its implementation. Test your names against this standard during code review: if a reviewer has to ask “what is X?”, the name failed.
This is not a high bar. It’s a precise bar. And the discipline of meeting it consistently is what distinguishes code that is easy to work with from code that requires an archaeologist.