almdev Технический блог

Доменный скилл: правила предметной области в файле
Знание, которого нет в коде, — единственное, ради чего стоит писать скиллы.

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

Подкатегория: Скиллы

Чтение
5 мин
Технологии / версии
Symfony · Coding agent · Laravel · Скиллы
Серия
Цикл «Скиллы» · часть 4 из 6
14 июл 2026 · 5 мин · 2 просмотра · AI
Правила предметной области, записанные рядом со схемой данных
Скиллы 07/2026

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

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

Скилл про данные, а не про код

Доменный скилл отвечает не на вопрос «как здесь пишут код», а на вопрос «как устроены данные и что в них нельзя сломать».

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

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

Каталог хостингов: стабильный slug и счётчик

Первый разбор — каталог провайдеров хостинга. Модель простая: название, слаг, описание, категория, реферальная ссылка, цена от, флаг активности, порядок, счётчик кликов.

Из этого списка два поля особенные, и скилл про них говорит прямо:

- slug уникален и стабилен; не менять у живого провайдера без причины
  (ломает закладки и историю кликов);
- clicks_count инкрементируется только через RedirectController;
- ссылки наружу на публичной странице — только через route('go', slug),
  не напрямую на referral_url;
- категории — только ключи из HostingProvider::CATEGORIES.

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

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

Каталог решений: публичные адреса

Второй проект — каталог типовых решений для бизнеса. Сущностей больше: карточка решения, блоки детальной страницы, бизнес-категории, заявки интереса.

Инварианты похожи по типу, но другие по сути:

- уникальные slug; опубликованный URL не ломается без редиректа;
- maturity_status — из закрытого списка mvp / ready / production;
- тексты карточек проходят отдельную редакционную проверку;
- сидеры идемпотентны: updateOrCreate по slug.

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

Конвейер фото: цепочка, у которой есть порядок

Третий случай сложнее двух предыдущих. Загрузка фотографии проходит через форму, конвертацию HEIC в JPEG, хранилище, генерацию миниатюр, чтение EXIF, вычисление размытой заглушки, геокодирование.

Скилл здесь нужен не ради инвариантов данных, а ради порядка и границ:

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

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

Обратите внимание, чего в скилле нет. В нём нет объяснения, зачем нужны миниатюры и как работает размытая заглушка. Это видно из кода и не нуждается в записи.

Что обязательно описать

Из трёх разобранных скиллов складывается один и тот же скелет.

Источники правды. Где лежит сущность, где сидер, где админка, где публичный вывод. Четыре-пять строк со ссылками на файлы. Без этого раздела работа начинается с поиска, а поиск находит не всё: сидер обычно не находят.

Инварианты. Что нельзя сломать и почему. Обязательно с последствием: «не менять слаг» без объяснения читается как каприз, «ломает закладки и историю кликов» — как инженерное ограничение.

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

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

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

Почему доменный ценнее общего

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

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

Есть и вторая причина, менее очевидная. Общее знание про Laravel и Symfony у любого исполнителя и так есть — оно вычитано из тысяч проектов. Знание про то, что на проде развёртывание пересоздаёт базу целиком и источник контента только сидеры, есть в одном месте: в моём скилле. Отсутствие первого восполняется само, отсутствие второго — нет.

Где скилл заканчивается

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

В скилл идёт то, что нужно, чтобы безопасно менять код. Не идёт: зачем этот продукт нужен, кто им пользуется, какие планы по развитию, история решений. Всё это живёт в docs/ и читается людьми.

Проверка — вопрос «изменит ли этот абзац хоть одну строку в диффе». Абзац про то, что каталог хостингов приносит партнёрский доход, не меняет ни строки. Строка про то, что переходы считаются только через один контроллер, меняет.

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

Что осталось

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

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

Серия

Цикл «Скиллы»

#symfony #coding-agent #laravel #skills #domen