Naming
Principles for naming variables, functions, types, and modules
Naming
A name is the smallest unit of design. It is also the cheapest to get right at the time of writing and the most expensive to fix later, because every reader pays the cost of a misleading name on every read.
Core Principle
A name should describe what the thing represents, not how it is implemented.
A reader who knows the name should be able to predict what the value holds, what the function returns, or what the type models — without opening the definition.
Variables
Reveal intent
// Bad — describes type, not meaning
const data = users.filter(u => u.active);
const list = orders.map(...);
const flag = true;
// Good — describes meaning
const activeUsers = users.filter(u => u.active);
const enrichedOrders = orders.map(...);
const isCheckoutEnabled = true;Use pronounceable, searchable names
hp, sz, tmplst may save keystrokes but cost every reader a mental lookup. Names should read as English nouns or noun phrases. Single letters are acceptable only for tight loop indices and short-lived locals where the surrounding context makes the meaning obvious (for (let i = 0; ...), nums.map(n => n * 2)).
Booleans read as predicates
A boolean's name should fit naturally inside if (...):
if (isEmpty) // ✓
if (hasNext) // ✓
if (canRetry) // ✓
if (status) // ✗ — status of what?
if (!isNotFound) // ✗ — double negativeAvoid negated names. isVisible reads better than isNotHidden.
Use paired antonyms consistently
When you pick first/last, do not also use start/end elsewhere for the same concept. Conventional pairs:
first / last | inclusive bounds |
begin / end | half-open ranges |
min / max | numeric extremes |
source / target | direction of an operation |
prev / next | sequence neighbors |
Functions
Verb phrases for actions, noun phrases for queries
order.save() // command
user.fullName() // query
client.fetchProfile(id) // command-with-resultFollow command-query separation: a function should either change state or return information, not both. When a function must do both (e.g. pop()), make the dual nature explicit in its name and documentation.
Abstraction-level consistency
The name should describe what is done, not how:
// Bad — leaks implementation
function loopAndSendEmails(users) { ... }
// Good — describes intent
function notifyActiveSubscribers(users) { ... }If you cannot find a name that describes intent without describing mechanics, the function is probably doing too much.
Length scales with scope
A name's length should be proportional to the size of the scope it lives in.
- A loop counter named
iis fine inside a 5-line loop. - A module-level export needs a name that survives autocomplete in an unrelated file.
- A public API symbol must be self-explanatory in code-review diffs and stack traces.
Types and Modules
Use noun phrases
Customer, PaymentProcessor, HttpClient. A type is a thing; its name should be a thing.
Avoid generic suffixes
Manager, Helper, Util, Processor, Handler, Service are warning signs. They typically indicate a class that has accumulated unrelated responsibilities because no one could think of a precise name. When you find yourself reaching for one, ask:
- What does this class actually model?
- Could it be split into several types with concrete names?
OrderManager rarely survives a careful look — it is usually OrderRepository + OrderValidator + OrderPriceCalculator in disguise.
Avoid empty words
Data, Info, Object, Item, Element add no information. CustomerInfo and Customer mean the same thing; pick one.
Consistency Across the Codebase
One concept, one word
Decide once whether the codebase uses fetch, get, load, or retrieve for "read from a remote source," and use it everywhere. Mixing them forces every reader to wonder whether the difference is meaningful.
One word, one concept
The inverse: do not reuse the same verb for unrelated operations. If add() means "append to collection" in one place and "perform addition" in another, readers will pause every time.
Acronym casing
Pick a convention and apply it uniformly: URL vs Url, ID vs Id, HTTP vs Http. Linters can enforce this; ad-hoc choices cannot.
Domain Language
Names should reflect the language of the business domain, not generic CS terminology, when the two diverge:
Invoice,Subscription,Tenant— domain terms users would recognize.DataObject,Record,Row— generic terms that hide the domain.
When the team and the customer use the same words, requirements translate to code with less friction. This is the core of the Ubiquitous Language idea from Domain-Driven Design.
Pre-Commit Checklist
- Does the name describe meaning, not type or implementation?
- Will the name be unambiguous to a reader who has not seen the surrounding code?
- Does each concept have exactly one word in this codebase?
- Do boolean names read naturally inside
if (...)? - Have you avoided
Manager/Helper/Util/Data/Info?