Naming Is Design: The Name Comes First
We treat naming as the finishing touch on code. It's the opposite: a name is your first abstraction, and a bad one is usually a design decision you haven't made yet.
The longest code review argument I ever witnessed was, on the surface, about a name. Two senior engineers, forty-some comments, escalating politeness — always a danger sign — over whether a class should be called PaymentProcessor or PaymentAttempt. A bystander called it bikeshedding. It was the opposite of bikeshedding. Buried in that thread was the actual design question of the entire subsystem: is this thing a service that acts, or a record of something that happened? One name implied statefulness, retries, and side effects; the other implied immutability and an audit trail. They weren't arguing about a label. They were arguing about what the thing was — and the name was the first place the disagreement became visible.
That's the thesis I want to defend today: naming is not the last step of implementation. It is the first act of design.
A name is a tiny theory
Phil Karlton's old joke — "there are only two hard things in computer science: cache invalidation and naming things" — endures because it's not really a joke. Naming is hard for the same reason design is hard: a name is a claim. It asserts that some slice of the world is one thing, that it has a boundary, that everything inside the boundary belongs together and everything outside doesn't.
When you name a function calculateShippingCost, you have committed to a theory: shipping cost is a computable property, derived from inputs, without side effects. If the function also reserves warehouse inventory, the theory is false, and every reader who trusted the name is now working from bad information. The bug isn't in the logic. The bug is in the claim.
This is why I tell students that a name is the smallest unit of abstraction we have. Long before you have an interface, a module, or an architecture diagram, you have a word — and the word is already doing an abstraction's job: hiding detail, promising behavior, drawing a boundary.
When you can't name it, you haven't designed it
Here is the most practically useful inversion of the idea: difficulty naming a thing is diagnostic information about the design.
You've felt this. You extract a function and reach for a name, and everything you try comes out wrong. processOrderAndNotify. validateOrUpdateUser. handleDataStuff. The conjunctions are confessions — and means the function has two jobs; or means it has two behaviors selected by something the name doesn't mention; handle and stuff mean you've drawn a boundary around things that don't belong together.
The novice response is to pick the least-bad name and move on. The better response is to treat the naming struggle the way a physicist treats an ugly equation: as a hint that the decomposition is wrong. Split the function until each piece can carry an honest verb-noun name, and you'll usually find the design improving as a side effect — not because small functions are magic, but because nameability is a proxy for conceptual coherence. The name isn't decorating the design. The name is testing it.
I'll go further: when I design something new, I try to write the names before the code — the nouns of the domain, the verbs of the operations — as a plain list. If the list reads like a coherent story, the design usually survives contact with implementation. If the list needs a paragraph of caveats, I've saved myself a week.
The domain speaks a language — borrow it
Eric Evans, in Domain-Driven Design, gave this practice a name of its own: the ubiquitous language — the discipline of using the domain experts' own vocabulary in the code, one term per concept, the same term everywhere.
The payoff is larger than politeness to your product manager. When code says Policy, Claim, and Adjuster — and those words mean exactly what the insurance people mean — three expensive translation layers collapse into one. Requirements conversations map directly to classes. Bug reports map directly to modules. A new engineer who learns the business is simultaneously learning the codebase.
And when the code invents its own dialect — when the domain's "quote" becomes the code's PriceRequestDTO, and the domain's "binding" becomes finalizeQuoteFlow — every conversation between business and engineering passes through an unwritten dictionary that lives in three senior heads. People leave; dictionaries leave with them.
One sharpened point for the AI era: your names are now prompts. Code assistants infer intent from surrounding vocabulary and propagate it. Name a concept well and the tool extends it coherently; call something manager2 and you have taught your very fast new colleague to speak nonsense fluently. Vocabulary has always compounded through a codebase. It now compounds faster.
A field guide to naming smells
A short taxonomy, from years of reading student and industry code alike:
The numbered ghost: data2, newList, temp3. A number suffix means "same kind of thing, different role — but I never figured out what the role was." The reader must now do the figuring, on every read. Name the role: retriesRemaining, dedupedUsers.
The weasel verbs: handle, process, manage, doStuff. These verbs are grammatically active and semantically empty. handleUpload tells me an upload is involved and nothing else. What happens to it? persistUpload, virusScanUpload, rejectOversizedUpload — each is a design decision the weasel verb was deferring.
The liar. getUser that creates one when missing. isValid that mutates state. tmpFix from 2019. Liars are worse than weasels: a vague name makes the reader work, but a false name makes the reader wrong. In review, I treat a lying name as a correctness bug, because downstream it will cause one.
The junk drawer: utils, helpers, common, misc. These are not module names; they are confessions that no one decided where things belong. Junk drawers grow monotonically — nothing named misc has ever gotten smaller.
The synonym shuffle. Customer here, Client there, Account in the billing service — all the same entity. Each synonym costs every reader a "wait, are these the same?" for the lifetime of the code.
Office hours
Practical exercises, as always:
- Rename one liar this week. Find a name that makes a false claim and make it honest — or better, make the code honest and keep the name. Either way, the claim and the behavior must match.
- Use the conjunction test. If a candidate name needs and, or, or a weasel verb, don't shop for a better name yet — ask whether the boundary is wrong.
- Write the glossary. Twenty minutes with your team: list the ten core nouns of your domain and their one canonical name each. Post it where reviews happen. Enforce it gently.
- In review, ask "what would you name this?" It's the least confrontational design question in existence, and it surfaces disagreements about what a thing is before they calcify into architecture.
We think we're arguing about labels. We're arguing about theories, one word at a time. Choose your words the way you'd choose your foundations — because that's what they are.