voidlearn
 

2.4Инженерная зрелость

Разберёшь вопросы о качестве решений, неопределённости и взаимодействии с командой. Сможешь показать не идеальный ответ, а критерии выбора, проверку предположений и то, как реагируешь на обратную связь.

«Нам нужна быстрая система»

Это не требование. Требование можно сделать и потом проверить, причём проверить может не автор. «Быстро» нельзя ни сделать, ни проверить: у заказчика в голове «чтобы список сделок открывался мгновенно», у тебя «p99 меньше 200 мс», а у нагрузочного теста вообще среднее по всем ручкам за сутки. Три разных мира, и на приёмке они встретятся.

Джун в этом месте идёт оптимизировать: добавляет индекс, ставит кэш, крутит пул соединений. Иногда угадывает. Мидл сначала задаёт вопросы, и не потому, что вредный, а потому что у слова «быстро» есть цена, и её платит бизнес. Разница между 500 мс и 200 мс на одной ручке может стоить недели работы, а между 200 мс и 50 мс - переписывания хранилища. Прежде чем платить, надо знать, за что.

Без чего требование остаётся пожеланием

Метрика + перцентиль + значение + условия нагрузки + окно наблюдения + способ измерения. Пример: «время ответа GET /api/deals - p95 не больше 300 мс при 400 RPS и 5 млн сделок в базе, считаем по гистограмме в Prometheus, окно - 30 дней, доля удачных интервалов 99%». Вот это можно принять или не принять, и спора на приёмке не будет.

Полный чеклист уточняющих вопросов

Вопрос заказчикуЗачем его задаватьЧто бывает, если не спросить
Что значит «быстро» и в каких единицах? Миллисекунды, секунды, «пока человек не заметил»?Чтобы перевести ощущение в число. Пока «быстро» не стало цифрой, договариваться не о чем: у тебя и у заказчика в голове разные значения этого словаДелаешь 300 мс, а ждали 50 мс - или наоборот, месяц пилил 50 мс, когда хватило бы секунды
Для какой доли запросов? Среднее, p95, p99 или «вообще всегда»?Чтобы знать, по какой доле запросов тебя будут судить. Среднее прячет медленный хвост, а «вообще всегда» стоит в разы дороже, чем p99Среднее 120 мс, а каждый сотый пользователь ждёт 4 секунды - и жалуется именно он
При какой нагрузке? Сколько RPS в обычный день, сколько в пик, когда бывает пик?Чтобы число вообще что-то значило: время ответа без указанной нагрузки - это скорость на пустом стенде, к проду она отношения не имеетПомерили на пустом стенде, в проде в понедельник утром всё легло
На каком объёме данных? Сейчас и через год.Чтобы решение не протухло через полгода: с ростом таблицы Postgres выбирает другой план запроса, и вчерашний индекс перестаёт помогатьНа 100 тысячах строк фильтр отбирает горстку строк и индекс работает, а на 20 млн тот же фильтр возвращает миллионы, и планировщик читает таблицу целиком
На каких сценариях? Какие конкретно экраны, ручки, отчёты?Чтобы понимать, что именно оптимизировать: «вся система» - это сотни ручек, и ускорить их все нельзя ни за какие деньгиУскорил то, чем пользуются двое, а тормозил список сделок у всего отдела продаж
Какой бюджет и срок? Неделя, квартал, «до конца года»?Чтобы выбрать класс решения. За неделю можно переписать запрос и добавить индекс; за квартал - завести кэш, реплику для чтения или сменить хранилищеПриносишь план на два месяца там, где ждали трёх дней
Что можно ухудшить взамен? Свежесть данных, консистентность, стоимость железа, часть функциональности?Чтобы знать, чем разрешено платить. Бесплатной скорости не бывает, и без ответа на этот вопрос ты сам себе запрещаешь кэш, денормализацию и чтение с репликиНе даёшь себе права на кэш и на лишнюю реплику для чтения, хотя заказчик легко согласился бы на минуту устаревания данных
Как будем измерять и кто подтвердит? Метрика, дашборд, кто смотрит на приёмке?Чтобы «сделано» не осталось вопросом веры: нужен график и живой человек, который по нему скажет «принято»Ты считаешь, что сделал; заказчик говорит «всё равно медленно», и спор нечем закрыть
Что уже пробовали и почему не помогло?Чтобы не пройти чужой путь заново. Один вопрос экономит неделю работы и заодно показывает, какие ограничения в системе уже известныСтавишь кэш, который год назад уже ставили и сняли из-за рассинхрона
От «сделайте побыстрее» к измеримому SLO «Хотим быструю систему» нельзя ни сделать, ни проверить 1. Что значит «быстро»? В каких единицах - миллисекунды, секунды, «пока не заметно»? 2. Для какой доли запросов? Среднее прячет хвосты. p95 или p99 - разница в цене. 3. При какой нагрузке? RPS в обычный день и в пик, когда именно бывает пик. 4. На каком объёме данных? Сейчас и через год: план запроса меняется вместе с таблицей. 5. На каких сценариях? Конкретные ручки и экраны, а не «вся система». 6. Бюджет и срок? Неделя и квартал - это разные классы решений. 7. Чем можно платить? Свежесть данных, деньги на железо, часть сценариев. 8. Как измеряем и кто примет? Метрика, дашборд, человек, который скажет «принято». 9. Что уже пробовали? И почему это не помогло - экономит неделю работы. Измеримый SLO - его уже можно принять или не принять p95 GET /api/deals не более 300 мс при 400 RPS и 5 млн сделок, окно 30 дней, 99% интервалов Метрика: гистограмма в Prometheus. Дашборд в Grafana. Приёмка: продакт по этому дашборду.
Уточнение здесь не бюрократия, а перевод. На входе фраза, за которую никто не может отвечать; на выходе строчка, которую можно повесить на дашборд и по которой любой человек через полгода скажет, выполняется она или нет. Девять вопросов занимают пятнадцать минут разговора и экономят недели работы не туда.
Как это выглядит в жизни за пятнадцать минут

Заказчик: «CRM тормозит, надо ускорить». Начинаешь разматывать, и почти всегда за общей фразой оказывается один конкретный сценарий и один конкретный человек, который жалуется.

  • «Что именно тормозит?» → «Список сделок у продажников».
  • «Когда?» → «Утром в понедельник и в конце месяца».
  • «Насколько сейчас и сколько было бы нормально?» → «Крутится секунд пять, хотелось бы, чтобы сразу».
  • «"Сразу" - это меньше секунды?» → «Да, чтобы за секунду».
  • «У всех или у некоторых?» → «У руководителей отделов, у них фильтр по всей воронке».
  • «Данные могут отставать на минуту?» → «Да, это не отчётность, это рабочий список».

Итог: «p95 списка сделок с фильтром "все сделки отдела" - не больше 1 секунды в пик понедельника при текущем объёме и полуторном на год вперёд; допустимо отставание данных до минуты». Из размытого «тормозит» получилось требование, у которого есть и цена, и способ проверки. Заодно выяснилось, что можно кэшировать, а это меняет решение целиком.

Подвох: одной latency интервьюеру мало

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

«Как ты ревьюишь чужой код»

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

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

Что я делаю до первого комментария
  • Читаю задачу, а не один диф. Половина серьёзных замечаний звучит как «код написан хорошо, но решает не ту задачу» или «решает только половину». Из дифа этого не видно.
  • Смотрю на размер. Если PR больше пяти-шести сотен строк осмысленных изменений, честнее сразу написать «давай разобьём», чем делать вид, что я это внимательно прочитал. Ревью на восемьсот строк всегда идёт с пропущенными багами, сколько ни старайся.
  • Прохожу диф целиком один раз без комментариев, чтобы понять замысел, и только потом иду вторым проходом придираться. Иначе первые двадцать комментариев будут про то, что автор уже решил тремя файлами ниже.
  • Отдельно открываю миграции, конфиги и всё, что трогает прод-инфраструктуру: там цена ошибки на порядок выше, чем в бизнес-логике.

Порядок приоритетов: сверху вниз по цене ошибки

УровеньНа что смотрюТипичная находка в нашей CRM
1. Правильно ли понята задачаДелает ли код то, что просили, и всё ли из того, что просилиСделали смену статуса сделки, но забыли, что при переходе в «выиграна» надо писать событие в Kafka - интеграция с биллингом не узнает
2. Что будет в продеОшибки не проглочены, контексты пробрасываются, тело ответа закрывается, горутины завершаются, ретраи идемпотентныdefer resp.Body.Close() забыт в ветке с ошибкой; горутина без ограничения времени висит до конца процесса
3. Данные и запросыN+1, отсутствующий индекс, транзакция вокруг сетевого вызова, длинные блокировки, выборка без лимитаСписок сделок тянет последний контакт отдельным запросом на каждую строку; в транзакции сидит вызов внешнего API на пару секунд
4. Совместимость и откатМиграция совместима со старым кодом? Контракт API не сломан? Формат события в Kafka читается старыми консьюмерами? Как откатываем?DROP COLUMN в том же релизе, что и код, который перестал ей пользоваться, - откат становится невозможным
5. Безопасность и приватностьВалидация входа, проверка прав на объект, конкатенация SQL, персональные данные в логахРучка отдаёт сделку по id без проверки, что она принадлежит отделу пользователя; телефон клиента улетает в лог целиком
6. НаблюдаемостьЕсть ли логи с контекстом, метрика на новый путь, понятно ли будет по дашборду, что это сломалосьНовый фоновый воркер без единой метрики: узнаем, что он встал, от менеджеров
7. ТестыЕсть ли тест на исправленный баг и на неочевидные ветки; можно ли это вообще протестироватьФикс без теста - значит, тот же баг вернётся через полгода и никто не заметит
8. ЧитаемостьИмена, размер функции, смешение уровней абстракции, мёртвый кодФункция на 200 строк, где HTTP-разбор, бизнес-правила и SQL живут вперемешку
9. СтильНичего не смотрю руками - это gofmt, golangci-lint и CIСпор о переносах строк в комментариях к PR - верный признак, что линтер не настроен
Ревью сверху вниз: от того, что дорого чинить, к тому, что чинит робот 1. Решает ли код ту задачу - и всю ли Читаю тикет, а не только диф. Половина настоящих замечаний живёт здесь. 2. Что будет в проде ночью Проглоченные ошибки, потерянный context, вечные горутины, неидемпотентный ретрай. 3. Данные и запросы N+1, нет индекса, сетевой вызов внутри транзакции, выборка без LIMIT. 4. Совместимость и возможность отката Миграция и старый код, контракт API, формат события в Kafka, кнопка «назад». 5. Безопасность и персональные данные Права на объект, валидация, склейка SQL, телефон клиента в логе. 6–7. Наблюдаемость и тесты Метрика на новый путь; тест на исправленный баг, иначе он вернётся. 8. Читаемость Имена, длина функции, смешанные уровни абстракции. 9. Стиль и форматирование Сюда человек не смотрит вообще: gofmt и golangci-lint в CI. цена ошибки падает вниз чем выше пункт, тем дороже пропустить: верхние три - это инцидент в проде, нижние два - это просто неудобно читать здесь человек не нужен
Иерархия важнее списка. Любой кандидат назовёт те же девять пунктов, разница в порядке. Тот, кто начинает с именования переменных, ревьюит текст; тот, кто начинает с «а что будет при откате», ревьюит систему. Заодно это ответ на вопрос, куда девать время: на PR в двести строк у тебя есть минут двадцать, и потратить их надо на верхние четыре уровня.

Как комментировать, чтобы это работало

Четыре приёма, которые снимают 90% боли код-ревью
  • Помечать вес комментария. Три метки. blocker («блокирующее»): без этого не мержим. вопрос: я не понял, объясни. nit: вкусовщина, можешь проигнорировать. Без меток все двадцать замечаний для автора весят одинаково, и он либо переделывает всё, либо не делает ничего.
  • Спрашивать, а не приказывать. «А что будет, если сюда придёт пустой список?» вместо «тут баг». В половине случаев выясняется, что ты не прав, и вопрос спасает лицо обоим. Плюс автор сам находит проблему, а такое запоминается лучше.
  • Если крупных замечаний больше пяти, идти в звонок. Письменный пинг-понг на десять итераций съедает дни и портит отношения. Пятнадцать минут голосом решают то же самое, а в PR остаётся короткое резюме договорённости.
  • Писать, что понравилось. Одна строчка «вот эту развязку с интерфейсом я себе заберу» меняет тон всей ветки. Ревью, где бывают только замечания, люди начинают избегать.
Что выдаёт джуна в ответе про ревью

Первое: начать с именования и форматирования. Значит, человек не видел, как пропущенный на ревью N+1 кладёт базу. Второе: «я проверяю, что код чистый и соответствует SOLID». Это лозунг, а не проверка. Третье: ни слова про людей. Ревью держится на коммуникации, и если кандидат не упомянул, как он доносит замечания, интервьюер запомнит именно это. Четвёртое: «я всегда всё проверяю досконально». На PR в тысячу строк это физически неправда, и опытный интервьюер это знает.

Как принимаешь технические решения и объясняешь их бизнесу

Принять техническое решение значит выбрать из нескольких вариантов при ограничениях. Если у тебя в рассказе один вариант, ты не принимал решение, ты просто сделал первое, что пришло в голову. Поэтому в сильном ответе всегда есть альтернативы, критерии выбора и то, что заставит вернуться и пересмотреть.

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

На собесах эту пару называют так: обратимое решение - двусторонняя дверь, необратимое - односторонняя.

Шаги, по которым это можно рассказать вслух
  1. Что за задача и по какой метрике поймём, что решили. Без метрики дальше нет смысла.
  2. Два-три реальных варианта. Обязательно с вариантом «ничего не делать» или «сделать самое дешёвое»: он часто выигрывает.
  3. Критерии: срок, риск, стоимость поддержки, обратимость, кто это будет эксплуатировать в три часа ночи.
  4. Данные, а не мнения. EXPLAIN ANALYZE, замер на копии прода, прототип на день, что угодно, лишь бы спор превратился в число.
  5. Решение и запись. Короткий ADR или хотя бы абзац в задаче: что выбрали, почему, что отвергли и по какой причине.
  6. Условие пересмотра. «Возвращаемся, если p99 уйдёт за 500 мс» или «если объём вырастет вдвое».

С объяснением бизнесу правило одно: говорить в деньгах, времени и риске, а не в технологиях. Бизнесу нечего делать с фразой «нужно вынести отчёты в отдельный сервис». Ему есть что делать с фразой «сейчас тяжёлые отчёты тормозят работу продажников в конце месяца - это тот же час, когда закрываются сделки». Дальше всегда идут варианты с ценой, а не отказ: «за неделю сделаем вдвое быстрее, за месяц сделаем в десять раз быстрее и уберём влияние на основную работу». Слово «нет» бизнес слышит как «инженер не хочет», а «вот три варианта и их цена» - как предложение выбрать.

Про техдолг разговаривать так же

«Надо порефакторить» звучит как просьба дать время непонятно на что, и её справедливо отклоняют. Работающая формулировка привязывает техдолг к тому, что бизнес уже чувствует: «каждая новая интеграция сейчас занимает три недели вместо одной, потому что весь обмен с внешними системами прибит к одному пакету; если потратим две недели на разделение, следующие интеграции пойдут по неделе - в ноль выйдем уже на первой». Это уже не рефакторинг ради красоты, а инвестиция с понятной точкой окупаемости.

Вопросы

3
Суть: не идти оптимизировать, а перевести ощущение в измеримое: метрика, перцентиль, нагрузка, объём данных, конкретные сценарии, срок и бюджет, чем можно платить и кто подтвердит, что готово. Два вопроса, которые отличают сильный ответ: «зачем это на самом деле» и «что можно ухудшить взамен».

Каркас ответа

  1. Сказать вслух, что это пока не требование. «Быстро» нельзя ни сделать, ни принять, значит, первым делом я перевожу его в проверяемую формулировку.
  2. Вопросы про число: что значит «быстро» и в каких единицах, для какой доли запросов (среднее / p95 / p99), при какой нагрузке, на каком объёме данных, на каких конкретно сценариях.
  3. Вопрос «зачем»: кто именно жалуется, что он в этот момент делает, что бизнес из-за этого теряет. Часто оказывается, что скорость только симптом, а болит другое.
  4. Вопрос про цену: срок и бюджет, а главное, чем можно платить (свежесть данных, консистентность, деньги на железо, часть функциональности).
  5. Вопрос про приёмку: какая метрика, на каком дашборде, кто скажет «принято».
  6. Закончить сформулированным SLO: показать, как из фразы получается строчка, которую можно проверить.
Образец ответа

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

Начну не с чисел, а с "зачем": что именно тормозит, кто жалуется и что этот человек в тот момент делает. У нас в CRM почти всегда за фразой "система тормозит" стоит один конкретный экран и один конкретный отдел. В последний раз это оказался список сделок у руководителей отделов, у которых фильтр по всей воронке, - и только утром в понедельник и в конце месяца. Это сразу сузило задачу с "всей системы" до одной ручки.

Дальше числа. Что значит "быстро" в секундах: сейчас крутится пять секунд, а нормально - чтобы за секунду. Для какой доли запросов: среднее прячет хвосты, поэтому я всегда переспрашиваю, говорим ли мы про p95 или p99, - разница между ними может стоить месяца работы. При какой нагрузке: сколько RPS в обычный день и в пик, когда бывает пик. На каком объёме: сейчас пять миллионов сделок, а через год? Планировщик Postgres на выросшей таблице легко уходит в seq scan там, где вчера был индекс.

Потом вопрос, который задают реже всего и который меняет решение целиком: чем можно платить. Можно ли показывать данные с отставанием в минуту? Если да - мне открылись кэш, материализованное представление и чтение с реплики. Если нет и нужна строгая свежесть - это совсем другой класс решений и другая цена. В том случае с CRM ответ был "это рабочий список, а не отчётность, минута отставания нормальна", и это определило всё дальнейшее.

И два закрывающих вопроса: какой срок и бюджет - неделя и квартал допускают разные решения; и как мы будем измерять и кто подтвердит приёмку, потому что без метрики "сделано" остаётся вопросом веры. Заодно спрошу, что уже пробовали и почему это не помогло: пару раз это спасало меня от повторения чужого пути.

В итоге из "хотим быструю систему" получается строчка, которую можно повесить на дашборд: p95 списка сделок с фильтром "все сделки отдела" - не больше одной секунды при текущей нагрузке в пик понедельника и полуторном объёме данных на год вперёд; допустимо отставание данных до минуты; метрика - гистограмма в Prometheus, приёмка - продакт по дашборду в Grafana. Вот с этим я уже готов идти что-то делать.»

Чего говорить нельзя

  • Сразу предлагать решения. «Поставим Redis, добавим индексы, поднимем реплику». Классический провал: кандидат оптимизирует то, чего не измерил, и, скорее всего, не то.
  • «Сделаем максимально быстро». Это обещание без цены и без критерия приёмки.
  • Спрашивать только про latency. Без вопросов «зачем» и «чем можно платить» ответ выглядит как чек-лист, а не как понимание.
  • Задавать вопросы бесконечно. Если на этом всё и закончилось, кандидат выглядит как человек, который умеет уточнять, но не умеет доводить. Обязательно закончить сформулированным требованием.
  • Обещать «всегда быстро». Требование «всегда меньше 100 мс» без перцентиля практически недостижимо: всегда найдётся GC-пауза, ретрай или холодный кэш.

Чем добить

Скажи, что после согласования SLO ты первым делом измеряешь, а не оптимизируешь: вешаешь гистограмму по этой ручке в Prometheus и панель в Grafana. Дальше два аргумента, которые звучат по-взрослому. «Бесплатной скорости не бывает»: за неё всегда платят свежестью данных, деньгами на железо или сложностью поддержки, и весь разговор нужен, чтобы выяснить, чем именно заказчик готов заплатить. И второй, «у скорости есть точка, после которой пользователь не замечает разницы»: если 210 мс и 40 мс для человека неотличимы, второй вариант не стоит своей инвалидации кэша. Так ты показываешь, что оптимизируешь бизнес-эффект, а не число на графике.

Суть: назвать явную иерархию по цене ошибки - задача, прод, данные, откат, безопасность, наблюдаемость, тесты, читаемость, и только в самом низу стиль, который смотрит линтер, а не человек. Плюс обязательно - как ты общаешься в комментариях: метки blocker/вопрос/nit, вопросы вместо приговоров, звонок вместо десятой итерации.

Каркас ответа

  1. Назвать принцип, а не список. «Иду сверху вниз по цене ошибки».
  2. Что делаю до комментариев: читаю задачу, смотрю размер PR, прохожу диф целиком один раз молча.
  3. Верхние уровни подробно: та ли задача решена → что будет в проде → данные и запросы → совместимость и откат → безопасность.
  4. Нижние уровни коротко: наблюдаемость, тесты, читаемость; стиль отдаю линтеру.
  5. Как коммуницирую. Половина ответа: ревью про людей, а не про текст.
  6. Один живой пример находки, он делает весь ответ достоверным.
Образец ответа

«У меня есть порядок, и он про цену ошибки: сверху то, что дорого чинить в проде, снизу то, что вообще не должен смотреть человек.

До первого комментария я делаю три вещи. Открываю задачу - довольно часто самое важное замечание звучит как "код хороший, но решает половину задачи", и из дифа это не видно. Смотрю на размер: если там больше пятисот-шестисот содержательных строк, я честно пишу "давай разобьём", потому что ревью такого объёма - это ревью с пропущенными багами, как ни старайся. И прохожу диф целиком один раз без комментариев, чтобы понять замысел, иначе первые десять замечаний окажутся про то, что автор решил тремя файлами ниже.

Дальше по уровням. Первое - та ли задача решена и вся ли: например, статус сделки меняется, но при переходе в "выиграна" не пишется событие в Kafka, и биллинг об этом не узнает. Второе - что будет в проде: проглоченные ошибки, потерянный context, незакрытое тело ответа в ветке с ошибкой, горутина без таймаута, неидемпотентный ретрай. Третье - данные: N+1, запрос без индекса, выборка без лимита, сетевой вызов внутри транзакции, который держит блокировку секунды. Четвёртое - совместимость и откат: совместима ли миграция со старым кодом, ведь при раскатке несколько минут в проде живут две версии; не сломан ли контракт API; прочитают ли старые консьюмеры новое событие; и есть ли вообще возможность откатиться. Пятое - безопасность и персональные данные: проверяются ли права на конкретный объект, а не только факт авторизации, не улетает ли телефон клиента в лог целиком.

Ниже - наблюдаемость и тесты. Новый фоновый воркер без метрики значит, что о его падении мы узнаем от менеджеров, а не от алерта. Фикс бага без теста значит, что баг вернётся. Ещё ниже - читаемость: имена, длина функции, смешанные уровни абстракции. А стиль и форматирование я не смотрю вообще, это gofmt и golangci-lint в CI; если люди спорят в PR о переносах строк, значит, линтер не настроен, и чинить надо это.

Отдельно - как я пишу комментарии, для меня это половина дела. Я помечаю вес: blocker - без этого не мержим, вопрос - я не понял, объясни, nit - вкусовщина, можно проигнорировать. Без меток автор видит двадцать одинаковых замечаний и либо переделывает всё, либо забивает. Формулирую вопросом: "а что будет, если сюда придёт пустой список?" - потому что в половине случаев прав автор, и вопрос спасает лицо нам обоим. Если крупных замечаний больше пяти, иду в звонок на пятнадцать минут вместо десяти итераций переписки, а в PR оставляю резюме договорённости. И стараюсь написать, что понравилось, - ревью, где бывают только замечания, люди начинают избегать.

Из недавнего: коллега добавлял выгрузку сделок в отчёт, код был аккуратный, но внутри цикла по сделкам подтягивался последний контакт отдельным запросом. На тестовых данных - двадцать сделок и незаметно, в проде у крупного отдела - несколько тысяч. Я не написал "тут N+1, исправь", а спросил: "сколько сделок бывает у самого большого отдела и что будет с этим циклом на таком объёме?" Он сам посчитал, переделал одним запросом с джойном, и мы заодно договорились, что такие места проверяем на реальных объёмах.»

Чего говорить нельзя

  • Начинать с именования и форматирования. Мгновенно читается как «ревьюил только учебные проекты».
  • «Проверяю чистоту кода и SOLID». Лозунг вместо процедуры. Если говоришь про принципы, называй, какое конкретно нарушение и чем оно навредит.
  • «Я всегда всё проверяю досконально». На PR в тысячу строк это неправда, и интервьюер это знает. Взрослый ответ звучит иначе: «я говорю, что не могу качественно отревьюить такой объём».
  • Ни слова про коммуникацию. В ревью всегда двое; кандидат, который описал только техническую часть, выглядит как источник конфликтов.
  • «Отклоняю PR, если не нравится подход». Без критериев это вкусовщина с правом вето. Нравится/не нравится тянет максимум на nit, а блокирует только то, что сломает прод или контракт.
  • Ревью как способ показать, что ты умнее. Тон «а вот здесь ты не знаешь, как работает планировщик» для любого тимлида красный флаг.

Чем добить

Скажи, что часть замечаний нужно убирать из ревью в автоматику: если одно и то же повторяется третий раз, это не тема для комментария, а тема для линтера, шаблона PR или чек-листа. У нас так появилась проверка на опасные миграции в CI (DROP COLUMN, RENAME, CREATE INDEX без CONCURRENTLY), и эти замечания просто перестали существовать как ручная работа. Второй сильный ход: сказать, что ревью для тебя ещё и способ распространять знание о системе. Читая чужие PR, ты держишь в голове, что происходит в соседних частях CRM, и это окупается на инцидентах.

Суть: решение - это выбор из вариантов при ограничениях, а не первое пришедшее в голову. Критерии: срок, риск, стоимость поддержки, обратимость, кто будет это эксплуатировать ночью. Записать решение и условие пересмотра. Бизнесу говорить в деньгах, времени и риске - и всегда давать варианты с ценой, а не слово «нет».

Каркас ответа

  1. Метрика успеха: по чему поймём, что решили задачу.
  2. Два-три реальных варианта, обязательно с самым дешёвым и с «ничего не делать».
  3. Критерии выбора, среди них обязательно обратимость: односторонняя дверь или двусторонняя.
  4. Данные вместо мнений: замер, прототип на день, EXPLAIN.
  5. Запись решения: ADR или абзац в задаче, что выбрали, что отвергли и почему.
  6. Условие пересмотра.
  7. Перевод для бизнеса: те же варианты на языке денег, сроков и риска.
Образец ответа

«Для меня решение - это выбор из вариантов при ограничениях. Если я могу назвать только один вариант, значит, я не принимал решение, а сделал первое, что пришло в голову. Поэтому у меня есть простой порядок.

Сначала метрика: по чему мы поймём, что задача решена. Потом два-три варианта, причём я всегда держу среди них самый дешёвый и вариант "ничего не делать" - они выигрывают чаще, чем кажется. Дальше критерии: срок, риск, стоимость поддержки и обратимость. Обратимость для меня главный: есть решения, откуда легко вернуться - добавить кэш, поменять библиотеку, - их надо принимать быстро и не устраивать вокруг них совет. И есть решения, откуда вернуться почти нельзя: разрезать монолит, сменить хранилище, зафиксировать публичный контракт API. Вот их я принимаю медленно, письменно и не в одиночку. Смешивать эти два режима - самая частая ошибка: команда неделю спорит про имя пакета и за час на созвоне решает разрезать сервис.

Дальше стараюсь заменить мнения данными: снять EXPLAIN ANALYZE, прогнать замер на копии прода, собрать прототип за день. Спор "кэш или индекс" мы так закрыли за час: замер показал 210 мс на переписанном запросе, и обсуждать стало нечего. Решение записываю коротко - что выбрали, что отвергли и почему; через полгода это единственное, что спасает от разговора "а почему у нас так сделано". И обязательно фиксирую условие пересмотра: "возвращаемся к кэшу, если p99 уйдёт за 500 мс". Это превращает решение из вечного в обратимое, и людям легче согласиться на не свой вариант, когда есть точка возврата.

С бизнесом правило одно: говорить в деньгах, времени и риске, а не в технологиях. Фраза "нужно вынести отчёты в отдельный сервис" для продакта не значит ничего. Работает другое: "тяжёлые отчёты сейчас тормозят работу продажников в конце месяца - ровно в тот час, когда закрываются сделки". И дальше всегда варианты с ценой: "за неделю читаем отчёты с реплики, станет вдвое быстрее и перестанет мешать основной работе; за месяц делаем отдельный сервис, будет в десять раз быстрее и появится история по срезам. Предлагаю сейчас первое, а второе - обсудить в квартальном плане". Я почти никогда не говорю "нет": бизнес слышит в этом "инженер не хочет". Вместо этого - "можно, и вот сколько это стоит", а решение пусть принимает тот, кто отвечает за деньги.

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

Чего говорить нельзя

  • «Я выбираю лучшее решение». Лучшего не бывает, бывает подходящее под ограничения. Ответ без слова «trade-off» звучит незрело.
  • «Смотрю, как принято в индустрии» / «так делают в больших компаниях». Аргумент по авторитету вместо аргумента по контексту.
  • Технические термины бизнесу. «Нам нужен шардинг, потому что вертикальное масштабирование упёрлось» гарантированно кончается репликой «делай как знаешь, только не мешай», а потом «почему это заняло два месяца».
  • Слово «нет» без вариантов. Отказ без цены выглядит как нежелание работать.
  • Решать всё в одиночку. Через односторонние двери идут вместе с командой, иначе это не зрелость, а самоуверенность.
  • Пугать бизнес. «Всё упадёт, если не переписать» без цифр звучит как шантаж, и его быстро перестают воспринимать всерьёз.

Чем добить

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

Что стоит запомнить

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

Частые ошибки

  • Слова «лучше» и «быстрее» без метрики не задают критерий завершения.
  • Уверенность без проверки предположений маскирует риск, но не снижает его.

Самопроверка

Сначала реши задачу самостоятельно, затем открой ответ и сравни ход рассуждений.

Применение

1. Уточни требование «сделать быстрее»

Заказчик просит ускорить экран заказов. Перед началом реализации задай три коротких вопроса, которые позволят проверить результат. Известно только, что экран иногда «долго грузится».

Ответ и разбор

Например: «Какой именно сценарий экрана важен? Какую задержку и для какого percentile считаем приемлемой? При какой нагрузке и объёме данных проверяем?» Затем согласуй источник измерения и кто принимает результат. Без конкретных чисел не выдумывай SLO; сначала собери базовую метрику.

Применение

2. Объясни выбор кэша с условиями пересмотра

Предложено кэшировать карточки клиентов на 10 минут. Данные об изменениях доходят через outbox с задержкой до 2 минут; клиентский экран может показывать устаревшие данные до 5 минут. Сформулируй решение и условие, при котором вернёшься к нему.

Ответ и разбор

Сначала договориться, что допустимая устарелость 5 минут относится к этому экрану; TTL 10 минут один не гарантирует это ограничение. Можно инвалидировать ключ после обработки outbox и оставить небольшой TTL как защиту от забытых записей. Проверять возраст данных и задержку доставки; если p99 становится больше согласованного лимита либо появляются жалобы на устаревшие карточки, пересмотреть схему. Не утверждай, что outbox мгновенный.

Применение

3. Ответь на замечание code review

Ревьюер пишет: «Этот retry может повторить списание после таймаута. Нужен idempotency key». Ты проверил код и видишь, что провайдер поддерживает ключ, но endpoint пока передаёт новый случайный ключ на каждую попытку. Составь ответ и следующий шаг.

Ответ и разбор

«Да, сейчас ключ генерируется на каждую попытку, поэтому провайдер не свяжет повторы одной операции. Переделаю: создам ключ на уровне команды списания и передам тот же ключ при всех retry; добавлю тест, что повторный вызов получает тот же ключ». Это признаёт конкретный дефект и называет проверяемую правку. Не включай retry для операции до этого изменения.

Применение

4. Сформулируй результат точно

Команда предлагает заменить PostgreSQL на отдельное хранилище ради «масштабируемости». Пока известны только 20 тысяч запросов в день и один медленный отчёт. Какой безопасный следующий шаг предложить вместо немедленной миграции?

Ответ и разбор

Сначала определить сценарий, который не укладывается в ожидания, измерить его на production-подобном объёме и проверить query plan. Сравнить индекс/переписывание запроса с целевым критерием и затратами миграции. 20 тысяч запросов в день сами по себе не доказывают, что PostgreSQL не справляется; смена хранилища нужна только при измеренном ограничении и понятном выигрыше.