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

Почему ассистенты ошибаются в запутанных репозиториях
Ассистенты не читают кодовую базу так, как это делает старший инженер. Они смотрят на файлы, ближайшие к задаче, копируют соседние паттерны и продолжают. Если эти соседние файлы смешивают несколько обязанностей, ассистент перенимает эту путаницу.
Именно поэтому грязный репо так быстро вредит точности правок. Модель видит то, что близко, а не то, что правильно. Изменение, которое должно затрагивать одно правило ценообразования, может подтянуть старую форму API, неправильную утилиту или тест из другого потока.
Смешанные обязанности усугубляют проблему. Когда логика биллинга сидит рядом с UI‑хелперами, фоновыми задачами и правилами только для админов, у ассистента нет чистой грани, за которую можно было бы зацепиться. Он начинает угадывать. И это предположение может выглядеть разумно на ревью, поэтому такие ошибки проходят незамеченными.
Общие папки создают другую проблему. Названия вроде common, shared или utils звучат аккуратно, но часто скрывают бизнес‑правила, которые принадлежат одной части продукта. Правило скидки для инвойсов оказывается рядом с форматированием дат, хелперами аутентификации и кодом повторных попыток. Ассистент затем трактует всё это как равноправно переиспользуемое, даже если какой‑то файл имеет смысл только в узком контексте.
Небольшие подсказки могут вылиться далеко за пределы того, что вы собирались менять. Попросите обновить отмену заказа, и ассистент может за один проход править экраны чекаута, джобы возврата, обработчики вебхуков и админ‑отчёты, потому что они все упоминают одни и те же поля. Репозиторий не дал границ, и он пересёк сразу несколько зон.
Люди могут остановиться и спросить: "Должно ли это вообще жить здесь?" Ассистент обычно этого не делает. Он следует локальным признакам. Если подсказки исходят от старого распределения владения, привычек папок или лет быстрых фиксов, вы получите код, который выглядит аккуратно рядом, но ломает большую систему.
Чёткие границы сокращают эту путаницу. Они показывают ассистенту, где правило начинается, где заканчивается и какая часть системы им владеет. Когда эта грань очевидна, предложения становятся меньше, импорты — чище, а правки остаются внутри нужного сервиса.
Как выглядит полезная граница
Хорошая граница ощущается немного скучно. Открыл модуль — и сразу понятно, какие правила там живут, какие данные он может менять и какой сервис его доставляет. Такая ясность мешает ассистенту угадывать.
На практике одно бизнес‑правило должно иметь один дом. Если правила скидок живут в checkout, ассистент не должен находить половину из них в админ‑формах и другую половину в общей папке. Когда правило меняется, меняется сначала один модуль. Другие модули вызывают его через небольшую публичную поверхность.
Пути записи важнее путей чтения. Читать историю заказов из трёх мест — это неудобно, но позволить трём местам создавать возвраты или обновлять статус инвойса — вот где начинается плохие предложения. Держите код, который пишет данные, рядом с кодом, который владеет этой частью бизнеса. Тогда ассистент видит один источник истины вместо нескольких конкурирующих паттернов.
Небольшие интерфейсы помогают по той же причине. Модуль должен экспортировать несколько простых функций, событий или API‑хендлеров. Он не должен выставлять внутренние таблицы, хелперы и побочные эффекты. Ассистенты могут следовать короткому контракту. Они теряются, когда каждый модуль тянет руку через стену и трогает приватный код.
Полезная граница также делает границы деплоя очевидными. Вы должны быстро ответить на несколько вопросов:
- Где начинается этот сервис?
- Какой код собирается и шипится с ним?
- Какой модуль владеет записями в этих данных?
- Что другие модули могут вызывать без лезть во внутренности?
Возьмите биллинг и админку. Админ может показывать инвойсы и запускать одобренные действия, но биллинг должен владеть логикой начислений, правилами возвратов и записью в книгу. Если биллинг деплоится как отдельный сервис, репозиторий должен это показать в одном очевидном месте. Его точка входа, конфиги, тесты и файлы релиза должны быть рядом с модулем, а не разбросаны по дереву.
Если новый инженер или ассистент может предсказать, где принадлежит изменение, прежде чем использовать поиск, граница, вероятно, выполняет свою задачу.
Разделяйте по правилам домена, а не по тому, кто сейчас владеет папкой
Команды меняются быстрее, чем бизнес‑правила. Репо должно следовать продукту, а не текущей орг‑схеме.
Если сегодня одну команду отвечает за checkout, а вскоре другая — за восстановление платежей, код не должен требовать крупного перемещения. Когда папки отражают ведомости по людям, каждая реорганизация превращается в переработку репозитория. Это создаёт шум, сломанные импорты и устаревший контекст для ассистента.
Лучшее разделение держит одну бизнес‑идею в одном месте. Логика checkout должна быть вместе, даже если две команды касаются её каждую неделю. Суммы в корзине, обработка налогов, повторные попытки платежей и правила подтверждения заказа должны жить рядом, только если они отвечают на один и тот же продуктовый вопрос. Если они относятся к разным вопросам — разделяйте.
Правила ценообразования требуют той же дисциплины. Держите их подальше от инструментов отправки писем, админ‑экранов или скриптов поддержки, даже если эти части читают данные о ценах. Код отправки писем должен отправлять сообщения. Админ — управлять действиями сотрудников и настройками. Ценообразование — решать, сколько стоит товар, когда применяются скидки и какой план может купить клиент.
Если смешивать эти области, ассистенты переносят логику не туда. Проверка скидки, написанная для биллинга, может оказаться в джобе отправки писем, потому что репо подсказывает, что они связаны.
Общие хелперы создают ещё одну проблему, когда команды выносят код слишком рано. Зона shared превращается в ящик с одноразовыми функциями с расплывчатыми именами. Подождите, пока два модуля не будут нуждаться в одном и том же хелпере по одной и той же причине. Если checkout и billing оба нормализуют валюту одинаково — вынесите. Если один модуль форматирует цену для счёта, а другой — для промо‑письма, держите их раздельно, пока перекрытие не станет реальным.
Есть простой тест‑запах. Если имя папки совпадает с именем команды, названием собрания или сферой менеджера, она, вероятно, быстро состарится. Если оно соответствует бизнес‑правилу — скорее всего проживёт дольше.
Эта стабильность важна. Люди переходят, команды дробятся. Продукт всё ещё нуждается в одном месте, где правила checkout имеют смысл.
Сопоставляйте модули с путями деплоя
Кладите код туда, где он шипится. Когда сервис деплоится самостоятельно, у него должен быть свой модуль, конфиг и тесты рядом. Это держит изменения локальными и даёт ассистенту меньшую область для чтения перед тем, как предлагать правки.
Границы становятся гораздо яснее, когда они выровнены с деплоем. Если API, воркер и админ‑приложение релизятся по разным расписаниям, они не должны жить в одном смешанном модуле только потому, что все затрагивают одну продуктовую зону. Общая библиотека может иметь смысл, но шипящий код и общий код — не одно и то же.
Обычная путаница выглядит так: одна папка содержит логику приложения, миграции БД, обработчики джобов, env‑файлы и тест‑файxtures для трёх разных сервисов. Человеку потребуется несколько дней, чтобы распутать это. Ассистенту обычно нет. Он читает миграцию для одного сервиса, конфиг для другого и предлагает изменение, ломающее оба.
Чище устроенный репо держит каждый целевой деплой самообъёмным. Код сервиса должен жить с файлами, которые контролируют его релиз. Миграции — рядом с базой, которую использует сервис. Сервисные тесты — рядом с кодом, который они проверяют. Общие хелперы выносятся в отдельную библиотеку только тогда, когда больше одного сервиса действительно её использует.
Последний пункт важен. Команды часто вытаскивают код в общий пакет слишком рано. Тогда пакет становится ящиком с мусором, и каждый ассистент начинает считать, что несвязанный код принадлежит вместе. Если правило сегодня использует только один сервис, держите его там.
Кросс‑сервисные вызовы требуют той же дисциплины. Поместите их в одно очевидное место, например, в клиент или модуль интеграции внутри вызывающего сервиса. Не разбросывайте HTTP‑вызовы, очередь сообщений и правила повторных попыток по случайным файлам. Когда эти вызовы живут в одном месте, ассистент может проследить, что происходит между сервисами, не догадываясь.
Подумайте о том, что вы деплоите во вторник в 16:00. Репо должно делать этот ответ скучным. Если релиз затрагивает API‑сервис, его конфиг, миграцию и тесты, всё это должно быть в одном понятном доме.
Простой способ перерисовать репо
Начните с пользовательских действий, а не с папок. Если ваш продукт позволяет людям регистрироваться, оплачивать счёта, запрашивать возвраты или экспортировать данные, выпишите эти действия в первую очередь. Это даст вам более чистую карту, чем то, что выросло из старых командных привычек.
Затем сопоставьте каждое действие с кодом, который реально его шипит. Возврат может затронуть веб‑хендлер, биллинговый сервис, джоб отправки писем и запись в книгу. Если одна подсказка постоянно тянет в себя отчётность, админ‑экраны и старые скрипты, это знак, что граница слишком широкая или размытая.
Не нужен грандиозный рефакторинг. Сделайте одну практичную итерацию. Выпишите бизнес‑действия, которые ваш продукт выполняет каждый день. Отметьте, какой сервис, воркер или приложение выполняет каждое из них в продакшене. Пометьте файлы, которые ассистенты тянут в подсказки по ошибке. Затем разделяйте по одному участку за раз и давайте папкам простые имена, которые соответствуют действию.
Добавьте одно короткое правило импорта прежде чем двигаться дальше. Без правила старая форма быстро вырастет обратно. Держите это просто: биллинговый модуль может использовать свои внутренности, другие модули вызывают его через небольшую публичную поверхность, а общий код остаётся универсальным. Если вам нужна целая параграфная инструкция, правило слишком расплывчато.
Простые имена папок тоже помогают. payments, refunds и invoices говорят людям и инструментам, что туда принадлежит. core, utils и misc приглашают дрейф. Ассистенты работают лучше, когда имена несут реальный смысл, потому что подсказки и поиск по файлам остаются уже.
Не перерисовывайте всё разом. Выберите область, которая вызывает самые громкие неправильные предложения, переместите её первой и посмотрите на результат. Хорошие границы обычно образуются из нескольких маленьких разрезов, а не одного большого рефакторинга.
Вы поймёте, что разделение работает, когда подсказка про одно действие в основном тянет файлы из одного модуля, одного пути деплоя и небольшого общего слоя. Это гораздо более удобная форма для ассистента — меньше места для догадок.
Пример: checkout, billing и admin
В магазине эти три зоны часто оказываются рядом с одними и теми же таблицами, поэтому люди складывают всё в один пакет orders. Неделю это выглядит аккуратно, а потом — очень грязно. Ассистент видит элементы корзины, попытки оплаты, записи инвойсов, заметки по возвратам и флаги сотрудников в одном месте, затем правит правила, которые не должны быть связаны.
Checkout должен владеть путём от корзины до размещённого заказа. Это включает суммы корзины, проверки купонов, выбор доставки, удержание стока и правила, определяющие, может ли существовать заказ. Если клиент меняет количество или адрес, ассистент должен оставаться в коде checkout и завершать работу там.
Billing решает другую задачу. Он должен владеть денежной логикой после того, как заказ создан: налоговая логика, создание инвойсов, повторные попытки оплаты, записи по возвратам и статус транзакций. Финансовые правила меняются по причинам, не связанным с поведением корзины. Когда ассистент меняет политику повторных попыток для упавших карт, он не должен блуждать в код купонов или экранах для сотрудников.
Admin может читать те же заказы и платежи, не владея этими правилами. Дашборды для сотрудников, потоки утверждений, заметки по фрод‑ревью, действия поддержки и внутренний поиск — всё это для админа. Дашборд может показывать статус инвойса или ошибки checkout, но это не значит, что админ должен считать налоги или пересобирать суммы корзины.
Одно чистое разделение легко описать:
- checkout — владеет корзиной, расчётом цен и созданием заказа
- billing — владеет налогами, инвойсами, платежами и возвратами
- admin — владеет представлениями для сотрудников, инструментами ревью и ручными действиями
Это делает изменения меньшими. Починка бага с округлением в checkout остаётся там же. Смена политики повторных попыток в billing остаётся в billing. Добавление нового действия поддержки делает работу в admin, не тянув финансовую логику.
Общие таблицы не означают общую собственность. Поместите каждое правило туда, где ему место, и ассистент начнёт делать меньше широких шумных правок.
Ошибки, которые создают шумные предложения
Самая распространённая ловушка — папка shared, которая постепенно превращается в ящик с мусором. Сначала туда попадает один хелпер для биллинга, затем правило checkout, затем админ‑код. Через несколько месяцев ассистент видит одну смешанную кучу вместо чётких доменных правил и начинает переиспользовать не то в не том месте.
Другой промах случается раньше. Команды делят код на frontend, backend и скрипты прежде чем они промапят поток бизнес‑действия. Такое расположение выглядит аккуратно, но скрывает реальную механику продукта. Если логика checkout живёт в трёх разных деревьях, ассистенту придётся угадывать, какие файлы относятся к одному изменению — тут и начинаются шумные предложения.
Файлы деплоя создают ту же проблему, когда они далеко от модуля, который шипится. Если у воркера, API и планировщика джобов все конфиги зарыты в одной ops‑папке, ассистент не сможет легко понять, что где запускается. Он может обновить код и упустить Dockerfile, CI‑ступень или env‑настройку, которые делают изменение рабочим.
Прямые записи в базу через границы модулей ещё хуже. Если админ пишет прямо в таблицы биллинга или биллинг лезет в данные checkout без ясного интерфейса, имена папок перестают что‑то значить. Репо говорит одно, поток данных — другое.
Старые границы тоже живут долго после изменений продукта. Стартап может начать с общей области orders, затем выделить отдельные потоки для checkout, инвойсов, фрод‑чеков и инструментов поддержки. Если репо всё ещё отражает старый продукт, ассистент продолжит тащить старые паттерны в новую работу.
Обычно эти запахи проявляются так:
- ассистент правит файлы в неожиданных модулях
- похожие бизнес‑правила существуют в двух‑трёх местах
- изменения деплоя пропускают до ревью или продакшена
- маленькие запросы вызывают широкие диффы
- тесты проходят в одном модуле, а другой тихо ломается
Чистые границы модулей не делают ассистента умнее. Они дают ему меньше пространства для ошибок.
Быстрые проверки перед фиксацией структуры
Репо может выглядеть аккуратно и всё ещё путать людей и инструменты. Прежде чем фиксировать раскладку, проверьте, остаётся ли одно изменение локальным или течёт по коду.
Начните с реального клиентского действия. Попросите того, кто не проектировал репо, проследить поток: обновление карты, размещение заказа или возврат. Если ему приходится прыгать по UI‑коду, общим хелперам, случайным сервисам и админ‑файлам, граница слишком размыта.
Несколько проверок ловят большинство проблем. Попробуйте задеплоить один модуль отдельно. Если небольшой релиз заставляет вас смотреть полрепозитория, путь деплоя слишком широк. Поменяйте одно бизнес‑правило и смотрите тесты. Правило биллинга должно падать сначала в billing, а не в пяти несвязанных местах. Почитайте импорты между модулями. Хорошие границы показывают маленький публичный API и скрывают остальное. Затем попросите ассистента сделать простую функциональную правку. Если ему нужно открыть десять папок прежде чем начать, структура мешает ему работать.
Проверка импортов важнее, чем многие думают. Когда разработчики лезут через границы в внутренние файлы, ассистенты быстро копируют эту привычку. Маленькое изменение превращается в правки там, где их быть не должно.
Тесты говорят то же самое. Если изменение одного правила вызывает падения в checkout, billing, admin и отчётности одновременно, репо, вероятно, смешивает доменные правила с удобными импортами. Это замедляет людей и снижает точность ассистента, потому что модель видит много слабо связанных файлов и не понимает, кто владеет правилом.
Скучно обычно лучше. Если действие клиента имеет одно очевидное место, одно правило ломает одну очевидную тестовую область, и один модуль может шипиться без чтения всего репо — вы близки к тому, чтобы зафиксировать структуру.
Что делать дальше
Начните с малого. Выберите один поток, который уже стоит времени или даёт плохие правки — например, checkout, billing или онбординг пользователей. Не трогайте всё сразу. Узкая правка даёт реальные доказательства и не запускает споры о вкусах папок.
Для этого потока напишите короткие правила границ рядом с кодом. Держите их короткими и резкими. Запись в billing может сказать: "Этот модуль решает начисления, возвраты и состояние инвойсов. Он не отправляет письма и не управляет правами админов." Это помогает людям и помогает ассистентам оставаться в рамках.
Затем протестируйте структуру реальной работой. Попросите ассистента сделать несколько небольших правок в этом потоке. Проверьте диффы и пометьте все места, куда он по ошибке полез. Ищите повторяющийся дрейф, например, утечку бизнес‑правил в UI или распространение деплой‑специфичного кода по общим папкам. Корректируйте границу только там, где повторяется одна и та же картина.
Это важнее длинного дизайн‑дока. Вы проверяете, как репо ведёт себя под нагрузкой. Если ассистент продолжает угадывать, граница ещё не ясна. Если диффы становятся меньше и чище — вы движетесь в правильном направлении.
Держите правила рядом с кодом и держите их читаемыми. Короткий README в каждом модуле часто работает лучше, чем центральный архитектурный файл, в который никто не заглядывает. Когда репо меняется, обновляйте эти заметки в тот же день. Старые правила хуже, чем их отсутствие, потому что они учат людей и инструменты доверять неправильной карте.
Если такая уборка постоянно тормозит, внешняя помощь может пригодиться. Oleg Sotnikov на oleg.is работает как Fractional CTO и советник стартапов, с практическим опытом в AI‑ориентированной разработке, структуре репозиториев и lean‑доставке. Это может помочь, когда проблемы в границах начинают влиять на скорость шипа, нагрузку ревью и риск в продакшне.
Часто задаваемые вопросы
Почему AI‑ассистенты делают такие широкие правки в запутанном репозитории?
Ассистент следует за локальными файлами и повторяет соседние паттерны. Если зона смешивает биллинг, UI, фоновые задачи и админ‑правила, он включит всё это в одну правку и будет угадывать, где должно быть правило.
Какой первый признак того, что граница слишком размыта?
Первый признак — маленькие запросы, которые затрагивают сразу несколько модулей. Если простая правка возврата тянет за собой экраны оформления, админ‑код и старые скрипты, границы модулей слишком рыхлые.
Как лучше организовать папки — по командам или по продуктовым областям?
Организуйте по продуктовым правилам, а не по текущему орг‑шилу. Команды меняются чаще, чем бизнес‑правила; checkout, billing и refunds должны иметь стабильное место в репо.
Когда хелпер принадлежит общей папке?
Не выносите хелперы в shared преждевременно. Поместите в общую папку только тогда, когда два модуля действительно нуждаются в одном и том же поведении по одной и той же причине; иначе держите хелпер рядом с правилом, которое его использует.
Почему пути записи важнее путей чтения?
Чтения могут приходить из разных мест без серьёзного вреда, но записи создают дрейф — если несколько модулей могут создавать возвраты или обновлять состояние счёта, ассистент увидит несколько источников истины и начнёт копировать неверный вариант.
Должны ли модули соответствовать путям деплоя?
Да. Если сервис деплоится отдельно, держите его код, конфиги, тесты и файлы релиза рядом. Это даёт людям и ассистентам одно очевидное место для изменений при деплое.
Нужно ли перерисовывать весь репозиторий сразу?
Нет необходимости переделывать всё сразу. Начните с одного болезненного потока и исправьте его первым. Несколько небольших разрезов обычно эффективнее крупного рефакторинга — вы сможете проверить форму на реальных изменениях.
Как на практике разделить checkout, billing и admin?
Пусть checkout отвечает за суммирование корзины, купоны, удержание товаров и создание заказа. Billing — за налоги, создание счётов, платежи и возвраты. Admin — за представления для сотрудников и ручные действия без владения денежной логикой.
Как протестировать структуру репо перед тем, как её зафиксировать?
Возьмите одно реальное действие пользователя и проследите его от начала до конца. Если приходится прыгать по UI‑коду, случайным хелперам, разным сервисам и админ‑файлам, структура всё ещё течёт.
Какое простое правило добавить после перемещения кода в новый модуль?
После перемещения кода напишите рядом с модулем короткое правило: что он владеет и что не владеет. Затем введите одно простое правило импорта: другие модули вызывают публичный API, не лезут во внутренности.