← All articles

Ubiquitous Language Is Just Meaningful Names for Grown-Ups

You would reject a variable called `d` in code review. So why do you accept a codebase where "customer" means three different things depending on who is talking? DDD's most important idea is not enterprise ceremony — it is the naming discipline you already believe in, escalated one level.

You would block this in review without thinking about it:

function proc(d: number, f: number): number {
  return d * f;
}

Nameless intent. You would ask for applyDiscount(subtotal, discountRate) and you would be right, because you already believe the thing every naming guide is really arguing: names carry intent, and a name that needs a comment is a name that failed.

Now, the same codebase. Product calls it a subscription. The database table is accounts. The billing service says contract. Support tickets say "the customer's plan." Four words, one concept — or possibly two concepts, and nobody is entirely sure which.

That passes review every day. It is the same defect, three levels up, and it costs incomparably more.

The thing you already accept

Meaningful naming is one of the highest-leverage inputs to readable code. The heuristics are familiar: reveal intent, avoid disinformation, use pronounceable and searchable names, don't encode types. The underlying claim is that the name is where understanding is transferred, and everything else — comments, docs, tribal knowledge — is a worse substitute.

You have internalised that for the next developer reading your function.

Escalate it one level

Now change who the reader is. Not the next developer — the domain expert, and the code, at the same time.

That is all a ubiquitous language is: a single, rigorous language shared by developers and domain experts, used in conversation, in documents, and in the code. The word the expert uses is the class name. The whiteboard term is the API field. There is no translation step.

And the reason to want it is exactly the reason you want good names, scaled up: translation is where knowledge leaks and bugs are born. Every time someone converts "the business means X" into "so the code calls it Y," a little precision falls on the floor. Do it across a dozen conversations and a two-year codebase, and the model quietly stops matching the business it claims to describe.

There is one test for this, and it is unusually sharp:

If the domain experts cannot follow your model, the model is wrong.

Not the experts. The model. The usual excuses — it is too abstract for them, they do not think in objects, they would not understand the technical constraints — get this exactly backwards. A model that only its authors can discuss has already failed at the one job it had, which was to be a shared representation of the problem.

If you accept that a name which needs a comment has failed, you have already accepted this. It is the same standard with a wider audience.

The technique that makes it concrete

The part that converts this from a slogan into a practice is modelling out loud: refine the model by speaking scenarios aloud using its terms, and treat awkward phrasing as a defect report about the model.

Take a shipping conversation. The first version is vague and half-technical: "we look up the legs for the booking and stick them in the schedule table." Nothing in that sentence is a domain concept — legs, schedule table and stick them in are three different altitudes in one breath, and a logistics expert cannot confirm or deny any of it.

Now say it again with the model carrying the weight: a Routing Service finds an Itinerary that satisfies the customer's Route Specification. That sentence is sayable to a logistics expert and it names three things that exist in the code, in the same words. When the sentence gets easier to say, the model got better. When you find yourself reaching for a workaround phrase, the model has a gap — and the awkwardness is the bug report.

This is why the language has to live in speech, not just in the repo. If a new term appears in the code but nobody says it in standup, the change did not take.

Why the codebase-only version is not enough

There is a version of this that engineers do naturally and stop at: pick good names inside the code, and translate at the boundary when talking to the business.

It fails for the same reason a well-named private method inside a badly-named class fails. The translation layer is still there — you have just moved it into people's heads, where it cannot be reviewed, tested, or refactored. Two engineers will translate "account" differently, and neither will notice until something bills wrong.

The catch that turns naming into design

Here is the part that stops this being naive, and it is the part most "we tried DDD" stories skip.

A language is only ubiquitous within a bounded context — never globally. "Customer" genuinely means different things in sales, billing, and support, and forcing one definition across all three produces a model that serves none of them. The failure mode is trying to build one canonical Customer for the whole company, and it is a well-travelled road to a class with forty nullable fields.

So the discipline is not "agree on one word." It is:

  1. Inside a boundary, be rigorous — one term, one meaning, in speech and in code.
  2. At the boundary, translate explicitly — and notice that the place where a word's meaning shifts is telling you where a real seam in the system is.

That second point is why this stops being about vocabulary. The word boundary and the design boundary are the same boundary. Naming, done seriously, is how you find your contexts.

The failure mode to watch

Teams adopt the vocabulary without the precision. Everyone starts saying "aggregate" and "context," the words get shared, and the meanings stay fuzzy. That is not a ubiquitous language — it is jargon adoption, and it produces the DDD projects that give DDD its reputation.

The test is not whether the team uses the words. It is whether two people, asked to define one of them, give the same answer.

Three moves for Monday

  1. Rename to the domain's word, not a synonym. If the business says policy, the class is Policy — not InsuranceContract because it reads better to you. Your preference is not a tiebreaker here.
  2. Get the term into both the model and the conversation. If it only exists in code, it has not landed. Say it in the next planning meeting and see whether anyone flinches — a flinch is data.
  3. Draw the boundary where the meaning shifts. When you find one word meaning two things, you have not found a naming argument. You have found a context boundary, and it was going to hurt you eventually anyway.

None of this is enterprise ceremony. It is the naming standard you already enforce in review, applied to the one interface that actually determines whether the software is right: the one between the code and the people who understand the problem.