Tech & AI

Naming Things Is Hard: A Developer's Guide to Good Names

A practical guide for developers on naming variables, functions, booleans, services and branches so code stays readable, reviewable and easy to change.

In this article

“There are only two hard things in Computer Science: cache invalidation and naming things.”

The line is widely attributed to the programmer Phil Karlton, and it has survived decades of retelling because every developer laughs, then goes quiet, then remembers a variable called data2.

Naming is hard because it forces you to decide what something actually is, and usually you haven’t decided yet. This guide is about names that survive: a second reader, a six-month gap, a new hire, and a reviewer at 4pm on a Friday.

Names are documentation that never goes stale

Comments drift. Someone changes the function, forgets the comment, and now it’s a small, confident lie sitting on top of working code.

Names can lie too, but they get caught faster. A function name is read at every call site, in every stack trace and review. If calculateTax starts sending emails, someone notices. If you need a comment to explain what a variable holds, that comment is usually a better name in disguise.

Reveal intent, not just contents

A good name answers the question the reader is about to ask. Not “what type is this?” but “why does this exist, and what do I do with it?”

// Before
const d = 86400;
const lst = get(u);

// After
const SECONDS_PER_DAY = 86400;
const openInvoices = fetchOpenInvoices(customer);

The first is shorter and tells you nothing. The second reads like a sentence.

Two cheap upgrades pay off constantly. Put units in names: timeoutMs, priceCents, maxUploadMb, because plenty of bugs are one person’s seconds meeting another person’s milliseconds. And name collections for what they hold, in the plural: activeUsers, not arr or, worst of all, stuff.

Abbreviations cost more than they save

An abbreviation saves a few keystrokes once and charges every future reader a decoding fee, forever. Your editor autocompletes. Nobody is paying you by the character.

It also breeds inconsistency. One file says cust, another cstmr, a third customer, and a search for all three still misses someone’s cusRec. Full words are searchable. Abbreviations are a scavenger hunt.

The test: would a new hire decode it on day one? id, url and i in a short loop pass. mgr and usrNmStr do not. Names should also survive being said in a standup, the same say-it-aloud test we use for choosing a name people remember. Cryptic, letter-swapped handles belong in gamertags, not in a billing module.

Booleans should read like yes-or-no questions

A boolean is an answer, so name it like the question. Prefix it with is, has, can or should, and phrase it positively.

// Before
if (!user.disabled && flag) { ... }

// After
if (user.isActive && hasVerifiedEmail) { ... }

flag says a decision is being made and nothing about what. disabled forces a negation, and negations compound: nobody has ever read if (!isNotHidden) correctly on the first try. Boolean parameters are worse. sendReport(true, false) is a riddle; named options turn it back into English.

Verbs for functions, nouns for data

Functions do things, so they get verbs: sendReceipt, parseHeader. Data is things, so it gets nouns: receipt, header. A function named userData() could fetch, build, validate or delete, and the reader has to open it to find out.

Verbs also promise things about cost and side effects.

Verb What the reader will assume
get Cheap, no side effects, returns something that already exists
fetch / load Touches the network or disk, may be slow, may fail
compute Real work, possibly worth caching
create / build Returns something new
ensure Safe to call twice: makes it true if it isn’t already

A getInvoices() that quietly makes three network calls is not a naming nit. It’s a performance bug waiting for a loop.

Beware of Manager, Helper, Util and Data

Some words are placeholders for a decision you haven’t made. Manager, Helper, Util, Processor and Data all say the same thing: code lives here.

Nothing is off-topic for a UserManager, so it grows into a junk drawer that saves users, hashes passwords, sends emails and, one day, formats dates for some reason.

// Before
class UserManager { /* saves, hashes, emails, formats... */ }

// After
class UserRepository {}
class PasswordHasher {}
class WelcomeEmailSender {}

If you can’t name a class without one of those suffixes, that’s the design talking: it does several jobs, and each wants its own name. The same goes for utils.py. Nobody has ever found a function on purpose in a file called misc.

Consistency beats cleverness

One word per concept, everywhere. If the codebase says fetch, don’t introduce retrieve. If the business says “customer”, don’t write Client in one service and Account in another; the next developer will assume they’re different things. Use the vocabulary your users already speak: if support says “ticket”, a class called Case will eventually cause a misunderstanding.

Clever names fail too. A service called Gandalf because it decides who shall pass is funny once, to the person who named it. Save jokes for names that are only identifiers, like the office Wi-Fi network or the team’s Discord server. As we argued in what makes a name funny, a name that needs explaining is already failing.

Name at the right level of abstraction

Name things for what they mean to the caller, not how they’re built today. getUsersFromPostgres() becomes a lie the day someone adds a cache. activeSubscribers survives the migration.

The opposite mistake is going so abstract the name stops meaning anything. process(item) and doWork() are true of every function ever written.

Rule of thumb: the wider the scope, the longer and more specific the name. i is fine in a three-line loop. A module-level i is a cry for help.

Renaming is refactoring

Your first name is a guess made when you knew least about the problem. When you learn what a thing really is, rename it. IDE rename tools make that nearly free, so the only real cost is nerve.

  1. Rename in its own commit. A pure rename is easy to review. One tangled with logic changes is a spot-the-difference puzzle.
  2. Deprecate before you delete. For public APIs, keep the old name as an alias for a while.
  3. Think hardest about the expensive names. Database columns, API fields, event names and config keys outlive the code that created them.

Repos, services and branches

Past a handful of services, whimsy stops scaling. An alert at 3am saying falcon is down tells the on-call engineer nothing. invoice-renderer is down tells them what broke and who to wake. Name services for what they do, and repos to match.

Themed naming still suits machines, where a name is just an identifier: build hosts named after cheeses hurt nobody, and our server names list exists for exactly that. But if people need to infer the job from the name, describe the job.

Branches show up in CI logs and review queues, so give them a pattern: fix/1423-duplicate-receipts beats wip and daves-branch, and the ticket number will rescue future you during an incident.

Naming in code review

“Bad name” is not a review comment. Useful naming feedback does three things:

  1. Says what you misread. “I assumed items was the cart, but it’s the wishlist” is evidence. If a careful reviewer misread it, the name is wrong, however technically accurate.
  2. Offers an alternative. wishlistItems gives the author something to accept in one click.
  3. Labels the weight. A local variable is a nit. A public API or schema name is worth blocking on.

Then timebox it. If two names are equally good, the author picks. If the debate outlives the pull request, take it to a call and give that call a better title than “Sync”. Our funny names for Zoom meetings can help.

Frequently Asked Questions

Who said “there are only two hard things in computer science”?

The line is widely attributed to Phil Karlton and has circulated among programmers for decades. A popular variant adds a third hard thing, off-by-one errors, which works because the list then miscounts itself.

Should variable names include the type?

Rarely. strName and arrUsers repeat what the type system and your editor already know, and the prefix becomes a lie the day the type changes. Name the meaning and let the tooling track the type.

How long should a variable name be?

Long enough to be unambiguous in its scope, and no longer. customerEmail is right. theEmailAddressOfTheCurrentCustomer is trying too hard. If a name keeps growing past four words, the thing it names is probably doing too much.

The short version

Name for the reader, not the writer. Reveal intent, spell words out, make booleans ask questions, give functions verbs, and delete the word Manager on sight. Pick one word per concept, and rename the moment you know better.

Nobody gets every name right the first time, which is why the joke has lasted. For more on names in software and AI, browse the tech and AI section or the full index of name lists.