Воспроизведение — половина работы
Пришла жалоба: «отчёт показывает не те суммы». Первое побуждение — открыть код отчёта и начать читать. Второе, с агентом, — описать ему жалобу и попросить починить. Оба ведут к одному результату: правка, про которую неизвестно, помогла ли она.
Починка начинается не с кода, а с воспроизведения. Пока поломка не повторяется у вас по требованию, чинить нечего — и не потому, что нельзя догадаться, а потому, что нечем проверить исправление.
Жалоба почти никогда не содержит того, что нужно. Она содержит впечатление. Превратить впечатление в шаги помогают четыре вопроса.
| Вопрос | Зачем он |
|---|---|
| у кого | права, настройки, роль — половина поломок только у части людей |
| когда | дата, время, что было перед этим, повторяется ли |
| на каких данных | конкретный документ, номер, период — не «в отчёте», а «в этом» |
| что именно вместо чего | ожидалось 1200, получено 1150 — а не «неправильно» |
Четвёртый вопрос — самый важный и самый редко заданный. «Неправильные суммы» — это может быть и ноль вместо числа, и число другого порядка, и число, отличающееся на копейку. Это три разные поломки с тремя разными причинами, и до ответа на этот вопрос вы не знаете, какую из них чините.
Дальше воспроизведение сокращают. Жалоба звучит как «зашёл в раздел, выбрал период, нажал сформировать, открыл вкладку, нажал печать — не те суммы». Половина шагов к делу не относится. Убирайте по одному и проверяйте, повторяется ли: обычно от семи шагов остаётся два, и эти два и есть поломка.
Сокращённое воспроизведение стоит того же, что и полное, а работать с ним в разы легче: меньше мест, куда смотреть, и проще объяснить другому — в том числе агенту.
Про агента в этой главе: воспроизведение он сделать не может — у него нет ни данных человека, ни его прав, ни его браузера. Зато он хорош в соседнем: получив описание, назвать гипотезы, от чего это могло бы зависеть. Список гипотез превращается в список проверок, и вот их-то уже можно пройти.
И последнее правило главы, которое экономит больше всего времени: записывайте воспроизведение сразу, как нашли. Через час вы будете помнить его неточно, а «работало, а теперь не работает, и я не помню, что делал» — самый дорогой вид починки из всех.
Сужение: делим пополам
Поломка воспроизводится. Теперь надо найти место, где всё идёт не так, — и здесь есть один приём, который работает всегда и почти не требует знания проекта.
Делите путь пополам. Данные проходят через цепочку: пришли, посчитались, сохранились, показались. Вопрос на каждом шаге один: верно ли здесь? Ответ «да» отсекает всё до этого места, ответ «нет» — всё после.
Это не метафора, а буквальный способ действия. В отчёте не те суммы — посмотрите, что лежит в базе. Верно — значит причина в расчёте отчёта. Неверно — значит причина раньше, в том, что записывалось. Один взгляд отсекает половину проекта.
Почему это работает лучше чтения: чтение кода даёт понимание, а не локализацию. Прочитав функцию, вы узнаете, что она делает; но не узнаете, вызывалась ли она, с какими данными и что вернула. Именно эти три вещи и нужны.
Инструменты деления простые и все доступны: напечатать значение в подозрительном месте, посмотреть, что в базе, прогнать кусок отдельно с известными данными, заглянуть в журнал. Выбор между ними — вопрос удобства, а не принципа.
Если у проекта есть история правок, добавляется мощный приём: деление по времени. Работало месяц назад, не работает сейчас; берём середину и проверяем; за десять проверок находим правку, после которой сломалось. Дальше остаётся прочитать одну правку вместо всего проекта.
Про агента здесь важное: сужение можно вести вместе с ним, и это заметно быстрее. Он хорош в том, чтобы по описанию «вот здесь верно, вот здесь неверно» назвать все места, где данные меняются между этими двумя точками. Это механический обход кода, и он делает его быстро и полно.
Чего ему поручать не стоит — сам вывод о причине по описанию жалобы. Он назовёт правдоподобную причину, и она будет из числа частых; но частая причина и причина в вашем случае — разные вещи. Проверить его догадку — тот же самый процесс сужения, который вы пытались пропустить.
Правильный порядок работы с ним такой: вы делите, он ищет. Вы говорите «до расчёта верно, после неверно» — он перечисляет всё, что происходит между. Вы выбираете следующую точку деления — он показывает код. Решение о том, где делить, остаётся за вами, потому что оно опирается на наблюдение, а наблюдать может только тот, у кого поломка воспроизводится.
Признак и причина
Место найдено. Теперь самое ответственное место починки: отличить признак от причины.
Разберём на живом примере. В отчёте в колонке «сумма в рублях» стоит ноль. Место нашли: функция пересчёта по курсу возвращает ноль, потому что курс на эту дату не найден.
Правка признака: «если сумма ноль — не показывать строку». Жалоба уходит. Данные по-прежнему неверны, просто их больше не видно, и через месяц кто-то сверит итоги и не сойдётся.
Правка причины: «курс на дату не найден — берём последний известный и помечаем в журнале». Данные верны, и, что важнее, видно, что курс не пришёл, — то есть настоящая беда (обмен курсов сломался) становится заметной.
Почему чинят признак — понятно: это быстрее и жалоба правда уходит. Иногда это даже верное решение: когда причина в чужой системе, когда чинить дорого, а поломка редкая, когда надо срочно погасить. Но тогда это должно быть решением, а не недосмотром: записано, что причина известна, заглушка временная, и когда к ней вернуться.
Есть более коварный случай — причина лежит на уровень глубже, чем кажется. Курс не найден, потому что обмен не отработал; обмен не отработал, потому что упал по таймауту; таймаут потому, что чужой сервис отвечает медленно раз в месяц. Где здесь остановиться?
И третье, про агента, потому что здесь его вклад неоднозначен. Агент склонен чинить признак, и это не его недостаток, а следствие постановки: ему говорят «в отчёте ноль, почини», и самый прямой способ убрать ноль — убрать ноль.
Лечится это формулировкой поручения. Не «почини, чтобы не было нуля», а «вот место, где появляется ноль; объясни, откуда он там берётся». Объяснение проверяется, и из него уже видно, где настоящая причина.
Хорошая проверка перед тем, как принять правку: попросить назвать, что именно было не так — одним предложением, без кода. Если предложение получается вида «значение оказывалось нулевым, и мы его теперь скрываем» — перед вами правка признака, и это видно сразу.
Починка целиком
Причина найдена. Остаётся собрать починку целиком — и порядок здесь важнее, чем кажется.
| Шаг | Что делается |
|---|---|
| 1. тест, который падает | проверка, воспроизводящая поломку — до правки |
| 2. правка причины | минимальная, только то, что относится к делу |
| 3. тест проходит | и старые тоже — проверка, что не сломали соседнее |
| 4. проверка руками | тот же путь, которым шёл человек из жалобы |
| 5. запись | что было не так, одним предложением |
Первый шаг — тест до правки, а не после. Это выглядит бюрократией и таковой не является. Тест, написанный до, доказывает две вещи: он действительно ловит эту поломку (сейчас он красный) и после правки поломки нет (стал зелёным). Тест, написанный после, доказывает только вторую — а первую принимает на веру, и зря: тест, который проходит всегда, встречается чаще, чем хотелось бы.
Второй шаг — минимальная правка. Соблазн причесать соседний код по дороге велик, особенно когда агент уже предложил. Не стоит: смешав починку с улучшением, вы теряете возможность откатить одно без другого, а если после выкладки что-то пойдёт не так, придётся выбирать между «откатить починку» и «разбираться под нагрузкой».
Замеченное по дороге записывается отдельной задачей. Это не отговорка: записанное действительно делается, а сделанное вместе с починкой — увеличивает риск обоих.
Четвёртый шаг — руками, тем же путём. Тест проверяет то, что вы придумали проверить. Человек шёл другим путём: через свой раздел, со своими правами, на своих данных. Пройти его путь занимает две минуты и ловит то, чего нет ни в одном тесте, — например, что вы починили в одном месте, а тот же расчёт есть и во втором.
Про агента в починке общая раскладка такая.
| Шаг | Кто делает |
|---|---|
| воспроизведение | вы: у него нет данных и прав |
| гипотезы, от чего зависит | он предлагает, вы проверяете |
| сужение | вы делите, он ищет между точками |
| отличить признак от причины | вы: спрашиваете объяснение и проверяете его |
| тест, который падает | он пишет, вы убеждаетесь, что он красный |
| правка | он пишет, вы читаете |
| проверка руками | вы: путём человека из жалобы |
И последнее, что стоит помнить про починку с агентом. Он почти всегда предложит правку с первого раза, ещё до того, как вы найдёте причину, — потому что по описанию жалобы правдоподобная правка придумывается легко. Эта правка часто даже работает, и именно поэтому она опасна: работающая правка без найденной причины означает, что причина осталась и вернётся.
Способ не попасться простой: не принимать правку, пока не можете назвать причину одним предложением. Не «код стал правильным», а «данные приходили без курса, и пересчёт отдавал ноль». Если предложение не складывается — причина не найдена, сколько бы кода ни было написано.