explainer · adi

Перед тем как чинить - выпишите три версии причины: приём следователя, который превращает 30 минут случайного фикса в 5 минут точной диагностики

Хороший следователь не фиксируется на первой версии. Он выдвигает три-четыре, описывает проверяемые следы, идёт проверять. Тот же приём - ADI - превращает торопливый фикс не туда в десять минут честной диагностики. Разбираю на двух кейсах: падение конверсии и медленный поиск.

Знакомый сценарий

Понедельник, 10:42. Менеджер по продукту открывает чат и видит сообщение от аналитика: «У нас вчера в checkout конверсия упала с 4.2% до 3.7%. Уже два дня так».

Через сорок минут в чате уже четыре человека. Разработчик пишет: «Я знаю, в чём дело - мы на прошлой неделе раскатили новую платёжную форму. Иду откатывать».

Менеджер кивает: рефлекс правильный. Откатывают форму. Конверсия не возвращается. Тратят ещё день на разбор бэкенда. Не помогает. На третий день кто-то от скуки открывает поведенческую запись на мобильном - и оказывается, что после релиза перетряхнули вёрстку нижней части экрана, и кнопка «Оплатить» уехала под клавиатуру на старых Android. До неё не дотянуться большим пальцем.

Платёжная форма ни при чём. Откат стоил один день и сместил релиз. Реальная починка - три строки CSS, пять минут работы.

Эту картину я видел в десятке команд. Хороший разработчик. Правдоподобная гипотеза. Бросок в починку. Не та работа. Я хочу разобрать, почему так получается и какой простой приём это останавливает - без необходимости менять весь процесс команды.

Почему первая версия почти всегда привычная

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

В разработке у этого якоря есть свой характер. Он почти всегда - то, что вы недавно трогали. Раскатили новую форму на прошлой неделе - она первая на подозрении. Меняли индексы в базе - значит, проблема в индексах. Подключили новую библиотеку - значит, она ломает сборку. Это разумно: то, что менялось, чаще ломается. Но это рассуждение работает только статистически, в долгой перспективе. На каждой конкретной задаче оно одинаково часто промахивается.

Второй виновник - желание побыстрее закрыть тикет. Мозг устроен так, что неопределённость - это болезненное состояние. Любая гипотеза, которая обещает понятный план действий, ощущается как облегчение. «Откатить форму» - это план. «Сначала разобраться» - это ещё одно неопределённое состояние.

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

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

Версия, две, три

Хороший следователь не фиксируется на первой версии. На месте преступления он выдвигает три-четыре, для каждой описывает, какие следы должны быть, если она верна, и идёт проверять. Версия, которая выдержала все проверки, остаётся.

В разработке у этого приёма есть короткое имя - ADI (выписать три версии причины, для каждой описать проверяемое следствие, проверить фактами). Имя не важно. Важна логика: между «первая мысль» и «начал чинить» вставляется десять минут, в которые человек проговаривает не одну, а три гипотезы - и сразу пишет, как каждую проверить.

Почему именно три, а не две и не одна?

Одна версия - это не мышление, это рефлекс. Если в голове только она, проверять нечего: всё, что вы найдёте, будет толковаться в её пользу.

Две - это ложный выбор. «Это или платёжная форма, или бэкенд». На этом этапе мозг уже сужает картину до бинарного выбора и отбрасывает всё, что не вписывается. Классический пример: «нам нужен Redis или Memcached?» - это не выбор между альтернативами, это выбор между двумя сортами одной альтернативы (внешний кэш). А реальный выбор - нужен ли вообще внешний кэш или достаточно профилировать запросы.

Три - это минимум, который ломает коридор. Третья версия почти всегда заходит с неожиданной стороны и заставляет проверять её отдельно. Часто она и оказывается правдой. Не потому что в третьей какая-то магия - а потому что первая и вторая попадают в одну колею, а третья выбивается из неё.

Если получается выписать четыре - отлично. Меньше трёх - значит вы ещё не вышли из якоря.

Конкретный пример: падение конверсии

Вернёмся к той самой команде с упавшей конверсией. Что было бы, если бы они вместо «иду откатывать» потратили десять минут на ADI.

Они бы выписали три версии.

Версия первая. Виновата новая платёжная форма, раскатанная на прошлой неделе. Проверяемое следствие: падение конверсии должно совпадать по времени с релизом формы и быть равномерным по всем устройствам и платёжным методам. Метрика для проверки: разбить конверсию за последнюю неделю по дням и по устройствам, посмотреть на резкий перепад.

Версия вторая. Виноват внешний платёжный поставщик. Проверяемое следствие: должны быть ошибки на стороне поставщика, видимые в логах; время отклика API должно вырасти; на стороне поставщика должны быть инциденты в их status page. Метрика: % ошибок 4xx/5xx от поставщика за последнюю неделю, p95 времени отклика.

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

Десять минут на формулировку. Дальше - двадцать минут на проверку. Метрика по версии первой: конверсия упала равномерно по всем устройствам? Нет, на десктопе она не изменилась. Версия первая опровергнута за пять минут - даже не пришлось ничего откатывать. Метрика по версии второй: ошибки поставщика на нормальном уровне, время отклика стабильно. Версия вторая опровергнута за две минуты, просто открыли дашборд. Метрика по версии третьей: открыли поведенческую запись на мобильном - и сразу видно, что пользователи доходят до экрана оплаты, скроллят, не находят кнопку, выходят.

Полчаса от сообщения аналитика до точного диагноза. Три строки CSS - ещё двадцать минут. К обеду релиз исправлен, конверсия восстановлена.

Команда без ADI потратила два с половиной дня и сместила следующий релиз. Команда с ADI - два часа и одну строку в журнале изменений. Разница не в скорости работы рук, а в том, в каком направлении эти руки работают.

Тот же приём в инженерной задаче: медленный поиск

Чтобы было видно, что приём не ограничен продуктовыми кейсами, разберу второй - короче.

Команда делает приложение для заметок. Появилось требование: поиск по 50 тысячам заметок должен возвращать результаты быстрее 100 миллисекунд. Сейчас - 800 миллисекунд. Разработчик пишет: «понятно, наш текстовый индекс не справляется, иду подбирать другой движок».

Десять минут на ADI остановили бы эту траекторию. Три версии:

Версия первая. Не справляется индекс. Следствие: профайлер должен показать, что больше всего времени уходит на поиск по индексу. Проверка: один запуск EXPLAIN ANALYZE или эквивалент в вашей базе.

Версия вторая. Не справляется ранжирование результатов. Следствие: профайлер должен показать, что индекс возвращает результаты быстро, но сортировка/скоринг занимают большую часть времени. Проверка: тот же EXPLAIN ANALYZE, посмотреть в нижнюю часть плана.

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

Пятнадцать минут на проверку трёх версий - и оказывается, что виновата третья. Индекс работает за 30 миллисекунд, остальные 770 - это перетаскивание десяти тысяч кандидатов в код приложения. Решение - добавить ограничение в SQL, чтобы база возвращала уже отсортированные первые двадцать. Не нужно менять движок. Не нужно переписывать индекс. Полчаса работы вместо трёх дней миграции на новый поисковый сервис.

Универсальность приёма видна на одной общей мысли: первая версия в обоих случаях - самая привычная для контекста. У продуктовой команды якорь - недавний релиз. У разработчика - то, что чаще всего винят в литературе о производительности. Оба якоря сильные. Оба часто промахиваются.

Цена десяти минут

Когда я первый раз показал ADI в команде, первая реакция была: «у нас нет десяти минут, у нас огонь горит».

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

Грубая калькуляция на средней команде:

  • Один промах «починили не то» - два-три инженера × один-два дня = четыре-шесть человеко-дней.
  • Таких промахов на команде из 10-15 человек - один-два в месяц.
  • Это от пяти до пятнадцати человеко-дней в месяц на починку не того.

В деньгах для команды с медианной зарплатой инженера 250 тысяч рублей в месяц - это от полумиллиона до полутора миллионов рублей в год, выкинутых в стену. И это считая только видимое - потраченное время. Невидимое - это упущенная работа, которую эти инженеры могли бы сделать вместо беготни не в ту сторону.

Десять минут ADI на каждом сложном инциденте - это процентная ставка за десять процентов того же бюджета. Самая выгодная сделка, которую я знаю в разработке.

Что отдать AI-агенту, что - себе

AI-агент (Claude Code, Cursor, любой кодовый ассистент) хорошо делает первый шаг ADI: выписать три гипотезы. Если ему дать симптом и контекст («упала конверсия в checkout, недавно был релиз вот этих файлов, метрики такие-то»), он за минуту даст три версии и для каждой предложит, как проверить. Часто - лучше, чем человек, потому что у агента нет якоря «то, что я недавно трогал».

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

Базовое правило: агент - генератор гипотез, человек - приоритизация и интерпретация. Если AI выписывает три версии за вас на 100% - отлично. Если он же выбирает, какую проверять первой, и доверяете ему - у вас не ADI, у вас угадайка с красивой обёрткой.

Что почитать дальше

Это второй пост в серии из восьми про дисциплину принятия архитектурных решений. В предыдущем - про то, почему через полгода никто не помнит, почему команда выбрала JWT, а не сессии: Кладбище решений.

Дальше я разберу:

  • Как оценивать надёжность доказательств, когда сравниваете альтернативы - почему среднее обманывает, а минимум честнее.
  • Как выглядит формат записи решения, чтобы через год её можно было пересмотреть без подвига памяти.
  • Как одно маленькое сообщение в чате превращается в продуктовый документ без расползания рамок.

К этому посту прилагается интерактивный разбор: 3D-граф с тремя гипотезами в пространстве F/G/R, где видно, как доказательства подкрепляют или опровергают каждую. Лежит в /guides - крутится мышью, ничего ставить не надо.