02 янв. 2025 г.·6 мин чтения

Версионирование языка домена до изменений API

Узнайте, как версионирование языка домена помогает сохранять стабильные бизнес-термины в коде, чтобы изменения партнёров, продукта и API вызывали меньше хаоса.

Версионирование языка домена до изменений API

Почему внешние изменения ломают внутренний код\n\nБольшинство продуктов описывают одну и ту же бизнес-идею разными словами. Продажи говорят «клиент». Биллинг — «аккаунт». Партнёр называет то же самое «подписчиком». Когда разработчики копируют эти внешние ярлыки в код, код начинает отражать каждую беседу, а не сам продукт.\n\nПоначалу это кажется безобидным. Класс называется PartnerAccount, потому что первая интеграция использовала этот термин. Через несколько месяцев маркетинг меняет тексты на сайте на «Организация», а новый партнёр предоставляет документацию с «Workspace». Бизнес почти не изменился, но теперь три имени указывают на одну идею. Люди тратят время, выясняя, означают ли эти имена разное.\n\nВнешний язык меняется быстрее, чем внутренняя логика. Документация партнёров пересматривается. Копирайт продукта переписывается. Команды продаж переименовывают тарифы, чтобы их было проще продавать. Код не должен впитывать каждое такое изменение, но команды часто встраивают эти ярлыки прямо в модели, эндпойнты, перечисления и поля базы данных.\n\nКогда это происходит, каждое переименование распространяется дальше, чем ожидалось. Оно просачивается в имена тестов, админские экраны, события аналитики, шаблоны поддержки и внутреннюю документацию. Даже если поведение остаётся тем же, люди всё равно правят десятки файлов только ради совпадения имён. Работа скучная и всё равно создаёт баги. Одно пропущенное переименование в отчёте или проверке прав может запутать поддержку или сломать тест.\n\nГлубже проблема не в самом переименовании. В кодовой базе отсутствует защищённый внутренний словарь. Когда внешние термины становятся внутренними, система теряет устойчивый центр. Поэтому версионирование языка домена часто важнее, чем версионирование API. Если в коде есть одно надёжное имя для каждой бизнес-концепции, команды могут отображать изменяющие слова партнёров и ярлыки продукта на краях системы, где это уместно.\n\n## Что должно оставаться стабильным внутри кода\n\nНазвания, которые описывают ваш бизнес, должны меняться как можно реже. Если ваша компания продаёт подписки, оставьте такие слова, как «подписка», «продление» и «отмена» неизменными в коде. Вендор может называть то же самое «планом», «ценой» или «контрактом». Ваш код не должен гоняться за этими ярлыками.\n\nЭто важно, потому что внешние термины меняются по причинам, никак не связанным с вашим продуктом. Платежный провайдер переименовал поле. Партнёр поменял имена вебхуков. Команда продаж обновила тексты на сайте. Если ваши внутренние модели отражают эти слова, одно небольшое изменение разойдётся по тестам, отчётам, админским инструментам и заметкам поддержки.\n\nНекоторые команды называют это единым языком предметной области. Сам ярлык менее важен, чем правило: один термин должен нести одно значение повсюду. Если «клиент» означает платящую компанию, не используйте это слово также для конечного пользователя, владельца аккаунта и регистрации на пробный период. Выберите разные слова и держите их раздельно.\n\nКороткие определения помогают больше, чем многие ожидают. Когда слово вызывает хотя бы один спор, запишите простое определение в репозитории. Коротко: «Account: запись компании, которая платит нам.» «User: человек, который входит в систему под аккаунтом.» Маленький глоссарий сокращает путаницу до того, как она дойдёт до кода.\n\nДержите внутренние термины отдельно от имён полей запросов и ответов. Рассматривайте поля API как перевод, а не как истину. Ваш слой на границе может сопоставлять поле провайдера current_price_id с вашим термином, например «активный тариф подписки», а остальная часть приложения останется нетронутой.\n\nПростой тест помогает: если вы смените поставщика в следующем месяце, какие имена должны остаться? Эти имена и должны быть в ваших доменных моделях, сервисных методах, событиях и тестах. Всё остальное должно жить на краях, где изменение дешевле.\n\n## Версионируйте язык раньше, чем версионируете API\n\nБизнес-термины — это продуктовые решения. Если относиться к ним как к случайным ярлыкам, код начнёт копировать каждое переименование от продаж, поддержки или API партнёра. Тогда одно небольшое внешнее изменение будет казаться большим.\n\nВерсионируйте слова первыми. Версионируйте API только когда смысл действительно изменился. В этом суть версионирования языка домена. Переименованное поле не должно требовать новой внутренней модели, если бизнес-идея осталась прежней.\n\nПредположим, долгие годы в продукте использовался термин «customer». Затем биллинг начинает называть того же человека «account owner». Если оба слова означают одну и ту же роль, оставьте одно внутреннее имя в кодовой базе и сопоставьте новый внешний ярлык с ним. Это избегает цепной реакции по сервисам, тестам, документации и дашбордам.\n\nДобавляйте новый термин только когда смысл действительно изменился. Обычно это видно по конкретным признакам: у нового объекта другие правила, им могут обладать другие люди, отчёты считают его иначе или на него влияютцена и права доступа. Если ничего из этого не произошло, скорее всего это переименование, а не новая концепция.\n\nСтарые имена должны продолжать работать какое-то время во время перехода. Это не значит, что их нужно держать навсегда, а значит — дать команде время мигрировать без сбоев в работе. На практике команды часто оставляют старое имя как псевдоним в адаптерах, мапперах запросов или сериализаторах, пока внутренняя модель остаётся стабильной.\n\nВывод старого имени требует даты, а не расплывчатого плана. Отметьте старый термин в коде и документации. Напишите рядом замену, укажите дату удаления и назначьте одного ответственного за очистку. Если этот шаг пропустить, старые и новые имена будут жить рядом годами.\n\nВнимательные команды уже работают так, даже если не называют это «версионированием языка домена». Они в первую очередь защищают смысл бизнеса, а меняют края. Такой порядок экономит время, снижает риски и сохраняет читаемость кода через месяцы.\n\n## Простой метод, который работает\n\nНе нужен тяжёлый процесс. Начните с тех слов, которые команда уже использует в тикетах, демо, чатах поддержки и звонках продаж. Цель не сделать язык звучащим умно. Цель — выбрать имена, которые будут понятны через шесть месяцев, даже если партнёры, поставщики или поля API изменятся.\n\nМаленькая команда обычно справляется за одну рабочую сессию и поддерживает это в ходе обычной продуктовой работы.\n\n1. Запишите существующие сущности и действия, которые появляются каждый день. Сосредоточьтесь на терминах продукта, а не на именах таблиц БД. Если ваш продукт работает с заказами, возвратами, котировками, одобрениями и продлениями — начните с них.\n2. Найдите дубликаты и выберите одно внутреннее имя для каждой идеи. Часто используют два или три слова для одного и того же: customer, client и account. Выберите один термин для кодовой базы и держите остальные в качестве псевдонимов только в заметках или адаптерах.\n3. Сопоставьте каждое внешнее имя с внутренним термином. Платёжный провайдер может присылать payer_id, а ваша команда называть это customerId. Оставьте внешнее поле на краю и переведите его один раз до того, как остальной код с ним поработает.\n4. Добавляйте короткую заметку, когда смысл меняется. Пишите просто: что изменилось, когда это случилось и какое старое имя или правило ещё существует для старых данных.\n5. Проверяйте именования перед выпуском API-обновлений. Если новый эндпойнт заставляет переименовывать внутренную концепцию, остановитесь и спросите почему.\n\nПростой пример иллюстрирует идею. Если один партнёр говорит subscriber, другой — member, а ваша команда имеет в виду paying user, выберите одно внутреннее имя, например subscriber. Затем переводите внешние ярлыки на границе. Одно такое решение сэкономит часы правок позже.\n\n## Небольшой пример из продукта для бронирований\n\nКоманда, работающая над приложением для бронирований, столкнулась с проблемой, которая распространялась по коду. Один и тот же человек появлялся как «guest» на одном экране, как «traveler» в данных бронирования и как «customer» в платежах и инструментах поддержки.\n\nСначала это казалось безобидным. Затем начали появляться баги. Разработчик менял правила для «guest» и забывал, что платёжный поток использует «customer». Скрипт поддержки считал «travelers» и пропускал людей, которые заплатили, но не заполнили форму поездки.\n\nПутаница усугубилась, когда команда добавила второго платёжного партнёра. Один партнёр присылал payer_id. Другой — user_id. Оба поля значили одно и то же: человек, оплативший заказ.\n\nВместо того чтобы пропускать имена партнёров по всему приложению, команда выбрала одно внутреннее имя и придерживалась его. В ядре система стала использовать paying_customer.\n\nНа границе системы payer_id и user_id оба превращались в paying_customer.id. Приложение сохраняло «guest» и «traveler» только там, где они действительно означали участника поездки. Одно такое решение убрало много шума. Ядру было всё равно, какой партнёр прислал событие, ему было важно только, существует ли paying_customer и к какому бронированию относится платёж.\n\nТихая победа пришла позже. Когда один партнёр переименовал поле в обновлении API, команда поменяла только адаптер, оставив остальную систему без изменений. Отчёты остались понятными. Скрипты поддержки продолжали использовать одно внутреннее имя. Новые разработчики читали код без догадок, означают ли «customer», «guest» и «traveler» одного и того же человека.\n\nКоманды часто торопятся с версионированием эндпойнтов. Часто лучшим первым шагом является версионирование слов внутри кода. Если эти слова стабильны, внешние изменения причиняют гораздо меньше вреда.\n\n## Где команды обычно ошибаются\n\nКоманды попадают в ловушку, когда позволяют каждой новой интеграции переименовывать их внутренний мир. Партнёр называет клиента «account», другой — «organization», и вскоре код следует за обоими. Через несколько раундов одна бизнес-концепция получает три имени, и каждое обновление API проливается в модели, тесты, отчёты и заметки поддержки.\n\nДругая, ещё более распространённая проблема: одно слово означает разное для разных людей. Продукт называет «order» покупку пользователя. Инженерия видит в «order» последовательность шагов в рабочем процессе. Поддержка ищет «order» как счёт-фактуру, по которой удобно искать. Команда думает, что договорилась, потому что использует одно слово. На самом деле — нет.\n\nТакая путаница быстро распространяется. Кто-то меняет ярлык в приложении и обновляет несколько классов, но документация всё ещё использует старое имя. Шаблоны поддержки держат старую формулировку. Дашборды аналитики используют третье имя, потому что никто не хочет ломать отчёты. Потом приходит баг-репорт, и три команды тратят полчаса, чтобы понять, говорят ли они вообще об одном и том же.\n\nКоманды также меняют имена слишком резко. Удалили старое имя в понедельник, внесли новое во вторник и ждут, что все сразу подстроятся. Это обычно создаёт больше хаоса, чем исходное переименование. Короткий период перекрытия работает лучше: держите старое и новое имена рядом, отметьте одно как устаревающее и дайте людям время обновить код, документацию и привычки.\n\nХудшие случаи — это отсутствие единого источника правды. Правила именования живут в чатах, комментариях к тикетам и в головах людей. Стартап может решить, что «workspace» теперь значит «client account», но если это решение останется в Slack, пол-репозитория будет всё ещё говорить «workspace» в следующем месяце. Новички учат неправильный термин из старых файлов и дублируют ошибку.\n\nНакопление дрейфа легко заметить на ранних стадиях. Каждая новая интеграция вызывает внутренние переименования. Продукт, инженерия и поддержка по-разному определяют одно и то же существительное. Старые и новые ярлыки живут рядом без правил. Споры по именам решают поиском в чатах вместо чтения одной общей записи.\n\nБольшинству команд не нужен громоздкий процесс. Им нужен один маленький глоссарий, одно чёткое значение на термин и короткий период перекрытия при смене имени.\n\n## Быстрая проверка перед релизом\n\nРелиз — плохое время обнаруживать, что два человека используют одно слово для разных вещей. Десятиминутный обзор может это заметить.\n\nВыберите самого нового участника команды и попросите объяснить каждое основное бизнес-слово простыми словами. Если он сомневается или двое дают разные ответы, термин ещё расплывчат. Эта неясность попадёт в обработчики, таблицы, события и документацию.\n\nДержите предрелизную проверку простой:\n\n- Каждое основное слово соответствует только одному бизнес-значению?\n- Оставили ли вы имена вендоров на границе системы, а не в доменном коде?\n- Записал ли кто-то, что изменилось, когда это случилось и какое старое имя ещё существует в период перекрытия?\n- Если два имени должны жить рядом пару релизов, корректно ли работают чтения, записи, логи и аналитика?\n\nНебольшой пример показывает, почему это важно. Платёжный провайдер переименовал trial_end в grace_period_end. Ваша команда всё ещё говорит о пробном периоде. Хороший код хранит «trial» внутри приложения и переводит поле провайдера в одном адаптере. Плохой код распространяет новое имя провайдера по сервисам, джобам и дашбордам.\n\nМалые команды часто пропускают это, думая, что все уже знают язык. Именно тогда накапливается дрейф. Если один человек может объяснить термин, одна запись фиксирует изменение, а один адаптер обрабатывает внешние имена, то откат и выпуск пройдут безопасно.\n\n## Как стабильный язык помогает всей команде\n\nКогда команды сохраняют одни и те же бизнес-термины внутри кодовой базы, ежедневная работа становится тише. Меньше встреч превращаются в споры об именах. Меньше тикетов перескакивают между поддержкой, продуктом и инженерией из‑за разных значений одного и того же слова.\n\nДокументация становится более надёжной. Если код говорит «customer», документация API и ответы поддержки тоже используют «customer», — люди быстрее понимают систему. Они перестают тратить время на выяснения, означают ли «account», «organization» и «workspace» одно и то же или это три разные вещи.\n\nМалые команды ощущают эффект быстрее. Поддержка пишет более понятные ответы, потому что язык продукта совпадает с кодом. Продажи перестают обещать одно, а продукт показывать другое. Инженеры оценивают работу с меньшей долей догадок, потому что имена не меняются в середине спринта. Рефакторинги становятся меньше, потому что команды сначала меняют адаптеры, полезет и текст UI, а уже потом трогают ядро модели.\n\nПланирование тоже улучшается. Если фича начинается как «team billing», а потом превращается в «organization billing», оценки обычно ломаются по простой причине: никто не понимает, к какому объекту это относится. Один разработчик думает, что это принадлежит группе пользователей, другой — что это юридическое лицо, а поддержка считает, что это сопоставимо с платным планом. Код следует этой путанице.\n\nСтабильный внутренний язык решает проблему на ранней стадии. Вы можете переименовывать поля на краю, сопоставлять старые термины с новыми и держать бизнес-модель стабильной внутри приложения.\n\nЭто особенно важно в маленьких продуктовых командах, где один человек за день делает продукт, поддержку и доставку. Если команда выберет одно имя и защитит его, передачи станут проще. Бэклог читается лучше. Документация API перестанет уходить от продукта. Когда изменения приходят, они попадают сначала на границу, а не раскалывают всю кодовую базу.\n\n## Следующие шаги для маленькой команды\n\nВам не нужен полный рефакторинг, чтобы это заработало. Выберите один поток, который уже вызывает путаницу, и приведите в порядок язык вокруг него. Регистрация, биллинг и заказы — хорошее место для начала, потому что мелкие ошибки в именах там быстро распространяются в поддержку, документацию и API.\n\nИспользуйте практическое правило: исправляйте те термины, которые отнимают у вас время каждую неделю. Если продукт говорит «customer», поддержка — «account», а код — «user», люди тратят энергию на перевод вместо того, чтобы строить. Выберите одно внутреннее имя, держите его стабильным и переводите внешние ярлыки на границе.\n\nХороший первый проход прост: выберите один проблемный поток, перечислите слова, используемые в продукте, коде, API и БД, затем выберите одно внутреннее имя для каждой бизнес-концепции. Переименуйте самые болезненные конфликты сначала и запишите сопоставления в короткой заметке для команды. Этого достаточно, чтобы начать. Цель не в идеальном именовании в первый день — цель — остановить появление новой путаницы с каждым новым фичером.\n\nМалые команды получают наибольшую выгоду, когда проверка имен становится частью планирования, а не уборкой, которую никто не планирует. Когда продукт и инженерия обсуждают фичу, потратьте пару минут на три вопроса: как это называется в бизнесе, как это называется в коде и что увидят внешние системы? Эта короткая проверка может предотвратить недели дрейфа.\n\nБиллинг часто демонстрирует проблему особенно ясно. Команда может использовать «plan», «subscription» и «contract» для одной и той же идеи в зависимости от того, кто писал фичу. Потом приходит изменение API, и все спорят о смысле, прежде чем кто-то тронет код. Стабильные внутренние имена делают такие изменения гораздо дешевле.\n\nЕсли после первого прохода сопоставление всё ещё кажется спутанным, короткий обзор с Олегом Сотниковым может помочь распутать язык домена, границы API и более широкие CTO-решения по ним. Внешний взгляд часто быстрее, чем ещё месяц исправления имён по одному.\n\nНачните с малого, держите внутренние слова стабильными и защищайте их при каждом изменении продукта.

Часто задаваемые вопросы

Что означает версионирование языка домена?

Это значит: внутри кода вы храните по одному устойчивому имени для каждой бизнес-концепции, даже если партнёры, поставщики или тексты интерфейса используют разные слова. Смысл версионируют аккуратно, а внешние ярлыки переводят на границе системы.

Зачем это делать до версионирования API?

Потому что формулировки меняются чаще, чем бизнес-правила. Если сначала защитить внутренние термины, переименование провайдера или правка копирайта останется в адаптере, а не распространится по моделям, тестам, отчётам и документации.

Как отличить переименование от реально новой сущности?

Спросите, появилось ли у сущности новое поведение: другие правила, иные права доступа, отличающийся учёт в отчётах или иная цена. Если ничего этого не изменилось, скорее всего это переименование, а не новая концепция.

Где должны жить термины поставщиков и партнёров?

На границе системы. Сопоставляйте поля типа payer_id или current_price_id один раз в маппере запросов, сериализаторе или адаптере, а в остальной части приложения используйте свои доменные термины.

Как маленькая команда может начать без большого процесса?

Начните с одного запутанного потока: биллинг, регистрация или заказы подойдут. Запишите используемые слова, выберите одно внутреннее имя для каждой бизнес-идеи и положите короткое определение в репозиторий.

Как долго хранить старые имена при переименовании?

Держите старое и новое имя рядом на короткий, запланированный период. Поставьте дату удаления для старого ярлыка и назначьте ответственного, чтобы оба термина не висели годами.

Что делать, если одно слово означает разное для разных команд?

Остановитесь и опишите каждое значение простыми словами. Если продукт, поддержка и инженерия используют одно слово для разных вещей, выберите разные термины и применяйте их последовательно в коде и документации.

Это действительно упростит повседневную работу?

Да. Устойчивые термины сокращают количество шумных переименований: имена в тестах, дашбордах, админках и макросах поддержки меньше расходятся. Новым сотрудникам читать код проще, когда одна концепция имеет одно имя.

Что стоит проверить перед выпуском?

Короткая проверка имён перед релизом. Попросите новичка объяснить ключевые бизнес-термины простыми словами; если он сомневается или ответы различаются, исправьте формулировки до релиза.

Когда стоит попросить внешнего CTO посмотреть?

Привлекайте внешнего CTO, когда та же проблема с именами постоянно возвращается через продукт, API и поддержку. Короткий внешний обзор часто быстрее и эффективнее чем месяц поштучного патчинга терминов.