Meaningful Names
Status: Complete
Category: Clean Code
Default enforcement: Soft
Author: PushBackLog team
Tags
- Topic: quality, supportability
- Skillset: any
- Technology: generic
- Stage: execution, review
Summary
Names for variables, functions, classes, and modules should clearly reveal their intent. A reader should be able to understand what something is and what it does from its name alone, without reading its implementation.
Rationale
Code is read far more than it is written
A well-cited estimate is that for every line of code written, it is read ten times or more — during review, debugging, refactoring, and onboarding. Naming is the primary mechanism by which intent is communicated across time and between people. A good name is documentation that cannot go stale, because it is the code.
Robert C. Martin puts it bluntly in Clean Code: “The name of a variable, function, or class should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.”
Cognitive load and naming
Poor names force the reader to maintain a mental substitution table while reading: “whenever I see x, I must remember it means cartItemCount.” Every such substitution consumes working memory that should be spent understanding the logic. Well-named code is low-density cognitive labour: the name does the work of explanation.
Naming as design feedback
Difficulty naming a function is a signal about the function itself. If you can’t find a single, clear name, the function is probably doing more than one thing. Awkward names — processAndValidateAndSave, handleThings — are design feedback: split the function until each piece can be named cleanly.
Guidance
Rules for variable names
| Principle | Bad | Good |
|---|---|---|
| Reveal intent | d | daysUntilExpiry |
| Avoid disinformation | accountsList (if it’s a Map) | accountsById |
| Avoid encodings | strName, iCount | name, count |
| Distinguish meaningfully | data1, data2 | sourceUser, targetUser |
| Pronounceable | genymdhms | generatedTimestamp |
Rules for function names
- Verbs for actions:
createUser,calculateDiscount,sendNotification - Questions return booleans:
isExpired(),hasPermission(),canCheckout() - Say what, not how:
getActiveUsers()notqueryDatabaseForUsersWhereActiveTrue() - Avoid context repetition: if the class is
UserService,UserService.getUser()is redundant —UserService.findById()is cleaner
Rules for class and module names
- Nouns for entities:
Order,PaymentGateway,UserProfile - Avoid
Manager,Processor,Handler,Helper,Util— these names signal missing design; what specifically does it manage? - One concept per name: if a class needs “and” in its description (“validates and sends emails”), split it
Length guidelines
Name length should be proportional to scope. A loop variable i is acceptable in a three-line loop where i is never re-used. A class-level field holding a business concept should be descriptive. Variables in tight algorithms can be shorter; variables in business logic should be explicit.
Examples
Before: cryptic
function proc(d: any[], f: boolean): any[] {
return d.filter(x => f ? x.a : !x.a);
}
What is d? What is f? What is a? What does proc do?
After: self-documenting
function filterOrdersByActiveStatus(
orders: Order[],
includeActive: boolean
): Order[] {
return orders.filter(order =>
includeActive ? order.isActive : !order.isActive
);
}
The same logic, but the name, parameter names, and type reveal intent entirely without a comment.
Boolean flag trap
// What does true mean? Only the implementation can tell you.
render(user, true, false, true);
// Better: use an options object or named constants
render(user, { showAvatar: true, isAdmin: false, canEdit: true });
Anti-patterns
1. Single-character names outside tight loops
u, d, t for user, data, and token are acceptable for 2-line functions but deadly in multi-page methods where they make reading impossible.
2. Abbreviations that save keystrokes, cost comprehension
usrMgr, acctLst, genDt — saves nothing; forces constant mental decryption.
3. Implementation names rather than intent names
sendHttpPostToEmailEndpoint describes how; sendWelcomeEmail describes what. The what name is always better.
4. Container filler words
theUser, aList, tempData, myString — the prefix adds nothing. user, items, payload.
5. Lying names
A variable named userList that is actually a Map, or a function named isValid that also fixes invalid data. Names that misrepresent are worse than cryptic names: they actively mislead.
6. Context-free names in a broader scope
id, name, value, data as class fields or module-level exports. Outside a tight local scope, these names carry no information about what they represent.
Related practices
Part of the PushBackLog Best Practices Library. Suggest improvements →