Ревью текста: как читать чужой документ и как принимать правки
RFC-14 «Асинхронные вебхуки для партнёров» собрал за четыре дня шестьдесят три комментария от шести ревьюеров. Пятьдесят четыре — про формулировки, оформление таблицы и наименование полей. Девять — по существу, и восемь из них про детали реализации: размер батча, формат подписи, тайм-ауты. Документ апрувнули единогласно.
Через три месяца выяснилось, что у сорока процентов партнёров нет очереди на своей стороне: они физически не могут принять вебхук, если их сервис лежит две минуты, — а RFC предлагал ровно одну попытку доставки. Вопрос «сможет ли адресат вообще это принять» не задал никто. Позже один из ревьюеров сказал фразу, которую стоит выписать на стену:
«Я думал, это не моя часть».
Шесть человек прочитали текст, и ни один не прочитал его как тот, кому по этому тексту жить. Каждый вычитывал свой участок — и все шестеро вместе сработали как один хороший корректор и ни один — как читатель.
Ревью текста — не корректура. Задача ревьюера: проверить, изменит ли документ решение нужного читателя, и найти самое дешёвое место, где это ещё можно починить. Всё остальное — сервис.
В первой главе мы договорились, что документ существует ради решения конкретного человека. Ревью — единственная встроенная в процесс точка, где эту гипотезу можно проверить до того, как за неё заплатит производство. Эта глава — про обе роли: как читать чужой документ, чтобы от вас была польза, и как принимать правки, не превращая это в защиту диссертации.
Почему ревью текста ломается чаще, чем ревью кода
У кода есть слой объективной проверки: компилятор, типы, тесты, линтеры. К моменту, когда человек открывает диф, машина уже сказала «собирается» и «тесты зелёные», и ревьюеру остаётся то, что машина не умеет: замысел, границы, читаемость. У текста этого слоя почти нет. Единственный настоящий тест — живой читатель, который по документу принял решение и не ошибся. Ревьюер играет роль этого недостающего теста, а вместо этого чаще играет роль второго компилятора: проверяет пробелы.
Три силы тянут ревью текста вниз, и все три надо знать в лицо.
Закон тривиальности. Люди комментируют то, в чём уверены. В формулировке уверены все, в выборе брокера сообщений — трое из шести. Сирил Норткот Паркинсон описал это на примере комитета, который за две минуты утвердил атомную станцию и сорок пять минут спорил про навес для велосипедов («Parkinson’s Law», 1957); в инженерной культуре тот же эффект известен по письму Пола-Хеннинга Кампа «Why should I care what color the bikeshed is?» — https://bikeshed.com/. Комментарий про запятую бесплатен и безопасен, комментарий «мне кажется, весь подход неверный» стоит репутации.
Асимметрия вежливости. Ревьюер видит, что автор потратил неделю, и щадит его: предлагает дешёвые правки, потому что дорогие звучат как «выброси». Парадокс в том, что дорогая правка на стадии черновика стоит день, а на стадии продакшна — квартал.
Отсутствие роли. «Посмотри, пожалуйста» не говорит ничего о том, что от вас нужно. В пустоте человек по умолчанию включает единственный знакомый режим — школьный красный карандаш.
Отсюда всё остальное в этой главе: ревью текста работает, только если у него есть протокол. Не «будь внимательным», а порядок действий, который не даёт съехать на запятые.
Шесть уровней правки и правило порядка
Правки различаются не «важностью», а ценой, которую придётся заплатить, если проблему не поймали сейчас. Это и задаёт порядок чтения.
- L0 — нужен ли документ. Есть ли адресат и решение, которое текст должен изменить. Если нет — всё остальное неважно: вы вычитываете текст, который не надо было писать.
- L1 — тезис и решение. То ли решение выносится; стоит ли главное в первом абзаце; не оказалось ли, что решение уже принято и документ описывает его задним числом.
- L2 — структура и полнота довода. Порядок разделов, отсутствующие альтернативы, дыры, в которые провалится скептик («Структура»).
- L3 — факты, числа, код, ссылки. Сходится ли арифметика, запускается ли пример, жива ли ссылка.
- L4 — ясность фраз и термины. Двусмысленности, пропавший деятель, мутная модальность («Ясность»).
- L5 — оформление и опечатки. Стиль-гайд, заголовки, регистр в списках, пробелы.
Правило порядка: не спускайтесь на уровень ниже, пока не закрыт верхний. Основание простое — правка уровня N обесценивает все комментарии уровня ниже N в том же куске текста. Четырнадцать замечаний к формулировкам в разделе, который после одного комментария L1 исчезнет целиком, — это не «тщательность», а сожжённый час вашего времени и час чужого.
Второе правило зеркальное: всё, что ловится на L5 и половина того, что ловится на L4, не должно исходить от человека дважды. Если вы второй раз пишете «тут пробел перед тире» — это заявка на правило линтера, а не комментарий. Ниже разберём, как это ставится в CI.
Третье наблюдение из практики: уровень комментария надо помечать явно. Автор не телепат; фраза «я бы тут поменял» без метки читается как требование, а «[nit] я бы поменял, не блокирует» — как подарок. Метка занимает пять символов и снимает половину трений.
Протокол: три прохода за двадцать минут
Ревью документа — работа с падающей отдачей, как и ревью кода. В классических исследованиях инспекций кода (Michael Fagan, «Design and code inspections to reduce errors in program development», IBM Systems Journal, 1976 — https://doi.org/10.1147/sj.153.0182; позже данные Cisco и SmartBear на тысячах ревью — https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/) видно одно и то же: после часа непрерывного чтения находимость дефектов падает, а после определённого объёма материала за подход — падает почти до нуля. С текстом ровно так же: три вдумчивых прохода по десяти страницам полезнее, чем один медленный по сорока.
решаю, знаю предмет,
буду этим пользоваться?"} R -- "роль не ясна" --> N["Спросить автора одной строкой.
Без роли комментарии будут про запятые"] R -- "роль ясна" --> P1["Проход 1 — 3 минуты:
шапка, заголовки, первый абзац, вывод"] N --> P1 P1 --> Q0{"Могу назвать: кто решает,
что решает, к какому сроку?"} Q0 -- нет --> C0["Комментарий L0/L1 и стоп.
Дальше не читать: текст будет переписан"] Q0 -- да --> P2["Проход 2 — 10 минут: читать как адресат,
ставить пометки, не формулировать правки"] P2 --> P3["Проход 3 — 7 минут: обратный конспект,
пометки превратить в комментарии"] P3 --> Q1{"Конспект читается
как связный довод?"} Q1 -- нет --> C2["Комментарии L2: порядок, пробелы в доводе,
чего не хватает скептику"] Q1 -- да --> C3["Комментарии L3 и L4: числа, примеры,
двусмысленные фразы"] C0 --> F["Наверх — от одного до трёх блокеров.
Остальное помечено как необязательное"] C2 --> F C3 --> F F --> D{"Блокеров больше трёх
или спор о самом решении?"} D -- да --> V["Двадцать минут голосом,
потом один комментарий с итогом в документ"] D -- нет --> A["Апрув или «правки не блокируют»"]
Проход 1 — три минуты, не читая подряд. Шапка, оглавление, первый абзац, последний раздел. Ответьте себе письменно: кто принимает решение, какое, к какому сроку. Не смогли — это и есть ваш главный комментарий, и он стоит всех остальных. Отправьте его сразу, не дочитывая: автору дешевле переписать вступление сегодня, чем получить это же замечание через три дня вместе с сорока правками формулировок.
Проход 2 — десять минут, в роли адресата. Выберите одну роль и держите её: «я дежурный, который
в три ночи открыл этот раннбук», «я партнёрский интегратор, у меня есть только эта страница».
Читайте подряд и ставьте пометки, не формулируя правок: ? — не понял, ! — не верю, x — здесь
я застряну и пойду спрашивать в чат, + — вот это сработало. Пометка занимает секунду
и не выбивает из чтения; попытка сформулировать комментарий выбивает — и дальше вы читаете уже
не как читатель, а как редактор.
Проход 3 — семь минут, обратный конспект. Выпишите по одному предложению на раздел — то, что раздел реально утверждает, а не то, что обещает заголовок. Получившиеся шесть-восемь строк прочитайте подряд. Если они не складываются в связный довод — проблема на L2, и об этом надо писать раньше, чем про фразы. Обратный конспект (reverse outline) — самый дешёвый способ увидеть структуру чужого текста, не переписывая его; он же вскрывает разделы, которые ничего не утверждают, и повторы, разбросанные по документу.
И только после этого пометки превращаются в комментарии — сгруппированные, с метками уровня, с явным выделением одного-трёх блокирующих. Всё остальное — «необязательно».
Как читать: приёмы, которые находят то, что не находится само
Тест первого экрана. Прочитайте первый экран и закройте документ. Что бы вы решили прямо сейчас? Если ответ «пошёл бы читать дальше» — вступление не работает; в реальности читатель не пойдёт.
Тест скептика. Назовите три возражения, которые задаст самый неудобный человек на встрече: «а сколько это стоит», «а что если провайдер не даст SLA», «а почему не оставить как есть». Документ отвечает на них — хорошо; не отвечает — это комментарий L2, а не придирка. Для RFC это вообще главная работа ревьюера («RFC»).
Тест исполнителя. Для всего, что читатель делает руками — README, руководство, раннбук, — надо не читать, а выполнять, желательно на чистой машине или в свежем контейнере. Половина дефектов инструкций невидима при чтении: пропущенный шаг с переменной окружения, команда, требующая прав, которых у новичка нет («README», «Руководства»).
Тест чисел и ссылок. Каждое число — откуда оно; каждую ссылку — открыть. Число без источника в инженерном документе живёт годами и потом цитируется как факт. Проверять это руками скучно, поэтому ссылки проверяет CI, а числа — тот единственный ревьюер, который знает предмет.
Тест на удаление. Найдите раздел, который можно удалить без потери для решения. Он почти всегда есть, и предложение «выкинуть третий раздел» — самый полезный комментарий, который вы можете дать автору: он экономит читателям сотни минут, а автору — поддержку этого раздела на годы вперёд.
Чего ревьюер делать не должен:
- Переписывать чужой текст под свой голос. Правка стиля без изменения смысла — это ваше удовольствие за счёт чужого времени. Исключение — когда автор явно попросил «поправь как считаешь нужным» или когда правка ловится стиль-гайдом.
- Требовать полноты. «Ещё бы про мониторинг написать» — плохой комментарий, если мониторинг не влияет на решение. Документ конкурирует за внимание, и каждый добавленный абзац отодвигает вниз тот, что работает.
- Заново открывать решение, принятое вне документа. Если язык сервиса выбран кварталом раньше, комментарий «давайте перепишем на Rust» — не ревью, а новый RFC. Автор вправе ответить одной строкой со ссылкой на то, где это решалось.
- Молчать про то, что понравилось. Ревью только с дефектами делает следующий документ хуже ровно в тех местах, где он был хорош: автор не знает, что именно сработало. Одна строка «сравнительная таблица по четырём критериям — то, ради чего я открыл документ» стоит дёшево и работает как обратная связь («Обратная связь»).
Анатомия комментария
Действенный комментарий устроен как хороший баг-репорт: метка уровня → что я наблюдал как читатель → чем это грозит решению → (необязательно) предложение. Наблюдение обязательно, предложение — нет: ревьюер надёжен как детектор проблемы и ненадёжен как проектировщик решения.
Пары «до и после» — одни и те же замечания:
| Плохо | Переписано |
|---|---|
| «Непонятно» | «[важно] В шаге 4 не понял, кто нажимает кнопку: дежурный или релиз-инженер. От этого зависит, нужен ли мне доступ к проду, а я по этому документу решаю, брать ли дежурство» |
| «Слишком длинно» | «[важно] До ответа на свой вопрос (можно ли мигрировать без даунтайма) я дочитал до шестой страницы. Если поднять абзац из раздела 5 наверх, дальше можно не читать» |
| «Мне не нравится вариант Б» | «[блокер] В варианте Б нет шага отката: если после переключения записи ломается репликация, мы теряем окно в 40 минут. Как дежурный я не смогу апрувить, пока не увижу процедуру отката» |
| «Тут пассив, перепиши» | «[nit] „Была произведена миграция“ — не видно, кто её делает. Не блокирует; если таких мест много, добавлю правило в Vale» |
| «Ок, апрув» | «Апрув. Отдельно: таблица сравнения по четырём критериям — то, ради чего я открыл документ, в следующем RFC сделаю так же» |
Метки удобно взять готовые — словарь Conventional Comments (https://conventionalcomments.org/)
даёт praise:, nitpick:, suggestion:, issue:, question:, thought:, chore: плюс
модификаторы (blocking) и (non-blocking). Конкретный набор не важен, важно, что он один
на команду и что «блокирует / не блокирует» видно из первого слова.
Три формулировочных правила, которые снимают почти все конфликты в тредах:
- Описывайте свой сбой чтения, а не намерения автора. «Я застрял здесь» вместо «ты плохо объяснил». Первое — факт, который автор не может оспорить; второе — оценка, которую он обязан защищать.
- Спрашивайте, если не уверены, что поняли. «Правильно ли я понимаю, что при
retryable: falseповтор создаст второй платёж?» находит дефект и одновременно даёт автору выход без потери лица. - Отделяйте вкус от проверяемого. «Мне не нравится» — вкус, «противоречит гайду / числу
в разделе 3 / коду в
payments.go» — проверяемое. Вкус можно высказать один раз и отпустить.
Про тон и про то, что делать, когда ревью превращается в столкновение позиций, — трек лидерства: «Обратная связь» и «Трудные разговоры».
Разбор: один абзац до и после ревью
Пример 1. Вступление RFC
Было:
В рамках работы над улучшением архитектуры системы уведомлений была проведена аналитика существующих решений. Рассматривались несколько вариантов, включая использование Kafka, RabbitMQ и облачных решений. По итогам обсуждения с командой было принято решение о целесообразности использования Kafka, так как это соответствует общей стратегии развития платформы.
Комментарии, разложенные по уровням:
- L1. «Не вижу, какое решение и от кого требуется. Формулировка „было принято решение“ говорит, что вы уже решили. Если так — это не RFC, а ADR, и он должен выглядеть иначе. Если решение ещё нужно — скажите, кто его принимает и до какого числа».
- L2. «Нет критериев сравнения и нет причин отказа от RabbitMQ и облачного варианта. Без этого первый же вопрос на встрече („а почему не SQS?“) вернёт нас к началу».
- L3. «„Соответствует стратегии“ — не критерий, который можно проверить. Какие числа за этим: объём в пике, срок хранения, стоимость?»
- L4. «Деятель исчез во всех трёх предложениях: аналитика проведена, решение принято. Кем?»
Стало:
Предлагаю взять Kafka как шину уведомлений. Решение нужно от Ани (владелец платформы) до 14 марта: после этой даты команда доставки встаёт без очереди. Критерии выбора: 12 000 сообщений в секунду в пике, хранение 7 дней для переигрывания после инцидентов, эксплуатация силами текущей команды. RabbitMQ отпадает по переигрыванию: он рассчитан на удаление сообщения после подтверждения, и семидневный буфер придётся строить сбоку. Управляемый SQS отпадает по стоимости при таком объёме — около 4 200 USD в месяц против 1 100 USD за self-hosted-кластер, расчёт в разделе 4. Главный риск: у нас нет опыта эксплуатации Kafka, поэтому предлагаю первые полгода на управляемом сервисе с планом переезда.
Обратите внимание на побочный эффект: правка уровня L1 сама убрала все замечания уровня L4. Пассив исчез не потому, что автор боролся с пассивом, а потому, что появились деятель («предлагаю»), адресат («от Ани») и решение. Это общее правило: комментарии про формулировки в тексте с неопределённым адресатом — лечение симптома.
Пример 2. Строка в документации API (английский оригинал)
Было:
The `retryable` field indicates whether the request can be retried.
It is recommended to retry requests in case of errors.
Что здесь видит ревьюер. L3: не сказано, при каких ответах поле принимает значение true, —
читатель вынужден угадывать по кодам. L4: it is recommended — рекомендовано кем и с какой
стратегией повторов; in case of errors — при каких именно, включая ли 400. L2: пропущено
главное для интегратора — что произойдёт, если повторить запрос, помеченный как неповторяемый,
и как связаны повтор и ключ идемпотентности. Формально текст верен, но по нему нельзя написать код,
а именно ради этого его читают («Документация API»).
Стало:
`retryable` (boolean) — `true` for HTTP 429 and 5xx responses, `false` for 4xx.
Retry the same request with the same `Idempotency-Key`, using exponential backoff
starting at 1s, up to 5 attempts. Retrying a request with `retryable: false`
returns `409 conflict` and never creates a second charge.
Разбор по-русски: появилось точное условие (429 и 5xx), появился деятель и повелительное
наклонение вместо «рекомендуется», появилась стратегия с числами (старт 1 секунда, максимум
5 попыток), появилась связь с ключом идемпотентности и явное описание отрицательного случая —
409 и гарантия отсутствия второго списания. Ревьюер не переписывал текст сам: он оставил три
вопроса («при каких кодах true?», «какой backoff?», «что будет, если повторить неповторяемое?»),
и ответы на них дал автор, который знает систему.
Пример 3. Абзац постмортема
Было:
Дежурный не заметил алерт о заполнении пула соединений и продолжил перезапускать сервис вместо анализа причины, из-за чего инцидент затянулся на 40 минут.
Комментарий ревьюера: «[блокер] Здесь причина сформулирована как свойство человека, а дальше в разделе действий стоит „провести обучение дежурных“. Но алерта про пул не существует — я проверил в конфигурации, метрика не выведена ни на один дашборд. Если оставить формулировку, мы починим внимательность вместо системы. Предлагаю описать, что дежурный видел на экранах, и вынести отсутствие сигнала в правки». Разбор жанра — «Постмортем», культура разбора инцидентов — трек SRE.
Здесь важен приём: ревьюер не переписал абзац, а показал противоречие между абзацем и проверяемым фактом. Такой комментарий невозможно отклонить формулой «дело вкуса».
Как принимать правки
Сторона автора сложнее. Текст ощущается как продолжение себя, особенно если он писался неделю, и первая реакция на правку — объяснить, почему всё правильно. Работающая установка звучит так: правка — это данные о читателе, а не оценка вас. Ревьюер почти всегда прав в диагнозе («я здесь споткнулся») и часто неправ в рецепте («напиши так»). Диагноз принимайте всегда, рецепт — по обстоятельствам.
Пять допустимых исходов для любого комментария — и ни одного шестого:
- Принять и починить. Дешёвая правка не обсуждается: править быстрее, чем спорить.
- Принять диагноз, изменить рецепт. «Ты прав, что тут спотыкаешься, но причина не в термине, а в том, что схема идёт после текста. Поменял местами — посмотри».
- Отклонить с причиной в одну строку. «Не буду добавлять раздел про мониторинг: он не влияет на решение „берём или нет“. Если это блокирует апрув — созвонимся». Отклонять нормально; ненормально — отклонять молча.
- Вынести в задачу. Правка нужна, но не в этом документе и не к этому сроку: тикет, ссылка в тред, тред закрыт.
- Зафиксировать разногласие в тексте. Если по существу не сходитесь, спор переезжает из комментариев в раздел «Альтернативы и возражения»: «Пётр считает, что при таком объёме выгоднее шардировать существующую БД; риск в том, что…». Читатель через год увидит, что возражение звучало и было учтено, а не то, что его не было.
Рабочие правила, которые экономят нервы:
- Отвечайте на каждый комментарий, хотя бы одним словом. Молчание читается как «проигнорировал» и стоит дороже, чем любой отказ.
- Правило двух. Если двое споткнулись в одном месте — виноват текст, даже если оба «поняли неправильно». Читателей не переучивают, тексты переписывают.
- Не спорьте с формулировкой, спорьте с фактом. «Комментарий звучит грубо» — отдельный разговор, желательно не в треде. В треде остаётся только «алерт существует / не существует».
- Держите паузу. Первая реакция на резкий комментарий — не ответ. Двадцати минут хватает, чтобы отделить «мне неприятно» от «он неправ», и это две разные проблемы с разными решениями.
- Считайте стоимость крупной правки вслух. «Переписать раздел под этот довод — примерно день; решение нужно к четвергу. Предлагаю принять как есть, а довод учесть в следующей версии» — это разговор о приоритетах, а не отказ.
- Не позволяйте ревью тянуться. Документ стареет до принятия: через две недели контекст уехал, и часть комментариев уже не про ваш текст. Разумный SLA — первый комментарий за 48 часов, закрытие раунда за неделю.
- Больше пяти ревьюеров — значит ни одного. Каждый рассчитывает, что важное поймает кто-то другой (ровно история из начала главы). Двое обязательных с явными ролями, остальные — FYI.
Как просить ревью
Половина плохих ревью заказана плохо. Запрос ревью — тоже документ, и у него тот же контракт: адресат, решение, срок.
Плохо:
Ребята, накидайте плиз ревью на RFC, ссылка. Спасибо!
Переписано:
Что: RFC-14, шина уведомлений. Решение — брать Kafka или нет.
Стадия: черновик, структура ещё двигается. Про формулировки пока не пишите.
@anya — ты принимаешь решение. Нужен ответ до 14 марта.
@petr — уровень «факты»: цифры по объёму и стоимости в разделе 4, я мог ошибиться.
@kate — ты будешь этим пользоваться: скажи, на каком шаге раздела 5 застрянешь.
Уже решено и не обсуждается: язык сервиса (Go), сроки квартала.
Дедлайн комментариев: 11 марта, 18:00. Молчание не считаю апрувом — напишу лично.
Три вещи, которые делают этот запрос рабочим: явная стадия (черновик — значит, комментарии L4–L5
не нужны и не приветствуются), явная роль каждого и явные границы обсуждения. Стадию удобно
держать прямо в front matter документа: status: draft → review → accepted; это же поле потом
показывает будущему читателю, чему верить.
про запятые не пишем D-->>A: Три пометки — роль в шаге 4, нет отката, цифра не сходится A->>A: Переписал вступление и раздел рисков A->>W: Ссылка, кому какой уровень нужен, дедлайн комментариев W-->>A: 14 комментариев, из них 2 блокирующих A->>W: Ответ на каждый — принял, решил иначе, вынес в тикет A->>L: Осталось одно разногласие, оно в разделе 7. Решение нужно до 14.03 L->>A: Вариант Б, но нужен план отката A->>A: status accepted, дата, ссылка на тикет
Порядок «сначала двое, потом все» экономит больше всего: первые два читателя ловят проблемы L0–L2, пока переписать вступление стоит час. Если отдать сырой черновик сразу двадцати людям, вы получите двадцать наборов комментариев L4 к тексту, который надо перекроить целиком.
Кого звать, зависит от того, какие уровни вам нужны:
Отдельный приём для документов, которые всё равно никто не читает заранее, — молчаливое чтение на встрече: первые 15 минут все читают текст в тишине, дальше обсуждение. Практика описана в «Working Backwards» Колина Брайара и Билла Карра как часть амазоновской культуры шестистраничных документов. Она честнее, чем ритуал «все посмотрели заранее», и хорошо сочетается с обычной фасилитацией («Фасилитация»).
Что отдать машине
Каждый класс комментариев, снятый автоматикой, — это высвобожденное внимание человека для L0–L2. Правило простое: комментарий, который вы дали трижды, становится правилом линтера или пунктом шаблона.
Документ живёт в репозитории рядом с кодом, ревьюится обычным pull request и проверяется теми же средствами, что и код (docs as code, совместная работа в git, проверки в CI).
# .github/workflows/docs.yml — проверки, после которых человеку остаются только L0–L2
name: docs
on:
pull_request:
paths: ["docs/**", "**/*.md"]
jobs:
prose:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Стиль и терминология: Vale со словарём команды.
- uses: errata-ai/vale-action@reviewdog
with:
files: docs
fail_on_error: true
# Разметка: заголовки, длина строк, единый стиль списков.
- run: npx markdownlint-cli2 "docs/**/*.md"
# Битые ссылки — самый частый способ документа соврать молча.
- uses: lycheeverse/lychee-action@v2
with:
args: --no-progress --max-concurrency 8 docs/**/*.md
# Примеры кода выполняются, а не просто лежат.
- run: python -m doctest -v docs/examples/payments.py
Словарь команды в Vale описывается декларативно и снимает бесконечные споры о терминах — терминологию мы обсуждали в «Ясности»:
# .vale/styles/Team/Terms.yml — одно правило вместо двадцати комментариев в год
extends: substitution
message: "Пишите «%s» вместо «%s» — термин зафиксирован в глоссарии"
level: error
ignorecase: true
swap:
юзер: пользователь
мёржить: вливать
апишка: API
"e-mail": email
Что машина не поймает никогда: нужен ли документ, тот ли читатель, отвечает ли текст на вопрос, ради которого его открыли. Поэтому автоматизация — не про «меньше ревью», а про то, чтобы человек тратил внимание там, где он незаменим.
Полезно измерять само ревью. Три метрики, которые честно показывают, живой процесс или ритуал: распределение комментариев по уровням (только L4–L5 — ритуал), время до первого комментария и доля апрувов без единого комментария. Первую считает скрипт по меткам:
"""Распределение комментариев ревью по уровням — по выгрузке из GitHub/GitLab API."""
from collections import Counter
LEVELS = {"блокер": "L0-L2", "важно": "L0-L2", "вопрос": "L0-L2", "nit": "L4-L5"}
def classify(comments: list[str]) -> Counter:
"""Читает метку в начале комментария; без метки — «не размечено». O(n) по времени."""
heads = (c.strip().lstrip("[").split("]")[0].strip().lower() for c in comments)
return Counter(LEVELS.get(h, "не размечено") for h in heads)
print(classify(["[блокер] нет отката", "[nit] пробел", "апрув"]))
# Counter({'L0-L2': 1, 'L4-L5': 1, 'не размечено': 1})
Цифра, которую он печатает, обычно неприятная: в командах, не договорившихся о протоколе, доля
L4-L5 и «не размечено» уверенно держится выше 70%.
Ревью и устаревание документации
Устаревание документа — это не лень, а отсутствие повторного читателя. Мы разбирали это в первой главе и подробно разберём в «Почему документация устаревает»; здесь важен один срез: ревью — единственный момент, встроенный в процесс, когда документ читает кто-то, кроме автора. Если этот момент случается ровно один раз в жизни документа, документ и будет свежим ровно один раз.
Структурные приёмы, которые превращают ревью из разового события в повторяющееся:
- Документ в том же pull request, что и код. Тогда ревьюер кода автоматически становится
ревьюером текста, а расхождение видно в дифе.
CODEOWNERSнаdocs/добавляет владельца раздела в ревьюеры без ручных напоминаний. - Ревью при касании. Правило «трогаешь код — пройди по разделу, который его описывает» плюс чек-лист в шаблоне PR («документация: обновлена / не требуется / не нашёл»). Ответ «не нашёл» тоже полезен: он находит документы-сироты.
- Просроченный
review_byкак задача. Раз в неделю CI собирает документы с истёкшим сроком и создаёт владельцу задачу на пятнадцатиминутное ревью с тремя допустимыми исходами: подтвердить, переписать, отправить в архив. Архив — нормальный исход; ревью, у которого нет исхода «удалить», всегда только добавляет текст. - Новичок как ревьюер. Первые две недели человек — единственный настоящий внешний читатель в команде, и это окно не повторится. Его вопросы к README и онбордингу принимаются как баги, а не как «он ещё не разобрался» («Онбординг»).
- Инцидент как ревью раннбука. Если ночью по документу шли и он подвёл — правка в него входит в план постмортема наравне с изменениями кода.
- Меньше рукописного — меньше ревью. Сгенерированный из OpenAPI справочник не нуждается в вычитке: ревьюить надо генератор и примеры, а не тысячу строк, которые всё равно перегенерируются («Документация API»). Самый надёжный способ не ревьюить текст — не писать его руками.
Отдельно про схемы: диаграмма в ревью проверяется теми же уровнями, что и текст — что она утверждает, кому и не противоречит ли коду. Про это — «Схемы в документах».
Ревью, которое делается ради процесса
Честная часть, без которой глава была бы враньём. Часть ревью в вашей организации — не чтение, а подпись: способ распределить ответственность или выполнить требование регламента. Признаки, которые видно из данных, а не из ощущений:
- Апрув через восемь минут после отправки тридцати страниц. Физически невозможно прочитать; значит, апрувили не текст.
- Комментарии только уровней L4–L5 — или полное их отсутствие месяцами.
- Ревьюеры назначаются по списку, а не по роли, и список не менялся два года.
- Ревью происходит после того, как решение принято: изменить оно уже ничего не может.
- Обязательных апрувов больше трёх. Каждый следующий снижает ответственность каждого предыдущего — это диффузия ответственности, а не контроль качества.
- Никто из апрувящих не задал ни одного вопроса за квартал.
- Правки после публикации отсутствуют. Живой документ правят и после апрува, процессный не меняется никогда.
Что с этим делают:
- Разделить согласование и ревью. Это два разных действия с разными артефактами: подпись фиксирует ответственность, ревью меняет текст. Смешанные в одном поле, они дают худшее из двух: подпись без чтения и чтение без последствий. Один обязательный ревьюер по существу плюс отдельная таблица согласований работает лучше, чем шесть равноправных апрувов.
- Найти настоящего адресата. Иногда «ревьюер» — не читатель по построению: адресат документа аудитор, служба безопасности или клиент, а внутренняя подпись — просто контроль соответствия. Это законно и не всегда бессмысленно, но такой документ надо писать под аудитора и не путать с рабочим текстом («Документирование требований», «Критерии приёмки»).
- Автоматизировать проверяемое. Всё, что в чек-листе согласования проверяется формально (заполнены поля, приложены ссылки, указан владелец), проверяется скриптом на CI. После этого разговор «зачем нам три подписи» становится разговором про оставшиеся два пункта.
- Считать честное время. «Шесть ревьюеров по сорок минут на двадцать документов в квартал — это тридцать два человеко-дня в год» — единственный язык, на котором такой разговор двигается («Работа вверх»).
- Не саботировать, особенно на новом месте. Сначала выясните, кто и от чего защищается этим процессом; иногда за нелепым регламентом стоит инцидент, о котором вам не рассказали («Первые 90 дней»).
И зеркальная честность: не всякое неприятное ревью — ритуальное. Ревью, где вам сказали «вступление не работает, переписывай» — самое полезное, что с текстом может случиться, и ощущается оно ровно так же плохо, как ритуальное.
Типичные ошибки
Со стороны ревьюера:
- Комментировать запятые в разделе, который предлагаете выкинуть.
- Читать в роли «вообще инженер», а не конкретного адресата: такие комментарии всегда про стиль.
- Переписывать текст под свой голос и называть это ревью; требовать полноты вместо достаточности для решения; отдавать сорок комментариев без приоритета.
- Молчать, потому что «наверное, я чего-то не понимаю». Именно это непонимание — самый ценный сигнал: читатель через год поймёт не больше вашего.
Со стороны автора:
- Спорить с формулировкой комментария вместо факта; молча игнорировать треды.
- Отвечать на правку объяснением в чате: следующий читатель этого объяснения не увидит, а текст остался прежним. Понадобилось объяснение — оно должно попасть в документ.
- Отдавать сырой черновик двадцати людям сразу и просить ревью без роли, стадии и срока — а потом удивляться, что пришли одни запятые.
- Принимать все правки подряд: текст, собранный из чужих предпочтений, теряет голос и обычно становится длиннее ровно в тех местах, где должен был стать короче.
Практика
- Разложите последнее ревью по уровням. Возьмите тред с комментариями к любому своему документу и промаркируйте каждый комментарий L0–L5. Посчитайте долю L0–L2. Если она меньше четверти — протокол ревью в вашей команде отсутствует, и это лечится одним сообщением о ролях при следующем запросе.
- Проведите ревью по протоколу трёх проходов на чужом документе, засеките время. Отдельно выпишите обратный конспект по одному предложению на раздел и покажите его автору: часто это единственный комментарий, который нужен.
- Перепишите три своих старых комментария по схеме «метка → наблюдение → следствие для решения». Сравните, сколько уточняющих вопросов задал бы автор в старой версии и в новой.
- Заведите один линтер. Vale со словарём из десяти терминов или lychee на битые ссылки — полдня работы, после которых целый класс комментариев перестаёт исходить от людей.
- Попросите новичка отревьюить README в первую неделю и заведите его вопросы как задачи. Через месяц окно закроется навсегда.
Источники
- Conventional Comments — словарь меток для комментариев: https://conventionalcomments.org/
- Google Engineering Practices: Code Review — принципы переносятся на текст почти дословно: https://google.github.io/eng-practices/review/
- Michael Fagan. Design and code inspections to reduce errors in program development. IBM Systems Journal, 1976 — https://doi.org/10.1147/sj.153.0182
- Karl Wiegers. Peer Reviews in Software: A Practical Guide. Addison-Wesley, 2002 — https://www.karlwiegers.com/
- SmartBear. Best Practices for Peer Code Review (данные исследования Cisco) — https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/
- C. Northcote Parkinson. Parkinson’s Law, 1957 — закон тривиальности; инженерная классика на ту же тему: https://bikeshed.com/
- Colin Bryar, Bill Carr. Working Backwards, 2021 — молчаливое чтение документа на встрече.
- Google developer documentation style guide — https://developers.google.com/style
- Write the Docs, раздел docs as code — https://www.writethedocs.org/guide/docs-as-code/
- Инструменты: Vale, markdownlint, lychee, textlint, LanguageTool.
Мини-итог
- Ревью проверяет не грамотность, а работоспособность: изменит ли документ решение того, кому он адресован. Без явной роли ревьюер по умолчанию превращается в корректора.
- Правки различаются ценой, а не важностью: L0 «нужен ли документ» → L1 тезис → L2 структура → L3 факты → L4 ясность → L5 оформление. Не спускайтесь ниже, пока не закрыт верхний уровень: комментарий уровня N обесценивает всё, что ниже. Правка L1 обычно сама убирает замечания L4.
- Протокол: три минуты на шапку и вывод, десять на чтение в роли адресата с пометками, семь на обратный конспект и превращение пометок в комментарии с метками и приоритетом.
- Комментарий устроен как баг-репорт: метка → что я наблюдал как читатель → чем это грозит решению → необязательное предложение. Диагноз ревьюера надёжнее его рецепта.
- Автор отвечает на каждый комментарий одним из пяти способов: починил, решил иначе, отклонил с причиной, вынес в задачу, зафиксировал разногласие в тексте. Молчание — худший из вариантов.
- Просить ревью надо адресно: стадия, роль каждого ревьюера, границы обсуждения, срок; сначала двое, потом широкий круг. Всё, что ловится на L5, обязана ловить машина.
- Устаревание — следствие того, что документ читают один раз. Ревью встраивается повторно:
документ в одном PR с кодом,
CODEOWNERS, ревью при касании, просроченныйreview_by, новичок как внешний читатель, инцидент как ревью раннбука. - Часть ревью — подпись, а не чтение. Распознаётся по времени апрува, уровню комментариев и числу обязательных согласующих; лечится разделением согласования и ревью, поиском настоящего адресата и автоматизацией формальных проверок.
Что дальше
Мы прошли все жанры и оба конца ревью. Остался последний шаг — сделать так, чтобы письмо не держалось на энтузиазме одного человека: шаблоны, которые не мешают, ревью в определении готовности, владельцы разделов, обучение команды и набор ресурсов, к которым возвращаются.