Logo von nextlevels
DEEN
Request a project

Ubiquitous language

Last updated on

In short

Ubiquitous language is the shared, binding domain language that domain experts and developers share in a domain-driven design project. The same terms appear in conversations, requirements, tickets, tests and the source code.

The term was coined by Eric Evans in his 2003 book "Domain-Driven Design: Tackling Complexity in the Heart of Software". "Ubiquitous" means "present everywhere": the language applies wherever the domain is discussed or programmed.

For CTOs and IT managers in mid-sized companies, this is not an academic detail. If sales, production and development understand three different things by "order", misunderstandings, wrong features and expensive rework arise. Ubiquitous language is the tool that closes this gap. How DDD as a whole can be used in mid-sized projects is described in the article domain-driven design in practice.

Origin and meaning of the term

Eric Evans published his book in August 2003 with Addison-Wesley. Ubiquitous language is one of its central patterns: the team builds a language based directly on the domain model. Martin Fowler sums up the idea in his bliki entry on ubiquitous language as a "common, rigorous language" between developers and users. It has to be rigorous because software cannot cope well with ambiguity.

The direction of dependency is important. The language is not a dictionary created afterwards that someone places next to the project. It emerges from the model, and the model emerges from the language. If the understanding of the domain changes, terms and model change together. Evans stresses that domain experts should object when a term expresses domain knowledge only imprecisely or awkwardly.

Evans later published a compact summary of all the patterns, the DDD Reference on domainlanguage.com. It is freely available under a Creative Commons licence (CC BY 4.0) and is suitable as a reference work for teams that already know the book. Vaughn Vernon took up the topic in 2013 in "Implementing Domain-Driven Design" and shows how the language is translated into concrete code.

Why a shared domain language achieves so much

In classic projects there are at least two languages: the language of the business department and the language of the developers. In between sit translators, such as business analysts, product owners or a requirements specification. Every translation costs precision. Ubiquitous language reduces these translation steps because everyone involved uses the same words with the same meaning.

Avoiding translation losses

A domain expert talks about a "Teillieferung" (partial delivery), the ticket calls it "partial shipment", the database has a column split_flag and the test checks status == 7. Four terms for the same thing. Every switch is an opportunity for errors. If instead the same term appears everywhere, a domain expert can read a test name and say immediately whether the rule is correct.

Securing domain knowledge in the system

Many mid-sized companies depend on a few knowledge holders who have special cases and exceptions in their heads. If this knowledge is in meaningfully named classes, methods and tests, it survives staff changes. The difference is particularly evident in organically grown legacy software: code with domain terms can be followed, while code full of technical abbreviations has to be painstakingly reconstructed.

There is also an early-warning effect. If a term feels clumsy in conversation or domain experts constantly have to paraphrase it, this is often a sign of a weak model. The language makes design problems audible before they become expensive in the code.

Scope: one language per bounded context

A common misconception is that the ubiquitous language applies to the whole company. Evans deliberately limits it to a bounded context, i.e. a clearly delimited area in which a model and its terms apply unambiguously. In his entry on the bounded context, Martin Fowler describes why a single, company-wide model rarely works in large domains.

The German word "Auftrag" (order) illustrates this well. In sales, an order is an accepted quote with a price and delivery date. In production, it is a production order with a routing and capacities. In service, it is a call-out to the customer. All three meanings are correct, but only in their context. Anyone who forces them into a common model gets an object with dozens of optional fields that nobody understands any more.

The boundaries between the contexts and the translation at these boundaries are documented in a context map. It specifies how a sales order becomes a production order. Within a context, on the other hand, the language remains strictly unambiguous.

How to build a ubiquitous language and keep it alive

A domain language does not emerge in one meeting; it grows with the project. These steps have proven themselves:

  1. Model together: workshops such as event storming bring domain experts and developers to the same wall. Alberto Brandolini developed the format; details can be found at eventstorming.com. The domain events collected there, such as "maintenance completed", provide the first terms.
  2. Create a glossary per context: each term gets a short definition, an example and, where necessary, forbidden synonyms. The glossary is best kept versioned in the repository, close to the code.
  3. Use the terms everywhere: classes, methods, events, test names, API fields, tickets and user stories carry the same words. Code reviews also check naming.
  4. Involve domain experts: they read test scenarios and glossary entries and object when a term does not fit.
  5. Clarify deviations immediately: if a new synonym appears, the team decides which term applies and adjusts the glossary and code.

Terms change: renaming is part of the job

A ubiquitous language is never finished. New insights lead to new or more precise terms. If the team discovers that a "maintenance order" actually means two different things, such as scheduled inspection and fault repair, the model has to be split and the code renamed. This targeted refactoring is not extra effort; it keeps language and code in sync.

Modern IDEs make renaming in code easy. Public interfaces, database columns and events consumed by other systems are more difficult. Versioning, transition phases with alias fields and clear communication to the dependent teams help here.

German or English in the code? Two positions

In German companies, a question arises that Evans' book hardly deals with: in which language do the domain terms appear in the code? There are good arguments for both answers, and the decision depends on the team and the environment.

Position 1: German domain terms in the code

Supporters argue that terms such as "Rüstzeit" (set-up time), "Abnahme" (formal acceptance), "Gewährleistung" (statutory warranty) or "Kommission" (picking order) have no exact English equivalent. Every translation creates precisely the gap that the ubiquitous language is supposed to close. Domain experts can read test names directly, and the glossary needs no translation column. The disadvantages: mixed code such as getRuestzeit(), umlauts in identifiers that cause problems depending on the tooling, and a higher barrier to entry for international or external developers.

Position 2: English throughout

Other teams write consistently in English because frameworks, libraries and technical literature are in English and because international colleagues without German skills work on the project. The code reads uniformly. The price is a translation layer: the glossary must map each German domain term bindingly to exactly one English term, otherwise competing translations arise. Moreover, mistranslations are hardly noticed by domain experts, because they do not read the code.

Many teams choose a middle way: technical vocabulary such as Repository, save or Handler in English, domain terms in German. The decisive criteria are the composition of the team, whether domain experts are supposed to read tests, and how well the domain terms can be translated. All that matters is that the decision is made consciously and recorded in the glossary.

BDD and Gherkin: making the language executable

Behaviour-driven development (BDD) is a practical tool for anchoring the ubiquitous language in tests. Dan North introduced BDD in 2006 in the article "Introducing BDD". According to the Cucumber documentation, the given/when/then scheme was explicitly influenced by the ubiquitous language from Evans' book.

According to the Gherkin reference from Cucumber, the description language Gherkin is available in over 70 languages. With the header line # language: de, the keywords include "Funktionalität", "Szenario", "Angenommen", "Wenn" and "Dann". This produces acceptance criteria that domain experts read and developers execute automatically — here in German, as a German team would write them:

# language: de
Funktionalität: Serviceeinsatz abschließen
  Szenario: Abschluss ohne unterschriebenes Abnahmeprotokoll
    Angenommen ein Serviceeinsatz ist im Status "laufend"
    Wenn der Techniker den Einsatz ohne Kundenunterschrift abschließen will
    Dann wird der Abschluss abgelehnt

The added value lies in the language, not in the tool. If the scenarios use different words from the code, BDD has missed its purpose.

Example: domain language in the code at a mechanical engineering company

The following example is fictitious. A mechanical engineering company is developing software for its technical customer service. In the service context, the business department and development have agreed on these terms (kept in German in the code, with English glosses):

Fictitious example: glossary terms, their implementation in code and typical anti-patterns
Term in the glossaryIn the codeWrong (anti-pattern)
Serviceeinsatz (service call)ServiceeinsatzJob, Ticket, Order alternately
Einsatz abschließen (complete the call)abschliessen(protokoll)updateStatus(id, 4)
Abnahmeprotokoll (acceptance report)Abnahmeprotokolldoc, attachment2
Ersatzteilanforderung (spare parts request)ErsatzteilanforderungOrderItem from the sales context
Wartung abgeschlossen (maintenance completed, event)WartungAbgeschlossenStatusChangedEvent

The difference becomes visible in the code. The first function is technically correct but reveals nothing about the business rule. The second variant names the intention and makes the rule from the Gherkin scenario directly readable:

// Unclear: technical names, the business rule is hidden in magic numbers
function update(o: Order, s: number): void {
  if (s === 4 && o.doc) {
    o.status = s;
  }
}

// Domain language: the code says what happens in customer service
type EinsatzStatus = "geplant" | "laufend" | "abgeschlossen";

class Serviceeinsatz {
  private status: EinsatzStatus = "geplant";

  abschliessen(protokoll: Abnahmeprotokoll): WartungAbgeschlossen {
    if (!protokoll.istVomKundenUnterschrieben()) {
      throw new AbnahmeFehlt(this.id);
    }
    this.status = "abgeschlossen";
    return new WartungAbgeschlossen(this.id, protokoll.datum);
  }
}

The same principle works with English names such as ServiceCall.complete(), as long as the glossary specifies the mapping bindingly. What matters are meaningful names that express a business action, instead of generic verbs such as update or process.

Typical mistakes with ubiquitous language

  • A glossary without reference to the code: a wiki glossary that nobody maintains and that does not appear in the code remains decoration.
  • One language for the whole company: anyone who wants to standardise "customer" or "order" company-wide creates bloated models. Every bounded context needs its own language.
  • Developer jargon as domain language: terms such as "entity", "DTO" or "flag" do not belong in conversations with the business department.
  • Tolerating synonyms: if "call-out", "order" and "appointment" are supposed to mean the same thing, the team has to settle on one term.
  • Leaving domain experts out: a language defined only by developers is not a shared language.
  • Shying away from renaming: outdated names in the code, because renaming seems tedious, let language and model drift apart.

Frequently asked questions about ubiquitous language

What is the difference between ubiquitous language and a glossary?

A glossary is a document. The ubiquitous language is the team's living language, used equally in conversations, tickets, tests and code. The glossary is a tool for recording this language. Without use in the code, it remains ineffective.

Do I need domain-driven design to use a ubiquitous language?

No. The term comes from DDD, but every software project benefits when the business department and development use the same terms. However, the language only develops its full effect together with a deliberately designed domain model and clear bounded contexts.

Who is responsible for the ubiquitous language?

The whole team, consisting of domain experts and developers. In practice, it helps if one person, such as the product owner, coordinates changes to the glossary. But decisions about terms are made by the team together, and code reviews check compliance.

How do I start in an existing system?

Begin with the context that is currently being actively developed. Collect the most important terms in a workshop, create a small glossary and consistently name new code accordingly. Adapt existing code step by step whenever you touch it anyway.

Next step

Do your business and IT teams speak the same language?

Shared domain terms from the first workshop to the last line of code: we build enterprise software that your domain experts can follow.

Changelog

  1. · Created · Jan Schumann