Loading learning content...
Naming is simultaneously the most mundane and most profound aspect of programming. We name things constantly—variables, functions, classes, files, packages—yet few developers invest deliberate effort in this skill. The result is codebases filled with cryptic abbreviations, misleading labels, and inconsistent terminology that obscure intent and slow comprehension.
"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
This quip resonates because it captures a truth: good naming is genuinely difficult. It requires understanding not just what code does, but what concept it represents, how it relates to other concepts, and how future readers will interpret it. It demands precision of thought and empathy for readers.
But here's the opportunity: naming is a skill that compounds. Every well-named element makes the next one easier to name correctly. A codebase with consistently good names becomes self-documenting—developers can navigate and understand it with minimal external explanation.
By the end of this page, you will understand the principles behind good naming, master naming conventions for different code elements (variables, functions, classes, packages), recognize and fix common naming anti-patterns, and develop the habit of intentional naming that makes code self-documenting.
Names are the primary interface between human understanding and code. They serve multiple critical functions:
calculateCompoundInterest abstracts the underlying math into a comprehensible concept.User should find user-related code; searching for validate should find validation logic.flag1 tells you nothing; isUserAuthenticated tells you everything.❌ Poor naming obscures intent:
function proc(d: any[], f: number): number {
let t = 0;
for (let i = 0; i < d.length; i++) {
if (d[i].s === 'A') {
t += d[i].v * f;
}
}
return t;
}
What does this do? You'd need to trace through the logic carefully to understand.
✅ Good naming reveals intent:
function calculateActiveOrdersTotal(
orders: Order[],
taxRate: number
): number {
let totalValue = 0;
for (const order of orders) {
if (order.status === 'ACTIVE') {
totalValue += order.value * taxRate;
}
}
return totalValue;
}
Instantly clear: sum values of active orders with tax applied.
Bad names impose a tax on every interaction with the code. Developers must repeatedly decipher what things mean, increasing time-to-understanding, raising bug risk, and creating mental fatigue. In large codebases, poor naming is a significant contributor to reduced velocity.
Before diving into specific conventions, let's establish principles that apply universally across all naming contexts:
genymdhms is unpronounceable; generationTimestamp is natural.userId in one place, don't call it user_id, uid, or userIdentifier elsewhere.i); global constants should be descriptive (MAX_CONNECTION_RETRY_ATTEMPTS).BillingDocument.The specificity gradient:
Name specificity should increase with scope:
// Loop variable - minimal scope, short name is fine
for (let i = 0; i < items.length; i++)
// Local variable - function scope, more descriptive
const activeUserCount = users.filter(u => u.isActive).length;
// Class field - class scope, specific and clear
private orderProcessingQueue: Queue<Order>;
// Global/constant - application scope, highly specific
export const DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS = 30000;
The abstraction level principle:
Names should match the abstraction level of their context:
// High-level business logic - use domain language
order.submit();
payment.authorize();
customer.verify();
// Low-level implementation - use technical language
buffer.allocate(1024);
connection.setTcpNoDelay(true);
index.rebalance();
When reviewing a name, imagine explaining it to a colleague. If you say 'this variable holds the... um... let me check the code', the name needs work. If you can naturally say 'this holds the number of active users', the name is communicating effectively.
Variables are the most numerous named elements in code. Getting variable naming right has outsized impact on readability.
| Pattern | When to Use | Examples |
|---|---|---|
| Noun or noun phrase | Most variables representing data | user, orderTotal, connectionPool |
| Boolean as question | Boolean variables | isActive, hasPermission, canEdit |
| Plural for collections | Arrays, lists, sets | users, orderItems, availablePorts |
| Count suffix | Numeric counts | userCount, errorCount, retryAttempts |
| Index suffix | Array indices | currentIndex, startIndex, lastProcessedIndex |
| Prefix for state | State indicators | previousState, nextValue, originalData |
12345678910111213141516171819202122232425
// ❌ Poor variable namesconst d = new Date();const arr = users.filter(u => u.a);const flag = order.total > 100;const temp = calculateDiscount(price);const data = fetchUserData(userId); // ✅ Intention-revealing variable namesconst orderCreatedDate = new Date();const activeUsers = users.filter(user => user.isActive);const qualifiesForDiscount = order.total > 100;const discountAmount = calculateDiscount(price);const userProfile = fetchUserData(userId); // ❌ Inconsistent boolean namingconst active = true; // Is it active now? Should be active?const disabled = false; // Negatives are confusingconst status = true; // Status of what? // ✅ Boolean as yes/no questionsconst isActive = true; // Clear: currently activeconst isEnabled = true; // Clear: avoiding double negativeconst hasValidSubscription = true; // Clear: possession checkconst canAccessAdminPanel = true; // Clear: capability checkconst shouldRetryConnection = true; // Clear: decision indicatorx, d, s mean nothing. Only i, j, k for simple loops are acceptable.data, info, object, item, thing provide no information. What kind of data? Information about what?userList, nameString, priceInt encode type information that the type system already provides.notFound, isNotValid, hasNoAccess force mental negation. Use positive forms.user1, user2, temp1, temp2 indicate you need an array or better naming.usr, cust, ord, qty save minimal characters at high comprehension cost.Short names are acceptable in limited contexts: loop indices (i, j), lambda parameters when context is clear (items.map(x => x.id)), well-known abbreviations in your domain (url, id, html), and mathematical formulas where notation is standard (x, y, dx, dy).
Functions and methods are verbs—they describe actions. Their names should clearly communicate what action is performed and, when relevant, on what or with what effect.
| Intent | Pattern | Examples |
|---|---|---|
| Retrieve data | get + noun | getUser(), getOrderTotal(), getCurrentTimestamp() |
| Modify data | set + noun | setPassword(), setConfiguration(), setActiveStatus() |
| Check condition | is/has/can + condition | isValid(), hasPermission(), canProcess() |
| Perform transformation | calculate/compute/derive + result | calculateTotal(), computeHash(), deriveKey() |
| Trigger action | verb + object | sendEmail(), processOrder(), validateInput() |
| Create new object | create/build/make + noun | createUser(), buildQuery(), makeConnection() |
| Search/find | find/search + criteria | findById(), searchByName(), findMatchingOrders() |
| Convert type | to + target type | toString(), toJSON(), toDTO() |
| Parse from format | parse/from + source | parseDate(), fromJSON(), fromString() |
1234567891011121314151617181920212223242526272829303132333435
// ❌ Vague or misleading function namesfunction process(order: Order): void { } // Process how?function handle(data: any): any { } // Handle what?function doStuff(): void { } // No information at allfunction orderFunc(o: Order): number { } // Type in name, unclear purpose // ✅ Clear, intention-revealing function namesfunction submitOrderForFulfillment(order: Order): void { }function parseJsonConfigFile(data: string): Config { }function calculateOrderTotalWithTax(order: Order): number { } // ❌ Get/set misusefunction getUser(name: string): void { // 'get' but returns void - actually creates this.user = new User(name);} // ✅ Accurate verb usagefunction createAndStoreUser(name: string): void { this.user = new User(name);} // ❌ Inconsistent boolean method namingfunction checkValid(): boolean { } // 'check' is vaguefunction valid(): boolean { } // Adjective, not verb phrasefunction validateOrder(): boolean { } // validate usually implies throwing // ✅ Boolean methods as questionsfunction isValid(): boolean { }function hasValidationErrors(): boolean { }function canBeProcessed(): boolean { } // Side effect clarityfunction ensureDirectoryExists(path: string): void { } // May create if missingfunction validateOrThrow(input: Input): void { } // Throws on invalidfunction findOrCreate(id: string): Entity { } // May create newMaintain symmetry in related operations: open/close, start/stop, begin/end, add/remove, create/destroy, insert/delete, increment/decrement, show/hide, enable/disable. Asymmetric pairs (open/end, start/finish) create cognitive friction.
Classes represent concepts; interfaces define contracts. Their names should communicate the essence of what they model or the behavior they specify.
| Pattern | Used For | Examples |
|---|---|---|
| Domain noun | Domain entities | Order, Customer, Product, Invoice |
| Role suffix | Service classes | OrderService, PaymentProcessor, EmailSender |
| Pattern suffix | Pattern implementations | OrderFactory, UserRepository, PricingStrategy |
| Action + er/or | Single-purpose classes | Validator, Parser, Serializer, Builder |
| Capability adjective | Interfaces | Comparable, Serializable, Observable |
| I-prefix (C#/Java) | Interfaces in some languages | IOrderRepository, IPaymentGateway |
12345678910111213141516171819202122232425262728293031323334353637383940414243
// ❌ Poor class namesclass Manager { } // Manager of what?class Helper { } // Vague, god-class riskclass Utility { } // Bag of unrelated functionsclass Data { } // What kind of data?class Info { } // Information about what? // ✅ Specific, meaningful class namesclass OrderFulfillmentManager { }class PasswordHashingService { }class ShippingCostCalculator { }class CustomerAccountData { }class ProductCatalogInfo { } // Interface naming approaches // Approach 1: Capability-based (preferred in TypeScript)interface Serializable { serialize(): string;}interface Observable<T> { subscribe(observer: Observer<T>): Subscription;} // Approach 2: Role-basedinterface OrderRepository { findById(id: string): Promise<Order | null>; save(order: Order): Promise<void>;}interface PaymentGateway { authorize(payment: Payment): Promise<AuthResult>; capture(authorizationId: string): Promise<CaptureResult>;} // Approach 3: Contract suffix (less common, but explicit)interface UserServiceContract { getUser(id: string): Promise<User>;} // Implementation namingclass PostgresOrderRepository implements OrderRepository { }class StripePaymentGateway implements PaymentGateway { }class InMemoryOrderRepository implements OrderRepository { } // Test doubleManager, Handler, Processor are often signs of unclear responsibility. Prefer specific roles: OrderSubmissionProcessor over OrderProcessor.Invoice, not Bill or PaymentRequest.PostgresUserRepository, CachedUserRepository).com.company.orders package, the class Order doesn't need to be OrderOrder or OrderEntity.Base or Abstract prefix for abstract base classes: AbstractPaymentProcessor, BaseController.Classes ending in -er or -or (Manager, Handler, Controller, Processor) often indicate procedural thinking disguised as OOP. They tend to become god classes. Ask: 'What object should own this behavior?' A UserValidator might be better as a validate() method on User or RegistrationRequest.
Constants represent fixed values; enums represent fixed sets of related values. Their naming conventions help distinguish them from mutable variables.
123456789101112131415161718192021222324252627282930313233343536373839404142434445
// Constants: SCREAMING_SNAKE_CASE is traditionalconst MAX_RETRY_ATTEMPTS = 3;const DEFAULT_TIMEOUT_MS = 5000;const HTTP_STATUS_OK = 200;const API_BASE_URL = 'https://api.example.com'; // Alternative: PascalCase or camelCase for object constantsconst DatabaseConfig = { host: 'localhost', port: 5432, maxConnections: 10,} as const; // Enums: PascalCase for enum name, UPPER_CASE or PascalCase for valuesenum OrderStatus { PENDING = 'PENDING', CONFIRMED = 'CONFIRMED', SHIPPED = 'SHIPPED', DELIVERED = 'DELIVERED', CANCELLED = 'CANCELLED',} // Alternative enum style (PascalCase values)enum PaymentMethod { CreditCard = 'credit_card', DebitCard = 'debit_card', BankTransfer = 'bank_transfer', DigitalWallet = 'digital_wallet',} // Union types as alternative to enumstype Priority = 'low' | 'medium' | 'high' | 'urgent';type Direction = 'north' | 'south' | 'east' | 'west'; // ❌ Poor constant namingconst MAX = 100; // Max what?const TIMEOUT = 5000; // In what units?const ERROR = -1; // Which error?const FLAG = true; // Meaningless // ✅ Descriptive constant naming const MAX_ITEMS_PER_PAGE = 100;const CONNECTION_TIMEOUT_MILLISECONDS = 5000;const ERROR_CODE_INVALID_INPUT = -1;const FEATURE_FLAG_DARK_MODE_ENABLED = true;Some languages have specific conventions: Java and Python use SCREAMING_SNAKE_CASE for constants. C# and TypeScript ecosystems increasingly use PascalCase for const objects. Follow your language community's conventions for consistency with the broader ecosystem.
Package and file names form the top level of your codebase's information architecture. They should communicate the organization and contents of your code at a glance.
| Ecosystem | Convention | Examples |
|---|---|---|
| TypeScript/JS | kebab-case or PascalCase | order-service.ts, OrderService.ts |
| Java | PascalCase (matches class) | OrderService.java |
| Python | snake_case | order_service.py |
| C# | PascalCase (matches class) | OrderService.cs |
| Go | lowercase | orderservice.go |
1234567891011121314151617181920212223242526272829
# Package/directory naming patterns # ❌ Poor package namessrc/├── stuff/ # Meaningless├── helpers/ # Vague grab-bag├── misc/ # Undefined scope└── code/ # Redundant # ✅ Descriptive package namessrc/├── orders/ # Order domain (noun = domain)├── authentication/ # Auth capability (noun = capability)├── payments/ # Payment processing└── notifications/ # Notification services # File naming within packages (TypeScript example)src/orders/├── Order.ts # Domain entity├── OrderService.ts # Business logic├── OrderRepository.ts # Data access├── OrderController.ts # HTTP handlers├── order-types.ts # Type definitions (kebab-case for non-class files)├── order.constants.ts # Constants├── order.errors.ts # Custom errors├── __tests__/│ ├── OrderService.test.ts│ └── OrderRepository.test.ts└── index.ts # Public API exportsorders, inventory, billing) are immediately understandable.services/, repositories/, handlers/ tell you nothing about what the system does.user-authentication/ is better than auth/ which is better than security/.Consistent casing is crucial for readability and searchability. Different elements use different casing conventions based on language and tradition.
| Style | Pattern | Example | Common Uses |
|---|---|---|---|
| camelCase | firstWordLower | orderTotal, userId | Variables, functions, methods |
| PascalCase | AllWordsCapitalized | OrderService, UserAccount | Classes, interfaces, types, enums |
| SCREAMING_SNAKE_CASE | ALL_CAPS_UNDERSCORES | MAX_RETRIES, API_KEY | Constants, environment variables |
| snake_case | all_lowercase_underscores | user_id, order_total | Python variables, database columns |
| kebab-case | words-with-dashes | order-service, user-profile | URLs, file names, CSS classes |
| Element | TypeScript/JS | Java | Python | C# |
|---|---|---|---|---|
| Variables | camelCase | camelCase | snake_case | camelCase |
| Functions | camelCase | camelCase | snake_case | PascalCase |
| Classes | PascalCase | PascalCase | PascalCase | PascalCase |
| Interfaces | PascalCase | PascalCase (I prefix) | PascalCase | IPascalCase |
| Constants | SCREAMING or PascalCase | SCREAMING_SNAKE | SCREAMING_SNAKE | PascalCase |
| Files | kebab or PascalCase | PascalCase.java | snake_case.py | PascalCase.cs |
When joining an existing project, follow the established conventions even if you prefer different ones. Consistency within a codebase matters more than adherence to any particular style. Use linters and formatters (ESLint, Prettier, Checkstyle) to enforce conventions automatically.
Domain-Driven Design emphasizes ubiquitous language—a shared vocabulary used by developers, domain experts, and in the code itself. This alignment eliminates translation friction between business concepts and technical implementation.
❌ Developer-invented terminology:
class DataWrapper {
items: LineItemDTO[];
}
class TransactionProcessor {
processTransaction(
rec: Record
): ResultObject { }
}
function handleUserAction(
payload: ActionPayload
): void { }
Developers invented DataWrapper, TransactionProcessor, handleUserAction. Business users don't recognize these terms.
✅ Domain-aligned terminology:
class ShoppingCart {
lineItems: CartItem[];
}
class OrderSubmissionService {
submitOrder(
cart: ShoppingCart
): OrderConfirmation { }
}
function addItemToCart(
cart: ShoppingCart,
product: Product
): void { }
ShoppingCart, submitOrder, OrderConfirmation are terms the business uses. Code becomes a model of the business.
Handler, Manager, Processor, ask: what would the business call this? OrderFulfillmentService is better than OrderProcessor.Customer in sales vs. support may have different attributes.When developers and business users use different terms for the same concept, every conversation requires mental translation. This slows communication, introduces errors, and creates drift between requirements and implementation. Ubiquitous language eliminates this overhead by ensuring everyone—including the code—speaks the same language.
We've explored naming as a fundamental skill. Let's consolidate the key principles:
isEligibleForDiscount not checkFlagAndCompare.Naming checklist for code review:
What's next:
With naming principles established, we now turn to code layout best practices—how to structure the internal content of files for maximum readability, including ordering, spacing, and visual organization.
You now understand naming as a core development skill. You can apply consistent naming conventions across variables, functions, classes, and packages. You know how to use domain-driven naming to align code with business language. Next, we'll explore how to layout code for optimal readability.