DarkRiDDeR14 мин

Код не равен решению: как назначить владельцев на стыке модулей

РазработкаКомандаGit

Симптом неприятно знаком: в релизе находится ошибка на стыке checkout и интеграции, но в обсуждении сразу появляются три разных ответа. Один человек последний менял строку, второй держит каталог, третий принимает решение о статусах платежа. Пока их называют одним словом «владелец», исправление стоит на месте: reviewer не знает, что проверить, а после merge никто не берёт на себя наблюдение за повтором. Цена — не формальная задержка, а второй дефект с тем же решением, только в другом файле.

В конце 2019 года для такой ситуации не нужен управленческий слой поверх всего проекта. Достаточно собрать короткую карту: кто отвечает за смысл решения, кто поддерживает конкретный путь в коде, кого запросить на review и кто закроет технический след после выпуска. Эти роли могут совпасть у одного человека. Важно не совпадение, а то, что в задаче их можно различить и проверить.

Сначала называем не владельца, а поломку

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

История Git помогает собрать контекст. Команда git blame показывает revision и автора, последними изменивших строки; git log помогает увидеть связанные изменения. Но это не выбор текущего ответственного. Человек мог внести механический перенос, а решение о контракте могло принадлежать другой области. Исторический автор — факт расследования, не автоматическое назначение для исправления.

Четыре роли в одном дефекте
РольНа какой вопрос отвечаетАртефактКогда работа закончена
Владелец решенияЧто означает статус и какое поведение допустимо?Короткая запись в issue или в описании changeЕсть явное решение и принятая граница
Владелец пути кодаГде изменить поведение и кто поддержит этот участок?Путь, тест, CODEOWNERS-правило или карточка модуляПатч попал в известную область без скрытого дублирования
Владелец reviewКто проверит контракт и побочный эффект перед merge?Запрошенный reviewer и итог reviewЕсть ответ на конкретный риск, а не только одобрение файла
Владелец последующего действияКто проверит выпуск, лог или открытую техническую задачу?Ссылка на проверку и назначенный исполнительРезультат проверки записан либо создана отдельная задача

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

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

CODEOWNERS — маршрут запроса, а не доказательство знания

В Git нет стандарта CODEOWNERS: это возможность хостинга. В GitHub файл с таким именем задаёт пользователей или команды для путей, и при pull request с изменением этих путей платформа может автоматически запросить review. Поэтому файл полезен как маршрут до нужного человека, но сам по себе не подтверждает, что reviewer прочитал контракт, а владелец решения согласовал семантику.

Правила ниже показывают минимальную границу. Более общий путь расположен раньше, а специфичный gateway — позже: в документации GitHub последнее подходящее правило имеет преимущество. Отдельная строка для самого CODEOWNERS не декоративна: иначе тот, кто меняет маршрут review, может незаметно назначить себе удобный маршрут. Псевдонимы в примере вымышлены и не относятся к этому репозиторию.

# .github/CODEOWNERS
# Псевдонимы и пути ниже учебные: это не фрагмент рабочего репозитория.
*                               @example/platform-review
/web/checkout/                  @example/checkout
/web/checkout/gateway/          @example/payments
/docs/checkout-contract.md      @example/payments @example/checkout
/.github/CODEOWNERS             @example/repository-admins

Не надо переписывать дерево целиком ради одного дефекта. Сначала выбираем два-три пути, по которым решение действительно проходит: обработчик статуса, контракт или fixture, рядом стоящий экран. Потом открываем pull request и смотрим, кого реально запросил хостинг из base branch. Если запрос не появился, это сигнал проверить расположение файла, регистр пути, доступ команды и порядок правил, а не повод назначить автора последнего коммита владельцем.

Маршрут от сбоя до понятного изменения

  1. Записать один воспроизводимый симптом, вход и неверный результат. Не писать «сломан checkout»: указать статус, экран или функцию, где поведение наблюдается.
  2. Собрать историю узкого диапазона строк и связанных путей через git blame и git log. Отделить факт «кто менял» от гипотезы «кто решает».
  3. Назвать владельца решения: он отвечает на вопрос о контракте или допустимом состоянии. Если такого ответа нет, первым результатом становится именно решение, а не патч.
  4. Назвать путь кода и проверить маршрут CODEOWNERS в target branch. Зафиксировать, кого нужно запросить на review и почему именно этого человека или команду.
  5. Открыть небольшой change с тестом отрицательного случая. В описании указать риск, решение, reviewer и действие после merge: проверку лога, выпуска или отдельную задачу.
  6. После merge выполнить запланированную проверку и записать результат рядом с задачей. Если сигнал не готов, не называть исправление полностью закрытым: есть патч, но нет следа его эксплуатации.

Review проверяет риск, а не присутствие имени

GitHub различает comment, approve и request changes. Это полезно использовать по смыслу. Для патча статуса reviewer по контракту должен подтвердить трактовку неизвестного значения; reviewer пути кода — проверить, что update не ломает переходы на соседнем экране. Одно «Approve» не обязано содержать оба знания. Когда роль указана рядом с вопросом, review становится коротким и предметным.

Не превращайте CODEOWNERS в список людей, которых нужно позвать на любую правку. Широкое правило * годится как запасной маршрут, но не показывает, кто может решить спорный контракт. Избыточный список вырабатывает привычку к механическому approval. Лучше небольшая карта с понятными границами и отдельным назначением специалиста, когда решение выходит за границу файла.

Что оставить после исправления

Минимальный след состоит из четырёх вещей: теста на прошлый сбой, записи о решении, пути с понятным маршрутом review и назначенной проверки после merge. Не обязательно строить каталог всей архитектуры. Достаточно, чтобы следующий разработчик нашёл ответ на два вопроса: почему статус обрабатывается именно так и кто подтвердит изменение, если контракт снова придёт с другой стороны.

Проверьте и отрицательный вариант. Удалите reviewer из черновой модели: становится ли понятно, что review не закрыто? Подмените неизвестный статус в тесте: остаётся ли он видимым, а не превращается в успех? Сместите файл в соседний каталог: маршрут ownership всё ещё соответствует реальной границе? Такой короткий тест вскрывает фиктивную ответственность раньше, чем она попадёт в выпуск.

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

  • GitHub Docs: About code owners — CODEOWNERS назначает владельцев путей, запрашивает их review для изменений и использует правила из base branch pull request
  • GitHub Docs: Pull request reviews — review имеет отдельные решения Comment, Approve и Request changes; правила ветки могут требовать approval перед merge
  • Git: git-blame documentation — git blame показывает revision и автора, которые последними изменили каждую строку; это исторический факт, а не назначение текущего владельца решения
  • Git: git-log documentation — git log позволяет ограничивать просмотр истории, чтобы собрать контекст изменения до исправления