Logo
Overview

Ubiquitous Language: единый язык предметной области в команде

October 16, 2026
8 min read

Ubiquitous Language — это не глоссарий на 40 страниц в Confluence, который открыли один раз перед релизом и больше не вспоминали. И не список терминов, который системный аналитик скинул в чат команды со словами «ознакомьтесь, пожалуйста». Это живой инструмент. Он либо работает каждый день — в коде, в обсуждениях, в названиях таблиц и API, — либо его нет. Третьего не дано.

Я видел проекты, где на одной встрече бизнес-заказчик 15 раз сказал «заказ», и каждый раз подразумевал разное: то корзину пользователя, то подтверждённый ордер в ERP, то заявку на возврат. Разработчики кивали. А потом в коде появлялся Order, Purchase, CartCheckout, и ещё бог знает что. Через полгода никто не понимал, какая из этих сущностей за что отвечает.

Что такое Ubiquitous Language: определение и границы

Ubiquitous Language (UL) — это единый язык предметной области, который используют все участники проекта: бизнес, аналитики, разработчики и тестировщики. Термины этого языка появляются в разговорах, требованиях, коде и тестах без искажений.

Самое опасное заблуждение — думать, что UL нужен «для порядка». Он нужен, чтобы устранить главный источник багов в корпоративных системах: несовпадение ментальных моделей. Когда бизнес говорит «клиент», а разработчик в коде пишет User, через месяц кто-то прикрутит к User ещё и сотрудника поддержки. И всё — полетели инварианты.

UL не про то, чтобы запретить разработчикам использовать английские названия в коде. Он про то, чтобы слово «клиент» и класс Customer означали строго одно и то же. Всегда. Без вариантов.

Откуда берутся термины

Не из головы архитектора. И не из последней книги по DDD. Термины берутся из речи бизнес-экспертов — тех людей, которые работают в домене последние 10 лет. Системный аналитик слушает, фиксирует, переспрашивает:

— Вы сказали «заявка». Это заявка на кредит, или заявка на изменение лимита?

— На кредит. На изменение лимита у нас «обращение».

Всё. Два разных термина. Две разные сущности. В коде это будет CreditApplication и LimitChangeRequest. Если бы аналитик постеснялся уточнить, мы бы получили один класс Application с кучей if-else внутри.

Подход «слушаем бизнес — фиксируем — переспрашиваем» кажется очевидным. На практике всё сложнее. Бизнес может использовать одно слово для двух разных смыслов и даже не замечать этого. Однажды на проекте для страховой компании слово «полис» означало и договор, и физический бланк, и строку в реестре. Три сущности. Одно слово. Распутали только через event storming — когда выписали все события, происходящие с «полисом», и увидели, что на каждом шаге это разные вещи.

Где живёт глоссарий

Глоссарий UL должен быть в двух местах одновременно:

  1. В коде — названия классов, методов, переменных, таблиц БД, endpoint’ов. Это первичный источник. Если в коде CreditApplication, а в Confluence написано «Кредитная заявка» — первичен код. Глоссарий в вики либо синхронизирован, либо вреден.

  2. В документации — Confluence, README репозитория, или docs/glossary.md, который лежит рядом с кодом и ревьюится вместе с ним. Главное правило здесь: одна запись — один термин. Никаких «Заявка — см. также: Обращение, Запрос, Заказ». Такие связки — признак, что язык ещё не единый.

Сам формат записи в глоссарии:

CreditApplication (Кредитная заявка)
Определение: заявка клиента на получение кредита, проходящая стадии
скоринга, андеррайтинга и одобрения.
Синонимы: нет (не путать с LimitChangeRequest — обращением на изменение лимита)
Где используется: модуль credit-origination, таблица credit_applications,
API POST /v1/credit-applications

Коротко. Без воды. Без «в рамках процесса кредитного конвейера осуществляется обработка». Только конкретика: что это, где лежит, с чем не путать.

100%
graph TD
  A["Бизнес: 'Кредитная заявка'"] --> G["Глоссарий UL"]
  C["Бизнес: 'Заёмщик'"] --> G
  E["Аналитик: требования"] --> G
  F["Разработчик: 'LoanRequest'"] -.->|"конфликт<br/>терминов"| G
  G --> H["Согласованный термин:<br/>CreditApplication"]
  H --> I["Модель предметной области"]
  I --> J["Код: класс<br/>CreditApplication"]
  I --> K["API: endpoint<br/>/credit-applications"]
  I --> L["БД: таблица<br/>credit_applications"]
  H --> M["Документация:<br/>Confluence / OpenAPI"]
  H --> N["Автотесты:<br/>credit_application_test"]
  G --> O["Отклонённый вариант:<br/>LoanRequest — забыт"]
  style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style C fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style E fill:#7b68ee,stroke:#5a4db2,color:#fff
  style F fill:#e0e0e0,stroke:#999,color:#333
  style G fill:#f0a500,stroke:#c88400,color:#fff
  style H fill:#50c878,stroke:#3a9a5c,color:#fff
  style I fill:#7b68ee,stroke:#5a4db2,color:#fff
  style J fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style K fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style L fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style M fill:#7b68ee,stroke:#5a4db2,color:#fff
  style N fill:#50c878,stroke:#3a9a5c,color:#fff
  style O fill:#e0e0e0,stroke:#999,color:#333

На схеме — путь термина «Кредитная заявка» от разных участников команды к единому названию. Обратите внимание на серый блок: разработчик пришёл со своим вариантом LoanRequest, но глоссарий его отклонил. Теперь в коде, API, базе и тестах — везде CreditApplication. Один термин на всех слоях системы.

Как выстроить единый язык: пошаговая практика

Строить UL лучше не «за один присест», а итеративно. На старте фиксируем ядро — 10–15 ключевых терминов, которые покрывают основной пользовательский сценарий. Остальное добирается по мере роста системы.

Шаг 1. Соберите носителей языка

В комнате (или в Zoom) должны быть минимум трое:

  • Бизнес-эксперт — человек, который знает, как работает домен. Не менеджер. Менеджер пересказывает. Эксперт — говорит на языке домена.
  • Системный аналитик — задаёт вопросы, фиксирует, переспрашивает, выявляет омонимы.
  • Техлид или senior-разработчик — переводит термины в названия классов и таблиц на лету, проверяет, не плодятся ли синонимы.

Шаг 2. Зафиксируйте сценарий и выпишите сущности

Возьмите один сквозной пользовательский сценарий. Для e-commerce: «Пользователь кладёт товар в корзину, оформляет заказ, оплачивает, получает уведомление». Пройдите по нему шаг за шагом и на каждом шаге спрашивайте: «Как это называется?»

Результат — список из 10–20 терминов с определениями. Без кода. Пока только слова.

Шаг 3. Проверьте на омонимы и синонимы

Для каждого термина спросите: «Есть ли другое слово, которым кто-то в команде называет то же самое?» Если да — выберите одно и зафиксируйте, второе — в чёрный список (запрещено к использованию).

Обратный вопрос: «Есть ли у этого слова другой смысл в каком-то контексте?» Если да — разделите на два термина.

На одном из проектов в e-commerce слово «корзина» означало и Cart (то, что пользователь наполняет товарами), и Basket (расчётная единица для скидок — набор товаров, к которым применяется одна акция). Два разных понятия. Одно слово. Разделили на Cart и PromoBasket. Споры заняли час. Месяц разработки сэкономили.

Шаг 4. Привяжите термины к коду

Теперь каждый термин получает «прописку» в системе:

  • Класс/модуль: имя на английском, соответствующее термину
  • Таблица БД: имя таблицы, колонок
  • API endpoint: путь, модель запроса/ответа
  • Тесты: название тестового сценария

Имя класса не обязано быть дословным переводом русского термина. CreditApplication — нормально. Главное, чтобы соответствие русский → английский было однозначным и задокументированным.

Шаг 5. Настройте ревью по глоссарию

Самое трудное — не создать глоссарий, а заставить его работать. Правило простое: любое новое название в коде (класс, таблица, endpoint, поле API) сверяется с глоссарием на code review. Если термина нет — он либо добавляется в глоссарий, либо переименовывается под существующий.

Без этого через три спринта у вас снова появятся Order, Purchase и CartCheckout.

Антипаттерны: как единый язык превращается в мёртвый документ

Антипаттерн 1. Глоссарий-простыня. 200 терминов, половина из которых не используется. Причина: в глоссарий записывали всё подряд на старте, не привязывая к сценариям. Лечение: удалить все термины, которых нет в коде.

Антипаттерн 2. Словарь синонимов. Глоссарий, где на каждый термин три варианта: «Заказ (он же Order, он же Purchase, он же CustomerRequest)». Это не единый язык. Это мусорка. Лечение: выбрать один вариант, остальные — запретить.

Антипаттерн 3. Глоссарий аналитика. Документ, который ведёт только аналитик. Разработчики не открывают, тестировщики не знают о его существовании. Лечение: перенести в репозиторий и ввести ревью.

Антипаттерн 4. UL через принуждение. Архитектор спускает список терминов сверху: «С сегодняшнего дня User называем Customer». Команда саботирует, потому что не участвовала в обсуждении. Язык работает только когда команда договорилась, а не когда подчинилась.

Антипаттерн 5. Слепое копирование из другого bounded context. В соседнем контексте сущность называется Product. Вы решаете: у нас тоже будет Product. Но ваш Product — это товар в каталоге, а их — продукт как финансовая услуга. В одном bounded context один термин — одна сущность. Между контекстами термины могут различаться.

Что писать в ТЗ, если в команде есть единый язык

Отдельный вопрос — как UL влияет на документацию. Когда единый язык внедрён, техническое задание перестаёт быть простынёй с определениями. Вместо:

«Система должна обеспечивать возможность оформления пользователем заявки на получение кредитного продукта с последующим изменением статуса заявки ответственными сотрудниками…»

Пишем:

«Клиент (Customer) создаёт кредитную заявку (CreditApplication). Сотрудник андеррайтинга переводит её в статус Approved или Rejected. Решение фиксируется в поле decision агрегата CreditApplication.»

Разница — в конкретике. Термины из UL работают как ссылки на код. Разработчик читает ТЗ и видит не абстрактное «изменение статуса заявки», а конкретный метод approve() на классе CreditApplication. Связка документации и кода через UL — это, честно говоря, одна из тех вещей, которые на словах звучат утопично, а на практике экономят часы.

UL в связке с DDD работает не изолированно. По-хорошему после единого языка выходят на bounded context и агрегаты — термины UL естественно группируются по контекстам. Слово «заявка» в кредитном контексте — это CreditApplication. То же слово в контексте поддержки — это SupportTicket. Разные контексты, разный язык.

Три ключевых совета из практики

Совет 1. Начинайте с малого. 10 терминов для одного сценария. Не пытайтесь описать весь домен за одну встречу. Это как пытаться выучить иностранный язык по словарю: запомните первые две страницы и бросите.

Совет 2. Спорьте при свидетелях. Когда бизнес-эксперт настаивает, что «клиент» и «пользователь» — одно и то же, а вы видите разницу, не решайте спор вдвоём. Вынесите на общую встречу с техлидом. Пусть команда примет решение. Если решение принято единолично — оно не работает.

Совет 3. Держите глоссарий там же, где код. Не в отдельном пространстве Confluence с особыми правами доступа. В репозитории, рядом с моделями. Тогда у разработчика нет оправдания «я не видел».

Чек-лист: единый язык на проекте за неделю

  • Собрали встречу с бизнес-экспертом, аналитиком и техлидом. Выбрали один сквозной сценарий
  • Выписали 10–15 ключевых терминов с чёткими определениями, без синонимов
  • Проверили каждый термин на омонимы: одно слово — один смысл
  • Создали docs/glossary.md в репозитории проекта
  • Привязали термины к сущностям в коде, таблицам, endpoint’ам
  • Договорились, что на code review проверяется соответствие названий глоссарию
  • Запланировали повторную встречу через 2 недели — дополнить глоссарий по итогам спринта