Advertisement

The old joke says there are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. The naming entry earns its place. Names are the user interface of code — the layer through which every future reader, including you in six months, perceives what the program does. A well-named codebase can be skimmed; a badly-named one must be decrypted.

Naming is hard because it is compression: squeezing a concept, its type, its units, its lifecycle and its intent into a few words. But compression has rules, and they can be learned.

Name the meaning, not the mechanics

The weakest names describe what a thing is made of; the strongest describe what it means. data, info, temp, obj, handler2 are mechanical names — true of almost anything, informative about almost nothing. unpaidInvoices, retryDelayMs, isEmailVerified each carry a claim about the world that the reader can check against the code. When a name states a meaning, a mismatch between name and behaviour becomes visible — which is exactly how good names catch bugs during review.

Two mechanical habits deliver most of this: put units in names that have them (timeoutMs, priceUsd, distanceKm — entire spacecraft have been lost to implied units), and make booleans read as assertions (isActive, hasStock, canRetry) so conditionals read as sentences: if the order can retry, retry it.

Advertisement

Scope decides how long a name should be

The classic fights — i versus index, short versus descriptive — dissolve under one principle: a name's length should grow with its scope. A loop counter alive for three lines can be i; everyone knows it, and a longer name would add noise, not clarity. A module-level constant read from fifty places deserves a full sentence-grade name like MAX_CONCURRENT_UPLOADS_PER_USER. The crime is inversion: cryptic abbreviations with global reach, or ceremonious names for two-line locals.

The same principle governs functions. A private helper called three lines below its definition can be terse; a public API name is a promise made to strangers and should say exactly what it does — including its surprises. If a function is honestly named fetchUserAndUpdateLastSeen, the name itself is telling you it does two things, and that the design, not the name, needs work. Names that resist naming are design feedback.

Consistency beats brilliance

A merely decent naming scheme applied everywhere outperforms scattered brilliance. If deleted things are removed in one module, destroyed in another and archived in a third, every reader pays a lookup tax forever. Pick one verb per concept — get for cheap reads, fetch for remote calls, build for construction, whatever your team likes — and enforce it in review as seriously as tests. The vocabulary of a codebase is an API, and synonyms are bugs in it.

And when you find a lie — a name that no longer matches behaviour after refactors — fix it immediately, whatever the diff noise. A wrong name is worse than a vague one: readers trust it and inherit a false belief. Renaming is the cheapest documentation update in existence, and modern editors make it a keystroke. The codebase reads the way it is named; name it like you'll be the one reading.