Files
progcode/editorial/agent-rewrites/291.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
15 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 291,
"slug": "editorial-2019-12-practice-code-ownership",
"title": "Код не равен решению: как назначить владельцев на стыке модулей",
"excerpt": "Ошибка проходит через несколько модулей, а команда ищет ответственного по последнему коммиту. Разделяем владельца решения, путь кода, review и проверку после merge.",
"contentHtml": "<p>Ошибка появляется на стыке checkout и платежного gateway. В журнале виден неизвестный статус, а экран показывает успешную оплату. Один разработчик ищет строку в parser, другой открывает последний коммит, третий ждёт ответа команды интеграции. Исправление задерживается, а следующий дефект повторяет ту же ошибку в соседнем файле. Цена — неверное состояние для пользователя, ручное расследование и потерянное время нескольких команд.</p>\n<p>Последний автор строки не обязан быть владельцем решения. Он мог перенести код, исправить форматирование или добавить временный обход. История Git отвечает на вопрос «кто менял строку». Она не отвечает на вопрос «что должен означать новый статус». Владелец пути отвечает за место изменения. Владелец решения отвечает за смысл контракта. Reviewer проверяет заданный риск. После merge отдельный человек проверяет результат.</p>\n<h2>Тезис: владение нужно разложить по вопросам</h2>\n<p>Слово «владелец» слишком грубое для дефекта, который пересекает границы. Назначьте четыре роли. Владелец решения подтверждает допустимое поведение. Владелец пути кода знает реализацию и соседние переходы. Reviewer отвечает на конкретный вопрос в pull request. Владелец последующего действия проверяет сигнал после merge. Один человек может взять все роли. Но карточка должна показывать их отдельно.</p>\n<p>Начинайте с наблюдаемого симптома. Запишите вход, неверный результат и место, где его видно. Формулировка «сломался checkout» не помогает. Формулировка «gateway вернул <code>unknown</code>, а обработчик перевёл его в <code>success</code>» задаёт границу расследования. В ней есть поведение, которое нужно подтвердить или отвергнуть.</p>\n<h2>Механизм: четыре следа вместо одного имени</h2>\n<p><code>git blame</code> показывает revision и автора последнего изменения строки. Используйте его, чтобы найти контекст. Затем откройте <code>git log --follow</code> для пути и связанные документы. Не переносите найденное имя в поле владельца решения автоматически. Исторический факт полезен, но он не заменяет договорённость о статусах.</p>\n<p><code>CODEOWNERS</code> решает другую задачу. В GitHub файл сопоставляет пути с пользователями или командами и может автоматически запросить review. Правило берут из base branch pull request. Поэтому новая строка в feature branch ещё не доказывает, что маршрут сработает. Более узкое совпадение должно стоять после общего. Учебные имена ниже вымышлены и не относятся к рабочему репозиторию.</p>\n<pre><code># .github/CODEOWNERS\n* @example/platform-review\n/web/checkout/ @example/checkout\n/web/checkout/gateway/ @example/payments\n/docs/checkout-contract.md @example/payments @example/checkout\n/.github/CODEOWNERS @example/repository-admins</code></pre>\n<p>В этом примере изменение gateway направляется команде платежей, а изменение контракта — двум областям. Последняя строка защищает сам маршрут. В реальном репозитории замените псевдонимы доступными пользователями или командами. Если у команды нет прав на репозиторий, автоматический запрос не создаёт знания и не исправляет процесс.</p>\n<div class=\"table-scroll\"><table><caption>Симптом → причина → проверка → действие</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Последний автор не отвечает</td><td>Исторический автор принят за владельца решения</td><td>Сравнить blame с diff, контрактом и тестами</td><td>Назначить decision owner по смыслу статуса</td></tr><tr><td>Review не пришёл</td><td>Путь не совпал или CODEOWNERS взят не из base branch</td><td>Проверить расположение, регистр, права и правило</td><td>Исправить маршрут или запросить reviewer вручную</td></tr><tr><td>PR approved, но риск остался</td><td>Одобрение не содержит проверяемого вопроса</td><td>Найти ответ на контракт и отрицательный путь</td><td>Перезапросить review с двумя конкретными вопросами</td></tr><tr><td>После merge нет результата</td><td>Follow-up не назначен</td><td>Проверить задачу, сигнал и срок проверки</td><td>Создать отдельное действие с исходом</td></tr></tbody></table></div>\n<figure><img src=\"/assets/editorial/2019/code-ownership-three-surfaces-2019.svg\" alt=\"Схема разделяет владельца решения, пути кода, ревью и последующей проверки вокруг одного дефекта.\" loading=\"lazy\" /><figcaption>Один дефект проходит через четыре поверхности ответственности. История Git даёт контекст, но не назначает владельца решения.</figcaption></figure>\n<h2>Учебный пример: неизвестный статус</h2>\n<p>Ниже — учебный сценарий. Он не описывает production-инцидент и не утверждает, что кто-то видел такие метрики. Пусть gateway возвращает <code>unknown</code>. Обработчик должен сохранить это состояние и показать безопасный экран, а не считать значение успешным.</p>\n<pre><code>function mapGatewayStatus(status) {\n if (status === 'paid') return { state: 'success' };\n if (status === 'pending') return { state: 'pending' };\n return { state: 'unknown', diagnostic: 'gateway-status-unmapped' };\n}\n\nconst result = mapGatewayStatus('unknown');\n// result.state === 'unknown'; success недопустим</code></pre>\n<p>Владелец решения подтверждает смысл <code>unknown</code>: это не оплата и не отказ, а состояние, которое требует безопасного отображения и диагностического следа. Владелец пути проверяет обработчик, retry и соседний экран. Reviewer задаёт два вопроса: не попадает ли неизвестное значение в success и сохраняется ли идентификатор для расследования. Follow-up owner проверяет запланированный сигнал после merge.</p>\n<p>Если contract owner не найден, патч нельзя считать готовым. Исправление может выглядеть разумно, но команда всё ещё не знает, какое поведение считать правильным. Отрицательный путь здесь важнее красивой ветки успеха: тест должен явно отвергать <code>unknown → success</code>.</p>\n<h2>Порядок действий</h2>\n<ol><li>Запишите один симптом: вход, неверный результат, путь и цену ошибки. Не начинайте с поиска имени.</li><li>Соберите узкую историю через <code>git blame -L</code>, <code>git log --follow</code> и просмотр связанных контрактов. Отделите факт изменения от гипотезы о владении.</li><li>Назначьте владельца решения и владельца пути. Если смысл статуса не подтверждён, сначала получите решение, а затем меняйте код.</li><li>Проверьте маршрут <code>CODEOWNERS</code> на base branch. Убедитесь, что путь совпадает, регистр верен, а users и teams имеют нужные права.</li><li>Откройте небольшой change с тестом отрицательного случая. В описании укажите риск и вопрос каждому reviewer.</li><li>После review зафиксируйте, что именно одобрено. Comment, Approve и Request changes — разные состояния; появление имени в списке не равно ответу на риск.</li><li>Назначьте follow-up до merge. После выпуска запишите подтверждённый сигнал, откат или новую связанную задачу.</li></ol>\n<h2>Ограничения</h2>\n<p><code>CODEOWNERS</code> не является каталогом экспертизы. Он знает путь и правила платформы. Он не подтверждает бизнес-смысл изменения и не проверяет production. Не делайте правило <code>*</code> единственной картой знания: оно направит запрос, но не покажет, кто решает спорный контракт.</p>\n<p>GitHub — только один вариант реализации. В другой системе может не быть CODEOWNERS или автоматического запроса. Тогда сохраните ту же модель в issue или шаблоне pull request: decision owner, code path owner, reviewer, follow-up owner. Меняется инструмент, но не вопросы, на которые должен ответить change.</p>\n<p>Не превращайте карту владения в процесс ради процесса. Для маленького модуля достаточно четырёх полей, одного теста и двух конкретных вопросов. Обновляйте маршрут, когда каталог переезжает или команда меняет границу. Устаревший список создаёт ложное чувство контроля.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Работа готова, если независимый разработчик может открыть задачу и найти четыре ответа: кто подтвердил смысл решения, где лежит изменённый путь, кто проверил риск и кто проверит результат после merge. Тест не допускает превращения <code>unknown</code> в <code>success</code>. Review содержит ответ на контрактный вопрос. Маршрут ownership проверен в base branch. После merge есть записанный сигнал или отдельная задача с назначенным исполнителем.</p>\n<p>Если хотя бы одного ответа нет, закрывайте кодовый change только как промежуточный результат. Это честнее, чем назвать исправление завершённым по одному факту merge.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: About code owners</a> — расположение CODEOWNERS, base branch, порядок правил и требования к доступу.</li><li><a href=\"https://docs.github.com/en/pull-requests/reference/pull-request-reviews\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: Pull request reviews</a> — различия Comment, Approve и Request changes.</li><li><a href=\"https://git-scm.com/docs/git-blame\" target=\"_blank\" rel=\"noopener noreferrer\">Git documentation: git-blame</a> — историческая аннотация строк и авторов изменений.</li></ul>"
}