24 авг. 2025 г.·7 мин чтения

Общее ядро или копирование кода: как выбрать более дешёвый путь

Общее ядро против копирования кода кажется мелким решением, но определяет затраты, задержки и трение в команде. Узнайте, когда подходит каждый вариант.

Общее ядро или копирование кода: как выбрать более дешёвый путь

Почему этот выбор становится дорогим

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

Проблема начинается, когда правила перестают двигаться вместе. Возможно, сегодня обе команды соглашаются, что значит «approved account». Через шесть месяцев одна команда добавляет период ожидания, а другой важна только история оплат. Имя осталось прежним. Работа вокруг этого имени — нет.

С этого момента одно решение замедляет обе команды. Малое изменение требует обсуждения, дополнительного тестирования и осторожности, потому что никто не хочет сломать другую сторону. Код общий, значит и решение общее.

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

Поэтому вопрос «общее ядро или копирование кода» — редко про аккуратность. Он про скорость изменений. Если два домена будут развиваться с разной скоростью, общий код часто становится более дорогим вариантом, даже если сейчас он убирает дублирование.

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

Что действительно означает общее ядро

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

Это соглашение важнее самого кода. Если одна команда понимает понятие иначе, чем другая, общее ядро превращает это расхождение в постоянное трение. Когда всё работает, обе команды могут ссылаться на одно определение и доверять ему.

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

Тесты важны не меньше кода. Если команды делят код, но не делят тесты, это не настоящее общее ядро — это совместный риск. Хорошие общие тесты делают правила видимыми и предотвращают случайное ломание одной стороны другой.

Большинство команд ошибаются по части размера. Общее ядро должно оставаться узким, почти скучным. Подумайте о паре основных правил, нескольких value-объектах, может быть одном валидаторе. Как только туда попадают хелперы, логика рабочих процессов или исключения, специфичные для команды, это перестаёт быть чистым соглашением и превращается в скрытое сцепление.

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

Что действительно означает копирование кода

Копирование кода не означает, что команда отказалась от дизайна. Это значит, что каждый домен владеет своей версией концепта и может менять её локально. Имена могут начинаться одинаково, но поведение редко остаётся одинаковым надолго.

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

Поэтому дублирование не всегда означает переплаты. Небольшое дублирование может стоить меньше, чем постоянные переговоры. Если одна команда меняет концепт каждую неделю, а другая — раз в квартал, заставлять их согласовывать каждое обновление будет дороже, чем локальная копия. Встречи, ревью и компромиссы — это реальные издержки.

За этим выбором всё равно есть цена. Исправление бага может потребовать двух релизов. Тесты могут раздвоиться. Правило может измениться в одном домене и остаться замороженным в другом. Такая дрейфовая разница не автоматически плоха. Проблемой она становится, когда команды ожидают единства после выбора независимости.

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

Начните со скорости изменений

Когда два домена используют один и тот же концепт, сначала проверьте, как быстро каждая сторона его изменяет. Это скажет больше, чем аккуратная диаграмма.

Начните с фактов за последние несколько месяцев. Считайте правки, а не мнения. Если одна команда трогала концепт 12 раз, а другая — 2, они не живут в одном ритме.

Быстрая проверка даёт достаточно сигнала. Как часто каждая команда меняет правила, поля или имена? Кто просит срочных фиксов, когда концепт ломается? Одна сторона выпускает ежедневно, другая — раз в месяц? Когда происходят изменения, нужны ли они обеим сторонам или только одной?

Сроки релизов важнее, чем многие ожидают. Если продуктовый код меняется каждую неделю, а финансовый — раз в квартал, общий код может превратить малое обновление в задачу по координации. Теперь обе стороны должны просмотреть изменение, вписать его в свой релизный цикл и разбираться с версионным дрейфом.

Копирование кода часто выигрывает, когда одна сторона меняется гораздо чаще. Вы платите небольшую первоначальную цену, а затем каждая команда движется по своему графику. Часто это дешевле, чем постоянные встречи, блокированные релизы и неожиданные поломки.

Возьмём простой пример «статус клиента». Команда роста может добавлять статусы для триалов, апселов и реактиваций каждые несколько недель. Биллингу может быть важно только «billable» и «not billable». При общем модели медленная сторона наследует ненужную текучку.

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

Сравните реальные издержки

Получить техническую помощь для основателя
Технические советы для основателей по архитектуре продукта без найма постоянного CTO.

Большинство команд оценивает выбор, считая только часы программирования. Это пропускает часть, которая обычно больнее: координацию.

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

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

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

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

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

Дешёвый код может создать дорогую координацию. Пара сотен скопированных строк может стоить меньше месяцев долгов по ревью, ожиданий релизов и низкоуровневого трения в командах. Считайте календарное время и внимание, а не только строки кода.

Простая дорожная карта принятия решения

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

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

  1. Напишите концепт в одном простом предложении. Например: «Статус клиента показывает, может ли аккаунт покупать, получать поддержку или доступ к премиум‑функциям.»
  2. Перечислите правила, которые обе стороны сейчас разделяют. Используйте факты, а не догадки.
  3. Отметьте правила, которые уже различаются. Это важнее, чем совпадающие имена полей.
  4. Оцените, как часто правила будут меняться вместе.
  5. Выбирайте общий код только когда общие правила выглядят стабильными на некоторое время. Если правила скоро разойдутся, копируйте код и дайте каждой стороне двигаться в своём темпе.

Небольшой пример делает это конкретным. Представьте, что продуктовая команда и биллинг используют «статус клиента». Сначала они делят три правила. Через два месяца биллинг добавляет «past due», а продуктовая команда — «grace period». Если вы рано навязали общее ядро, теперь каждое изменение требует координации, ревью и согласования релизов между командами.

Именно поэтому решение чаще связано не с элегантностью, а с моделями изменений. Стабильные общие правила могут жить в одном месте. Правила, которые расходятся под давлением, должны расходиться и в коде.

Запишите общие правила, отметьте различия и посчитайте ожидаемое число совместных изменений на следующие шесть месяцев. Чаще всего это делает более дешёвый вариант очевидным.

Простой пример со статусом клиента

Когда две команды говорят «статус клиента», они часто имеют в виду разное. Продажи могут использовать статус, чтобы решить, кто может купить, продлить или требует работы с биллингом. Саппорт — чтобы понять, как обрабатывать тикет сейчас.

Продажи могут держать три состояния: trial, active и past due. Саппорт может иметь VIP, blocked и waiting on finance. Метка звучит одинаково, но назначение разное.

Здесь команды и попадают в ловушку. Они создают одну общую модель статуса, потому что это кажется аккуратно. Через месяц модель растёт дополнительными полями и флагами, чтобы удовлетворить потребности обеих сторон. Продажи хотят состояния биллинга и правил продления. Саппорт — правил сервиса и приоритетов в очереди. В общую модель добавляются исключения для всех.

Вскоре никто не может изменить статус без проверки у двух команд, двух тестовых наборов и кучи пограничных случаев. Обновление продаж ломает экраны саппорта. Правило саппорта путает отчётность. Модель становится больше, но система от этого не чище.

Две локальные модели часто дешевле. Продажи держат свою модель статуса. Саппорт — свою. Когда нужно общаться, используют небольшое сопоставление на основе общих фактов, например неоплаченные счета, блоки аккаунта или уровень поддержки.

Реальный поток может выглядеть так: продажи пометили аккаунт как past due из‑за сбоя оплаты. Саппорту не нужен новый общий статус для этого. Он может сопоставить «past due» и «высокий контрактный уровень» в «waiting on finance» и при этом считать клиента VIP для срочных вопросов.

Такой разрыв обходится дешевле, когда два смысла меняются с разной скоростью. Делитесь только фактами, которые обе команды действительно владеют. Пусть каждая команда даёт своё имя статусу.

Как держать выбор под контролем

Привлечь Fractional CTO
Привлеките старшего специалиста по архитектуре, владению и релизным компромиссам.

Первая версия обычно выглядит нормально. Проблемы появляются позже, когда один домен меняется каждую неделю, а другой почти не двигается. Тогда общий код начинает тянуть команды вместе, а копии кода — дрейфовать так, что никто это не отслеживает.

Если вы держите общее ядро, делайте его скучным. Класть туда только стабильные правила: общие термины, пара валидаций, небольшие value‑объекты. Оставляйте за кадром быстро меняющиеся workflow, feature‑флаги и дедлайн‑исключения. Общий код должен меняться редко, иначе он станет пробкой.

Назначьте для этого кода одного явного владельца. Не группу в чате и не две команды, которые по очереди. Один человек или одна команда решает, что добавлять, что оставлять и когда изменение требует новой версии. Это одно правило существенно сокращает потери времени.

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

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

Команды попадают в беду, пропуская такую уборку. Временный переключатель остаётся навсегда. Старый флаг никогда не удаляют. Скоро маленькое решение превращается в скрытое сцепление, и простое изменение требует нескольких pull request'ов и дополнительного тестирования.

Контроль приходит от простых привычек: держите центр маленьким, назначьте владельца, фиксируйте происхождение копий и рано убирайте старые пути.

Ошибки, которые создают скрытое сцепление

Команды часто создают скрытое сцепление по простой причине: два понятия выглядят похоже на бумаге, поэтому их считают одним и тем же в коде. Совпадающие названия недостаточны. Одна команда может использовать «account status» для биллинга, другая — для правил поддержки. Ярлык совпадает. Смысл — нет.

Эта ошибка становится дорогой, когда общий код внедряют слишком рано. Небольшой общий тип превращается в общую книгу правил, затем в общую валидацию, затем в общий риск релизов. Вскоре одна команда не может поменять поле или правило без согласования с другой.

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

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

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

Координация команд имеет реальную цену, даже если никто не ставит её в бюджет. Встречи, ревью, чаты и ожидание ответов складываются. Общая модель, которая экономит 30 строк кода, может легко стоить часов каждую неделю.

Короткая проверка здравого смысла помогает:

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

Если на «нет» отвечает более одного вопроса, копирование кода обычно дешевле.

Быстрая проверка перед коммитом

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

Команды обычно жалеют о выборе, когда поздно отвечают на один практический вопрос: кто вынужден ждать, когда правило меняют? Эта задержка часто дороже самого кода.

Проведите короткую проверку с владельцами доменов. Если ответы кажутся вынужденными или расплывчатыми, остановитесь и копните глубже. Эта неуверенность уже часть стоимости.

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

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

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

Запишите решение в одном предложении. Укажите, кто владеет концептом, как часто вы ожидаете изменения и что заставит вас пересмотреть выбор позже.

Следующие шаги для вашей команды

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

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

В течение одного‑двух релизов отслеживайте, кто запрашивал каждое изменение, какие команды координировались, в скольких местах менялся код, кто ждал и какие правила разошлись.

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

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

Это как раз те проблемы, с которыми работает Oleg Sotnikov через oleg.is как Fractional CTO и советник для стартапов. Он помогает стартапам и малому бизнесу разобраться с границами доменов, владением и архитектурными решениями до того, как неправильная модель затвердеет в коде.

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

Что такое общее ядро простыми словами?

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

Оно работает только тогда, когда концепт остаётся стабильным и команды могут согласовывать изменения без больших задержек.

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

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

Это даёт каждой команде пространство для изменений без ожидания одобрения другой стороны.

Как понять, действительно ли две команды имеют в виду одно и то же?

Не обращайте внимания на название — сравните правила. Спросите, какие решения формирует концепт, какие поля важны и какие пограничные случаи обрабатывает каждая команда.

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

Является ли дублирование кода всегда плохим решением?

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

Плохой дизайн начинается, когда команды притворяются, что разные смыслы — это одна общая модель только потому, что код выглядит аккуратно.

Что должно оставаться внутри общего ядра?

Держите в общем ядре только стабильные правила. Небольшие value-объекты, общие термины и простая валидация обычно подходят.

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

Насколько небольшим должно быть общее ядро?

Делайте его настолько маленьким, чтобы обе команды могли объяснить суть несколькими предложениями. Если в него начали попадать вспомогательные функции, процессная логика или множество спецслучаев, оно стало слишком большим.

Узкая центральная часть легче доверяема и проще изменяется.

Что делать, если одна команда релизит еженедельно, а другая — ежемесячно?

Разные ритмы релизов часто делают общий код дороже. Быстрая команда в итоге ждёт, или медленная вынуждена принимать ненужные изменения.

Если одна группа выпускает раз в неделю, а другая — раз в месяц, копирование кода обычно причиняет меньше проблем.

Как две локальные модели могут общаться без общего статуса?

Пусть каждая команда хранит свою модель, а между ними передаётся только набор доверенных фактов. Затем добавьте небольшое сопоставление между этими фактами и локальным статусом каждой команды.

Например, биллинг может передавать факты "неоплаченный счёт" или "блок аккаунта", а саппорт отображает их в свои статусы.

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

Относитесь к каждой копии как к форку с записью о происхождении. Зафиксируйте, откуда она взята и кто теперь ею владеет.

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

Когда стоит просить внешнее архитектурное ревью?

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

Fractional CTO или консультант по архитектуре помогут пересмотреть домены, убрать скрытое сцепление и выбрать более чистый разрез до того, как неправильная модель закрепится в коде.