REUSE — конфлюэнтность и переиспользование
Один закон, работающий на всех уровнях гранулярности: то же, что делает хорошим коммит, делает хорошим модуль, компонент, движок и продукт. BM (BMFP / BMBP / BMAP) описывает один продукт изнутри. Этот документ — как единицы переиспользуются между продуктами и как это удерживается в git.
5 минутОдин закон, работающий на всех уровнях гранулярности: то же, что делает хорошим коммит, делает хорошим модуль, компонент, движок и продукт. BM (BMFP / BMBP / BMAP) описывает один продукт изнутри. Этот документ — как единицы переиспользуются между продуктами и как это удерживается в git.
Конфлюэнтная единица
Единица любого размера конфлюэнтна, если держит четыре свойства одновременно:
| Свойство | Что значит |
|---|---|
| Цельность | одна ответственность; ничего лишнего, ничего недостающего |
| Самодостаточность | имеет свою границу (контракт), собирается и проверяется сама, не втягивает соседей внутрь |
| Неразрушение | не ломает рабочее состояние соседей и прошлого; добавляется, не подламывая существующее |
| Однонаправленность | потребители зависят от неё; она не зависит от того, кто выше неё |
Детектор костыля: если внутри единицы появляется знание о конкретном потребителе
(if (хост == X)), свойство самодостаточности нарушено — знание выносится наружу, в потребителя.
Уровни — одно правило, разная гранулярность
| Уровень | Единица | Контракт (граница) | Где живёт |
|---|---|---|---|
| Коммит | связное изменение | сообщение + рабочее дерево | история репо |
| Модуль / функция | слой shared | сигнатуры | внутри пакета (BM) |
| Компонент / виджет | UI-кусок | props / события | пакет, алиас в сборку |
| Движок | host-agnostic ядро | публичный API | отдельный репо |
| Сервис | доменная зона | конверт { status, data } | отдельный бэк (BMBP) |
| Продукт | сборка единиц | поставка | отдельный репо (BMAP) |
Четыре свойства из предыдущего раздела одинаковы на каждой строке. Меняется только размер единицы и форма её контракта. Поэтому правило не нужно учить заново на каждом уровне — оно одно.
Движок и оболочка
Внутри продукта единица расслаивается по знанию о хосте:
- Движок — host-agnostic ядро (чистая логика + рендер), не знает, где исполняется.
- Оболочка — тонкая привязка движка к конкретному хосту (ОС, чужая ОС, киоск, сайт).
Переиспользуется движок; оболочка остаётся в продукте. Это та же однонаправленность слоёв из BM, но на оси «знание о хосте»: движок ← оболочка, не наоборот.
Жизненный цикл выделения
Корень — расширяемость: модульность нужна, чтобы система росла; переиспользование — её побочный приз. Выделение не проектируется заранее — оно вызревает:
-
Рождение в месте нужды
Способность живёт внутри продукта, который первым её потребовал. Выносить раньше — преждевременная абстракция, тот же костыль наоборот.
-
Перерос место — выделение
Способность выделяется, когда переросла своё место: либо нужна второму потребителю, либо выросла в самостоятельную часть, которой нужен свой пайплайн/жизненный цикл. Тогда она переезжает в свою единицу за границами продукта — свой репо/папка, своя история, своя обвязка (манифест зависимостей, тесты, README), свой контракт.
-
Потребление по зависимости
Оба продукта подключают её как зависимость, закреплённую версией. Никто не копирует и не форкает.
Триггер выделения — реальная потребность (второй потребитель или собственный размер/пайплайн), не предполагаемая. Не раньше — но и не держим силой, когда единица уже переросла место.
Топология
- Продуктовый репо содержит сборку и оболочки (всё хостовое) и зависит от shared-единиц.
- Shared-единица — сосед продуктов, а не их ребёнок. Её канонический репозиторий, история и
владение находятся вне продукта. Локальный checkout зависимости может быть смонтирован в
канонический слот продукта (например,
frontend/modules/<module>BMAP) через workspace или submodule, но это не меняет владение и не превращает модуль в дочерний репозиторий продукта. - Зависимости между репозиториями однонаправленны, как слои внутри BM: продукт → shared → фундамент. Shared не знает о продукте. Циклов нет.
- Подключение — менеджером пакетов / workspace / submodule, с закреплённой версией. Контракт стабилен и версионируется: внутренние изменения единицы не ломают потребителя (semver по смыслу).
Git
- Всё под git с первого файла. Кода вне git нет — ни у продукта, ни у shared-единицы.
- Local-first. Git работает полностью локально; удалённый репозиторий — синхронизация и бэкап, не условие работы.
- Граница репо = граница единицы. Выделили способность — она получает свой репо и свою историю, а не размазана по чужой.
Локальная разработка shared-единицы и продукта идёт в одном workspace: весь исходный код уже на диске, IDE видит обе границы, а сборка не требует сети. Универсальное изменение коммитится в репозиторий shared-единицы, после чего продукт обновляет закреплённую версию. Специфичное для продукта знание остаётся в его оболочке. Постоянные ветки и отдельные копии shared-кода под потребителей не создаются.
Коммит — наименьшая конфлюэнтная единица
Те же четыре свойства, спроецированные на коммит:
| Свойство | На коммите |
|---|---|
| Цельность | одна подсистема = один коммит; часто и гранулярно |
| Самодостаточность | собирается и проходит проверки сам по себе |
| Неразрушение | дерево рабочее на каждом коммите (история bisectable); прошлое не переписывается деструктивно |
| Однонаправленность | опирается на предыдущие коммиты, не требует будущих |
- Сообщение — сухое,
type(scope): что. Факт, без пафоса. - Автор — человек; без авто-атрибуции инструментов.
Словарь
- Конфлюэнтность — свойство единицы быть цельной, самодостаточной, неразрушающей и однонаправленно-зависимой; одинаково на всех уровнях.
- Единица — носитель конфлюэнтности любого размера: коммит / модуль / компонент / движок / сервис / продукт.
- Движок / оболочка — host-agnostic ядро и тонкая привязка к хосту; переиспользуется ядро.
- Выделение (graduation) — переезд способности из продукта в собственную shared-единицу при появлении второго потребителя.
- Shared-единица — самодостаточный переиспользуемый пакет за границами продуктов, со своим контрактом, историей и обвязкой.
- Контракт единицы — её публичная форма (props/события, API, конверт); внутренности скрыты, потребитель зависит только от формы.
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.