У меня всё работает: определитель невоспроизводимых багов

воспроизведение сессии

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

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

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

1. Состояние

Самая частая причина. Вы воспроизводите баг на только что загруженной странице. А пользователь сидит в этой вкладке уже два часа.

Одностраничное приложение, открытое долго, накапливает историю: устаревшие записи в клиентском кеше, вебсокет, который переподключился с другой сессией, стор, который изменил код со страницы, где пользователь был час назад, токен доступа, обновившийся посреди работы. У вашей свежезагруженной страницы этой истории нет.

Признак: после обновления страницы всё работает. Если автор отчёта пишет «помогает обновить страницу», дело почти наверняка в этом, и воспроизводить нужно не его шаги, а его шаги после двадцати минут работы с чем-то другим.

Как поймать: по списку того, что пользователь действительно делал за сессию, по порядку, а не того, что он счёл важным. Такой список даёт журнал действий, а поле «шаги воспроизведения» не даст никогда: нужный шаг — как раз тот, о котором пользователь не подумал упомянуть.

2. Тайминги

На вашей машине всё быстрое. Локальный API, прогретый кеш, быстрый диск. На телефоне в поезде запрос, который у вас занимает 40 мс, идёт 4 секунды — и состояние гонки, которое у вас не проявляется никогда, у пользователя срабатывает стабильно.

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

Как поймать: по таймингам сетевых запросов. Посмотрите, какие запросы шли одновременно и сколько длился каждый. Классическая картина: два запроса стартовали вместе, а код рассчитывал, что первый и закончится первым.

Как воспроизвести: замедлить. Троттлинг сети и процессора в DevTools превращает очень многие баги из «не воспроизводится» в «воспроизводится каждый раз». Это самые ценные пять минут во всей статье.

3. Окружение

Браузер, версия, ОС и — то, что недооценивают сильнее всего, — размер вьюпорта.

Вы тестируете на 1440px в десктопном Chrome. Пользователь — на 390px в Safari на iOS или на 1280px с масштабом браузера 125%, а это не то же самое, что 1024px, и разница ломает вёрстку. Safari до сих пор заметно расходится с остальными в разборе дат, backdrop-фильтрах, поведении прокрутки и в том, как обращается с хранилищем в приватном режиме.

Признак: у автора отчёта баг стабилен, у вас его нет никогда. На его машине воспроизводится в 100% случаев.

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

4. Пользователь и права доступа

Вы вошли как админ, под своим аккаунтом, в своей организации. Пользователь — участник с правами только на чтение в команде из сорока человек, на тарифе, где выключен фича-флаг.

Баги, которые зависят от роли, систематически пропускают: разработчики почти никогда не ходят по своему продукту под пользователем с минимальными правами.

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

Как поймать: прикладывайте к отчёту роль пользователя, тариф и состояние флагов. В любом приличном SDK можно добавить свой контекст — в BugDump это identify() для того, кто пользователь, и setContext() для любого состояния, — и роль с тарифом стоит прикладывать в первую очередь.

5. Данные

В аккаунте пользователя есть запись с эмодзи в названии, или null там, где вы ждали строку, или 4000 строк там, где в ваших фикстурах 12, или валюта без дробной части, или дата из 1970 года, потому что импорт пошёл не так.

Признак: воспроизводится на одной конкретной записи и больше нигде. Автор отчёта часто так и пишет: «так только в этом проекте», — и этим отдаёт вам готовый диагноз.

Как поймать: по ID записи и ответу API, на котором всё упало. Тело ответа с проблемным значением сразу закрывает вопрос. Во многом поэтому тела ответов так полезны — и поэтому же с ними нужно обращаться осторожно: в том же ответе, где лежит битая дата, лежит и всё остальное о пользователе.

6. Расширения и внедрённые скрипты

Блокировщики рекламы режут ваш эндпоинт аналитики, а код не готов к тому, что этот запрос упадёт. Менеджеры паролей встраивают свои элементы в DOM вашей формы и ломают обработку событий. Расширения-переводчики переписывают текстовые узлы и рушат согласование дерева в React. Корпоративные прокси переписывают ответы.

Признак: никто не может воспроизвести баг, включая других пользователей, а в консоли — запрос, упавший без статуса, или ошибка DOM, указывающая на элемент, который вы никогда не рендерили.

Как поймать: выдают упавшие запросы без ответа. А ещё воспроизведение сессии, в котором виден DOM, которого ваше приложение не создаёт.

7. Сама сеть

Страницы входа в публичный Wi-Fi, корпоративный перехват TLS, сбои DNS, узел CDN, который отдаёт одному региону устаревший бандл, деплой в процессе, когда HTML уже новый, а чанка, на который он ссылается, уже нет.

Признак: отчёты собираются в кучку по месту или по времени. Несколько отчётов за короткий промежуток или все из одной страны.

Как поймать: по времени отчётов и URL ресурса, который не загрузился. ChunkLoadError в консоли почти по определению указывает на эту категорию и значит, что ваш деплой удаляет старые чанки раньше, чем с ними закончили работать старые вкладки.

8. Время

Часовые пояса, переход на летнее время, границы месяцев, форматы дат в разных локалях и компьютер, у которого часы сбиты на одиннадцать минут, — этого хватает, чтобы токен казался просроченным сразу по прибытии.

Признак: воспроизводится только в определённые часы, или только у пользователей из определённых регионов, или только в конце месяца.

Как поймать: по часовому поясу и местному времени автора отчёта рядом с серверным временем. Записать это ничего не стоит, а категорию оно определяет с первого взгляда.

Метод

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

  • Помогает обновить страницу? → состояние

  • Баг плавающий? → тайминги

  • У пользователя воспроизводится в 100% случаев? → окружение

  • У него та же роль или тариф, что у других авторов отчётов? → пользователь и права

  • Дело в одной конкретной записи? → данные

  • Запрос упал без статуса? → расширения или сеть

  • Отчёты собраны в кучку по времени или по месту? → сеть

  • Только в определённые часы? → время

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

Записывайте разницу, а не описание

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

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

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

Читайте также