Работа в кодовой базе
Как вносится отдельное изменение в код: с чем сверяются до, как правят, что проверяют перед сдачей и что делают при расхождении с архитектурой. Правила одинаковы для человека и для ИИ-помощника: у кода два равных пользователя, оба действуют одной логикой и видят одни и те же документы (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 больше нельзя переиспользовать отдельно |
Пометка «временно» здесь не работает: у временного решения нет ни срока, ни владельца, и оно остаётся в коде. Если правило действительно требует нарушения границы, граница проведена не там — это разбирается отдельно и меняет архитектурный документ, а не обходится в одном файле.
Что делать при неопределённости
Неопределённость — не повод остановиться и не повод угадать.
- Выполняется часть, которая от неопределённости не зависит. Как правило, это большая часть работы, и она не переделывается при любом ответе.
- Вопрос задаётся как выбор из названных вариантов с последствиями каждого, а не как «что делать?». Вопросы собираются в один список, а не выдаются по одному.
- Допущение, принятое без подтверждения, записывается рядом с изменением — в описании коммита или в запросе на просмотр. Непроговорённое допущение проверяющий не отличит от согласованного решения.
Спрашивают, когда без ответа затрагивается видимое снаружи: контракт, форма ответа, схема данных, права, поведение при отказе. Внутреннее устройство — выбор реализации, разбиение на файлы, имена внутри слоя — решается на месте по соседнему коду.
Очевидная неэффективность
Преждевременная оптимизация не выполняется: код пишется простым, узкие места ищутся измерением. Но известная заранее неэффективность не вносится:
- бэк — блокирующий вызов в асинхронном коде, запрос в цикле вместо одного запроса, выборка всей таблицы ради одной строки;
- фронт — перерисовка экрана из-за одного значения, состояние и эффекты там, где достаточно вычисляемого значения.
Проверка перед сдачей
Прогоняется:
<команда прогона тестов> # ожидается: все тесты прошли, код возврата 0
echo $? # ожидается: 0
git diff --stat # ожидается: в списке только файлы, относящиеся к задачеЧто покрывать и какими тестами — тестирование. Исправление ошибки начинается с теста, который падает до правки и проходит после.
Инварианты слоёв проверяются поиском по импортам, а не чтением:
grep -rn "@infrastructure/clients" <каталог boundary> # ожидается: пусто
grep -rn "<импорт транспортного фреймворка>" <каталог core> # ожидается: пустоГлазами смотрят то, чего тесты не покажут:
- диф целиком — не осталось ли отладочного вывода, закомментированного кода, случайного переформатирования и правок в файлах, к задаче не относящихся;
- значения окружения — адресов и ключей в коде нет, они приходят из настроек (git и репозитории);
- фронт — экран в состояниях загрузки, ошибки и пустых данных, на узком экране тоже (правила дизайна, адаптивность);
- бэк — ответ ручки целиком: конверт, код ошибки, запись в журнале при отказе.
Изменения в документации проекта проверяются по правилам письма.
Типичные отказы
| Признак | Причина | Что делать |
|---|---|---|
| диф красный целиком, содержательная правка в нём не видна | файл выдан заново вместо изменения | вернуть исходный файл и внести только относящееся к задаче; переформатирование — отдельным коммитом |
| просмотр изменения занимает столько же, сколько написать заново | в одном коммите смешаны правка поведения и переименования | разнести по коммитам: одно изменение — один коммит |
| «временное» нарушение слоя осталось в коде | у временного решения не было ни срока, ни владельца | вернуть правило в его слой; сокращение пути не окупается |
| правка сделана, но поведение изменилось не там, где ожидали | изменение внесено не в тот слой | перенести туда, где живёт правило: проверяемое только через интерфейс лежит не на своём слое |
| ошибка вернулась после исправления | изменение проверено просмотром, а не прогоном | прогнать набор; сначала воспроизводящий тест, потом правка |
| код работает, но выглядит в файле чужим | имена и стиль взяты не из соседнего кода | привести к правилам именования своей архитектуры |
| запрос выполнен буквально, граница слоя нарушена | расхождение не было проговорено | вернуть по канону и назвать расхождение; молчаливое согласие дороже спора |
| ссылка на архитектурный документ никуда не ведёт | имя взято из чужого свода | сверить с составом раздела architecture/: PRINCIPLES, BMAP, BMFP, BMBP, BMGP, REUSE |
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.