The Art of Naming Things
Naming is the hardest problem in computer science. Learn practical strategies for naming variables, functions, and classes that make your code self-documenting.
Phil Karlton famously said there are only two hard things in computer science: cache invalidation and naming things. He wasn’t joking. A bad name forces every future reader to reverse-engineer what the code does. A good name makes the code read like prose.
Let’s look at concrete strategies for naming variables, functions, and classes — with real before-and-after examples in TypeScript and Python.
Why Naming Matters
Code is read far more often than it is written. When you name something d, tmp, or data, you’re offloading mental work onto every person who reads that code — including future you. Good names eliminate the need for comments and reduce bugs caused by misunderstanding.
Variables: Say What It Is, Not What It Looks Like
Bad Names
// What is d? Days? Distance? A date?
const d = new Date();
const n = users.length;
const tmp = calculateTotal(items);
const data = await fetch('/api/orders');
const flag = user.age >= 18;
Good Names
const currentDate = new Date();
const userCount = users.length;
const orderTotal = calculateTotal(items);
const pendingOrders = await fetch('/api/orders');
const isEligibleToVote = user.age >= 18;
Notice the pattern: good variable names answer “what does this represent?” without needing context.
Booleans Deserve Special Attention
Boolean names should read like yes/no questions:
// Bad — is this a noun? A verb? What does "true" mean?
const login = true;
const admin = false;
const visible = true;
// Good — reads naturally in an if-statement
const isLoggedIn = true;
const hasAdminAccess = false;
const isModalVisible = true;
// These read beautifully in conditionals:
if (isLoggedIn && hasAdminAccess) {
showDashboard();
}
In Python, the convention is slightly different but the principle is the same:
# Good Python boolean naming
is_authenticated = True
has_permission = False
can_edit = user.role in ("admin", "editor")
if is_authenticated and has_permission:
render_dashboard()
Functions: Say What It Does, Not How It Does It
Functions are actions. Name them with verbs. The name should tell you what happens when you call them — not the implementation details.
Bad Function Names
function process(data: unknown[]): void { /* ... */ }
function handle(event: Event): void { /* ... */ }
function doStuff(): void { /* ... */ }
function userData(id: string): Promise<User> { /* ... */ }
What does process do? What does handle handle? These names tell you nothing.
Good Function Names
function validateEmailAddress(email: string): boolean { /* ... */ }
function sendWelcomeEmail(user: User): Promise<void> { /* ... */ }
function calculateShippingCost(order: Order): number { /* ... */ }
function fetchUserById(id: string): Promise<User> { /* ... */ }
Each name is a mini-specification. You know the input, the action, and roughly the output.
The Scope-Length Rule
Short names are fine in short scopes. Long names are necessary in long scopes:
// Fine — `u` lives for one line in a lambda
const activeUsers = users.filter(u => u.isActive);
// Fine — `i` in a tiny loop is universally understood
for (let i = 0; i < 10; i++) {
console.log(i);
}
// NOT fine — `u` used across 50 lines of business logic
const u = await fetchUser(id); // What's 'u' 30 lines from now?
// ... 30 lines later ...
await sendNotification(u); // Is this the user or something else?
Classes: Say What It Is, Not What It’s Made Of
Class names should be nouns. They represent things, not actions.
// Bad — too vague, too generic
class Manager { }
class Processor { }
class Helper { }
class Utils { }
class DataService { }
// Good — tells you exactly what this thing represents
class InvoiceGenerator { }
class MarkdownParser { }
class EmailNotificationSender { }
class ShoppingCart { }
class PasswordHasher { }
Avoid the “Manager” and “Handler” trap. If your class is called UserManager, it probably does too many things. Break it apart:
// Instead of one god-class UserManager:
class UserAuthenticator {
async authenticate(email: string, password: string): Promise<AuthResult> { /* ... */ }
}
class UserProfileUpdater {
async updateProfile(userId: string, changes: ProfileChanges): Promise<void> { /* ... */ }
}
class UserNotifier {
async sendWelcomeEmail(user: User): Promise<void> { /* ... */ }
}
Python-Specific Conventions
Python has its own naming culture. Respect it:
# Classes: PascalCase
class HttpRequestHandler:
pass
# Functions and variables: snake_case
def calculate_monthly_revenue(transactions: list[Transaction]) -> Decimal:
total = sum(t.amount for t in transactions if t.is_completed)
return total
# Constants: SCREAMING_SNAKE_CASE
MAX_RETRY_ATTEMPTS = 3
DEFAULT_TIMEOUT_SECONDS = 30
API_BASE_URL = "https://api.example.com"
# Private members: leading underscore
class PaymentProcessor:
def __init__(self):
self._transaction_log: list[Transaction] = []
def _validate_card(self, card: CreditCard) -> bool:
"""Internal validation — not part of the public API."""
return card.is_valid and not card.is_expired
Naming Anti-Patterns
1. Hungarian Notation (Don’t Do This)
// The type system already knows the type
const strName: string = "Alice"; // Just use: name
const arrItems: Item[] = []; // Just use: items
const boolIsActive: boolean = true; // Just use: isActive
2. Abbreviations That Only You Understand
// Bad — what does 'calc' calculate? What's 'mgr'?
const calcVal = getMgr().proc(usrInp);
// Good
const discountedPrice = getPricingManager().applyDiscount(userSelection);
3. Names That Lie
This is the worst sin. A name that says one thing but does another:
// This function also sends an email and logs an audit event.
// The name is a lie.
function saveUser(user: User): Promise<void> {
await db.users.save(user);
await emailService.sendWelcomeEmail(user);
await auditLog.record('user_created', user.id);
}
// Honest names:
function createUserAndNotify(user: User): Promise<void> { /* ... */ }
// Or better yet, split it:
function saveUser(user: User): Promise<void> { /* ... */ }
function onUserCreated(user: User): Promise<void> { /* ... */ }
A Practical Rename Checklist
When you’re stuck naming something, ask these questions:
- What does it represent? (Not how it works — what it is)
- Would a new team member understand it without context?
- Does it read naturally in an
ifstatement or function call? - Is it the same length as its scope demands? (Short scope → short name is OK)
- Does the name match what the code actually does? (No lying names)
The Payoff
Investing 30 extra seconds in a name saves hours of confusion downstream. When your code reads like this:
const eligibleCustomers = allCustomers.filter(c => c.isActive && c.hasVerifiedEmail);
const discountedInvoices = eligibleCustomers.map(c => generateInvoice(c, applyLoyaltyDiscount));
await Promise.all(discountedInvoices.map(invoice => emailService.send(invoice)));
Nobody needs a comment. Nobody needs to ask “what does this do?” The names tell the story. That’s the art of naming things.