There’s a well-known quote about the two hard problems in computer science: cache invalidation and naming things. The naming problem is usually discussed in terms of choosing good names. But there’s a subtler version of the problem that causes more damage.

The Split

It starts innocently. Someone builds a service and calls it DataProcessor. In conversation, everyone calls it “the ingestion pipeline.” The code says one thing. The team says another.

Now you have two names for the same concept, and they drift.

The ingestion pipeline gains features. It starts doing transformation, validation, routing. But the class is still called DataProcessor. A new team member reads the code and sees DataProcessor. They read the wiki and see “ingestion pipeline.” They’re not sure if these are the same thing. They ask. Someone explains. Ten minutes spent on a problem that shouldn’t exist.

Multiply this by every concept in a codebase and you get a significant tax on communication.

Why It Happens

The split happens because naming in code and naming in conversation serve different constraints.

Code names need to be valid identifiers. They can’t have spaces. They follow conventions — PascalCase, snake_case, whatever the language demands. They need to be unique within their scope.

Conversation names need to be memorable. They need to be short enough to say out loud. They often evolve organically — someone calls a component “the widget thing” in a meeting, and it sticks.

These constraints pull in different directions. The formal code name and the informal conversation name diverge. Neither is wrong. Both are incomplete.

The Ubiquitous Language

Domain-Driven Design introduced the concept of “ubiquitous language” — the idea that the team should use the same terms everywhere. In code, in conversation, in documentation, in tickets.

This sounds simple. It is simple to state. It is very difficult to maintain.

The ubiquitous language requires discipline at the exact moment when discipline is hardest: during live discussion. When someone starts a sentence with “you know, the thing that handles…” you have to stop and say “the OrderRouter?” And then everyone has to agree to call it that, every time, even when “the routing thingy” is faster to say.

The payoff is that new team members can search the codebase for the term they heard in a meeting and find the relevant code. The code becomes searchable by its concepts, not just its implementation details.

The Rename Resistance

Once a split exists, fixing it means renaming something. And renaming things in code is surprisingly expensive.

Every reference needs updating. Tests break. Configuration files point to the old name. Deployment scripts use the old name. Log analysis queries use the old name. The old name is cached in people’s muscle memory.

This cost means that renames happen reluctantly and late, long after the confusion has become entrenched. The best time to get the name right is when the thing is created. The second best time is as soon as you notice the split.

The Test

Here’s a simple test for name health: can a person who just joined the project read a code review and follow the discussion without a glossary?

If the review mentions UserAuthService and the comments reference “the login flow,” there’s a split. If the commit message says “fix race condition in EventBus” and the ticket says “resolve timing issue in the notification system,” there’s a split.

Each split is small. Their accumulation is not.

Getting It Right

The fix isn’t clever naming conventions or mandatory terminology documents. It’s a habit: when you name something in code, say the name out loud. If it sounds wrong in conversation, it’s the wrong name.

A name that works in code and in conversation is worth the extra minutes spent choosing it. Those minutes save hours of confusion later. Name things once. Make sure the one name works everywhere.