Naming isn't hard. You're just not listening.

Andrew D.Andrew D.
8 min read

In the last article: Building software that actually has meaning, I briefly introduced DDD and how it helps us reflect our business in code. We do this by understanding the domain, the industry, the business we are trying to model. One of the strongest ways of doing so is by speaking the language of the domain. This is what we refer to as ubiquitous language, and its the (first) model of your system.

I’ve been in countless meetings where developers, product managers, and other stakeholders were all using the same word, but meaning completely different things. One person’s “order” was an invoice. For someone else, it was a shopping cart. Someone else thought it meant a shipment. This is exactly how code, becomes chaos.

💡
If a word exists in the business but not in the code, your system is losing information. If a word exists in the code but not in the business, you’re inventing complexity.

What is this "Ubiquitous Language"?

The term, coined by Eric Evans in his seminal book "Domain-Driven Design", is (deceptively) simple.

Ubiquitous Language: A shared, rigorous language developed collaboratively by the entire team: developers, domain experts, product managers, and stakeholders, to talk about the domain of the software.

This language isn't just a list of buzzwords. It’s an intentionally crafted vocabulary where each term has a precise, unambiguous meaning. It's used everywhere: in conversations, on whiteboards, in user stories, in documentation, and, more importantly, in the code itself.

When a business expert talks about a "Premium Policy," the code should have a PremiumPolicy class. When they describe the process of "underwriting a claim," there should be a service or method called underwriteClaim(). There is no translation. The language is ubiquitous (found everywhere).


Why use a shared vocabulary?

Ubiquitous Language is used when discussing and implementing the domain by everyone on the team including developers, product owners, domain experts, testers.

It’s not just naming things nicely. It’s about aligning your language with the business so that every term in your codebase maps cleanly to a real-world concept your users care about.

When done right:

  • Your models reflect real concepts, not abstractions.

  • Your method names mirror business actions.

  • Conversations flow naturally from whiteboard to code editor.

💡
If a product manager says, “We need to apply a discount policy during checkout,” you should have something like DiscountPolicy.applyTo(cart) in your code, not Utils.calculateFinalPrice().

This reduces translation overhead, eliminates ambiguity, and creates clarity across your entire system. Because if we don’t understand what we're building, how can we design it well?

Signs you’re not using ubiquitous language

Before we go further, let’s diagnose your current environment. If any of these feel familiar, chances are your language is fractured and your domain model is suffering:

Developers and PMs use different terms for the same concept
A product manager talk about “quotes,” but developers refer to them as “proposals.”

The same word means different things to different people
“Order” might mean “a confirmed transaction” to one team, but “an item in a shopping cart” to another.

Code is full of generic abstractions
If you see lots of Manager, Helper, or Util classes, you're likely working around missing domain clarity.

Business rules live in tribal knowledge
When logic isn’t reflected in code and just lives inside someone's head or in some Confluence document, you don’t have a shared language. You have a guessing game.

By surfacing these issues and pointing them out, we can begin to create the space to fix them, and not just by slamming more processes, but with better conversations. And we start by all of us being on the exact same page about what something means.

Where do you start?

With a single, non-negotiable rule for your team:

If a concept, term, or rule is not defined in the Ubiquitous Language, it does not exist.

This changes the dynamic of every conversation. Imagine you're in a planning meeting, and your PM says, "We should really give users a grace period after their subscription expires."

A dev jots down "add grace period," and the team moves on.

The new way: Everyone stops.

  • "Hold on. What do we mean by grace period?"

  • "Is it different from a Suspension?"

  • "What can the user do during this period? What can't they do?"

  • "How long does it last? Is it the same for all subscription types?"

Your (team's) first job is not to write code. It's to collaboratively define what a "grace period" is. Update the shared glossary (perhaps a confluence page). Agree on the definition, rules, and boundaries. Only then can you discuss how to model and implement it. First, you update the language. Then, you update the code.

Start listening for ambiguity in your next meeting. When you hear two people use the same word to mean different things, don't let it slide. Stop and ask, "What do we mean by that?". Here’s a tweet codeopinion had pinned that I really like and that provokes this idea:

How to Develop Ubiquitous Language Collaboratively

Ubiquitous Language isn’t invented in a vacuum. It’s discovered through conversations, refined through iterations, and solidified in both code and communication.

  1. Talk to domain experts
    Sit down with the people who live and breathe the business: sales teams, operators, analysts, etc. Ask how they talk about the domain. What terms do they use? What distinctions matter to them?

    You’re not looking for implementation details, you’re mining for conceptual clarity.

  2. Model together
    Use whiteboards, sticky notes, or collaborative tools like Miro to sketch out business processes and data flows. As terms emerge, challenge them. Ask:

    “When you say ‘account,’ do you mean a user, a customer, or a payment profile?”

    Modeling in conversation is how ambiguous terms get ironed out.

  3. Bake the Language Into the Code

    Class names, method names, file names, these should all reflect the shared vocabulary.
    If your domain expert says “shipment,” you shouldn’t be writing ItemDeliveryManager. Use the exact words your team has agreed on. Model the code based on the real-world, not the other way around.

  4. Evolve it continuously

    Your understanding of the domain will deepen over time. Your language should, too. Keep the conversation going in retros, domain discovery sessions, and design reviews. Ubiquitous Language is an evolving asset, not a one-time artifact.

An example of ubiquitous language in logistics

Let’s say you’re building a logistics platform for a company that ships products internationally. There are:

  • Warehouse managers

  • Customer service reps

  • Transportation partners

  • Business analysts

  • Backend and frontend devs

Everyone thinks they’re speaking the same language, but are they, really?


“We need a shipping feature”

The business asks for a feature to “track shipments”, and the product owner says:

“We need to show the customer when their shipment is dispatched, where it is, and when it’s delivered.”

The backend dev creates a Shipment class with fields like status, createdAt, deliveredAt. While this may seem intuitive and correct, it might actually lead to chaos.


Everyone Means Something Slightly Different

When you talk to different domain experts, you quickly discover the term shipment is overloaded:

  • Warehouse Manager: A shipment is created when a package is picked and packed. To them, it starts inside the warehouse.

  • Carrier (e.g. DHL): A shipment starts after pickup, when it enters the transportation network.

  • Customer Service Rep: They see a shipment as the customer’s whole order, including separate boxes.

  • Backend Dev: They've modeled a shipment as 1:1 with a single order line item.

  • Business Analyst: They refer to bulk movements of inventory between warehouses as “shipments,” too.


Clarify the Language Through Modeling

Now, you bring everyone together in a domain modeling session. Through discussion, sketching flows, and asking lots of “what do you mean by…” questions, you discover you actually have three different concepts hiding behind the word "shipment":

  • Fulfillment is when the warehouse picks, packs, and hands off a package

  • ParcelShipment is a single trackable package that moves through a carrier (with a tracking number)

  • CustomerOrderShipment is the business-facing concept: "Your order has shipped"


Bake the Language into the Code

You never start with code. You first understand the domain and then you start modeling in code. Now, each concept has a distinct name, directly tied to how the business actually operates, and everyone is on the same page on it.

Pseudo-example:

public record Fulfillment(
    string Id,
    LineItem[] OrderLineItems,
    DateTime PickedAt,
    DateTime PackedAt,
    DateTime HandedOffAt
);

public record ParcelShipment(
    string TrackingNumber,
    Carrier Carrier,
    ShipmentStatus Status,
    DateTime EstimatedDeliveryTime
);

public record CustomerOrderShipment(
    string OrderId,
    ParcelShipment[] Parcels,
    CustomerOrderShipmentStatus Status
);

Everyone Starts Speaking the Same Language

  • Customer service can now say:
    “This customer’s order has 2 parcel shipments, one is delivered, the other is in transit.”

  • Devs can write:
    customerOrder.getOutstandingParcels(), instead of shipmentManager.getItems().

  • Analysts can ask:
    “What’s the average handoff delay between fulfillment and parcel pickup?”


This clarity improves:

  • Communication across roles

  • Code readability and correctness

  • Feature discovery (e.g. you now realize you can support partial shipments)

  • Domain understanding for new team members

That’s the power of Ubiquitous Language:
You don't just name things better, you see the business better.

🧠 Final thoughts

Too often, we jump straight into code, frameworks, and tooling, hoping architecture will somehow emerge from chaos. But Domain-Driven Design asks us to zoom out, to listen, and to design with deep intention. It’s not about patterns for the sake of patterns. It’s about building software that speaks the language of the people it serves.

When your models reflect real business concerns and when your code reads like a conversation between domain experts, this is when you unlock something powerful: Alignment. Clarity. Velocity.

Challenge: Come up with an example of how a concept can be misunderstood due to a lack of ubiquitous language and post it in the comments below.

0
Subscribe to my newsletter

Read articles from Andrew D. directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Andrew D.
Andrew D.

Currently learning how to build time machines with the critter stack; CQRS + ES. Also heavily betting on the Cloudflare stack to ship highly scalable services at the edge, quickly.