gitaspen docs

Работа в кодовой базе

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

10 минут

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

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

Место в цепочке

Откуда пришлиЭтот документКуда ведёт
код разложен по слоям (BMAP / BMFP / BMBP / BMGP)как вносится и проверяется отдельное изменениепрогон тестов, фиксация в истории

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

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


С чем сверяются перед изменением

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

Что меняетсяЧто перечитываетсяЧто оттуда нужно
любой кодPRINCIPLESконтракт на границе, отсутствие знания о конкретном хозяине
место пакета в репозитории, связь фронта и бэкаBMAPкорни репозитория, форма конверта
фронтBMFPслои, инварианты, именование, порядок добавления фичи
бэкBMBPслои, инварианты, DI, идемпотентность, транзакции
шлюзBMGPмаршруты, upstream, что живёт на шлюзе, а что в сервисе
код, у которого появился второй потребительREUSEкогда выделять единицу и как её подключать

Проверка перед началом: назвать слой, в котором окажется правка, и правило этого слоя, которое её ограничивает. Слой не называется — место для изменения ещё не выбрано; выбор места идёт до написания кода, а не после.


Правка вносится точечно

Меняется то, что относится к задаче, и ничего вокруг.

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

Одно изменение — один коммит. Переименование, переформатирование и смена стиля идут отдельными коммитами, не вперемешку с правкой поведения — иначе их нельзя ни просмотреть, ни откатить по отдельности (git и репозитории). Гранулярность — это и есть механизм отката: обратным коммитом снимается ровно то, что лежит в отдельном коммите.

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

Задел «на будущее» не пишется. Абстракция под воображаемого второго потребителя — тот же костыль, только наоборот: единица выделяется, когда переросла место, а не заранее (REUSE).


Расхождение с архитектурой не выполняется молча

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

Расхождение формулируется как проблема и следствие, а не как предпочтение: «бизнес-логика попадёт в клиент — при смене транспорта её придётся переносить», а не «так не принято».

Признаки, при которых сверка обязательна до написания кода:

  • нарушается граница слоя: api идёт в хранилище, boundary зовёт клиент напрямую;
  • в одном месте смешаны ответственности — транспорт и правило предметной области;
  • значение зашивается в код вместо настройки: адрес, лимит, ключ, имя конкретного потребителя;
  • бизнес-правило переезжает в API или в UI;
  • в переиспользуемой единице появляется ветвление по имени её потребителя.

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


Код читается как соседний

Новый код не выделяется в файле. Ориентир — не общий вкус, а то, что уже лежит рядом.

Имена — по правилам своей архитектуры. Суффиксы ролей и алиасы слоёв на фронте (BMFP, раздел «Именование»), имена классов слоёв и фабрик DI на бэке (BMBP, раздел «Именование»). Одно понятие — одно имя во всём коде.

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

Структура файла и форма импорта — как у соседей. Абсолютные импорты через алиасы слоёв, тот же порядок объявлений, то же разбиение на файлы.

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

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


Границы слоёв не нарушаются ради краткости

Вызов «через слой» короче на несколько строк и стоит потери границы: слой перестаёт быть заменяемым, а тест на него перестаёт что-либо доказывать.

СокращениеЧто ломается
api обращается к репозиторию мимо coreправило предметной области оказывается в транспорте и не проверяется без него
boundary зовёт клиент или хранилище напрямуюUI начинает знать форму ответа сервера; смена транспорта задевает экраны
в infrastructure появляется бизнес-правилоправило дублируется при втором адаптере, и тесты слоя логики его не видят
shared импортирует вышележащий слойпоявляется цикл, и shared больше нельзя переиспользовать отдельно

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


Что делать при неопределённости

Неопределённость — не повод остановиться и не повод угадать.

  1. Выполняется часть, которая от неопределённости не зависит. Как правило, это большая часть работы, и она не переделывается при любом ответе.
  2. Вопрос задаётся как выбор из названных вариантов с последствиями каждого, а не как «что делать?». Вопросы собираются в один список, а не выдаются по одному.
  3. Допущение, принятое без подтверждения, записывается рядом с изменением — в описании коммита или в запросе на просмотр. Непроговорённое допущение проверяющий не отличит от согласованного решения.

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


Очевидная неэффективность

Преждевременная оптимизация не выполняется: код пишется простым, узкие места ищутся измерением. Но известная заранее неэффективность не вносится:

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

Проверка перед сдачей

Прогоняется:

bash
<команда прогона тестов>     # ожидается: все тесты прошли, код возврата 0
echo $?                      # ожидается: 0
git diff --stat              # ожидается: в списке только файлы, относящиеся к задаче

Что покрывать и какими тестами — тестирование. Исправление ошибки начинается с теста, который падает до правки и проходит после.

Инварианты слоёв проверяются поиском по импортам, а не чтением:

bash
grep -rn "@infrastructure/clients" <каталог boundary>          # ожидается: пусто
grep -rn "<импорт транспортного фреймворка>" <каталог core>    # ожидается: пусто

Глазами смотрят то, чего тесты не покажут:

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

Изменения в документации проекта проверяются по правилам письма.


Типичные отказы

ПризнакПричинаЧто делать
диф красный целиком, содержательная правка в нём не виднафайл выдан заново вместо изменениявернуть исходный файл и внести только относящееся к задаче; переформатирование — отдельным коммитом
просмотр изменения занимает столько же, сколько написать зановов одном коммите смешаны правка поведения и переименованияразнести по коммитам: одно изменение — один коммит
«временное» нарушение слоя осталось в кодеу временного решения не было ни срока, ни владельцавернуть правило в его слой; сокращение пути не окупается
правка сделана, но поведение изменилось не там, где ожидалиизменение внесено не в тот слойперенести туда, где живёт правило: проверяемое только через интерфейс лежит не на своём слое
ошибка вернулась после исправленияизменение проверено просмотром, а не прогономпрогнать набор; сначала воспроизводящий тест, потом правка
код работает, но выглядит в файле чужимимена и стиль взяты не из соседнего кодапривести к правилам именования своей архитектуры
запрос выполнен буквально, граница слоя нарушенарасхождение не было проговореновернуть по канону и назвать расхождение; молчаливое согласие дороже спора
ссылка на архитектурный документ никуда не ведётимя взято из чужого сводасверить с составом раздела architecture/: PRINCIPLES, BMAP, BMFP, BMBP, BMGP, REUSE
Инструкция не помогла?

Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.