Что делать, если баг не воспроизводится локально?

Ответ

Когда баг не воспроизводится в локальном окружении, я действую по следующему алгоритму:

  1. Воссоздание окружения: Разворачиваю максимально приближенную к production среду, используя Docker-контейнеры с идентичными версиями ПО (PHP, веб-сервер, СУБД) и настройками.
  2. Сбор данных: Запрашиваю у тестировщика или из системы мониторинга полный контекст:
    • Точные шаги воспроизведения.
    • Логи ошибок (PHP, nginx/Apache).
    • Данные окружения (phpinfo(), $_SERVER).
    • Состояние данных на момент ошибки (дампы БД, кэша).
  3. Детективное расследование: Ищу различия между средами. Частые причины:
    • Разные версии PHP или расширений: Проверяю php -v и php -m.
    • Конфигурация: Настройки памяти (memory_limit), времени выполнения (max_execution_time).
    • Проблемы с данными: Уникальное состояние базы данных или кэша, специфичное для продакшена.
    • Поведение пользователя: Особые куки, сессия, права доступа (ACL).

Пример инструментария для отладки:

// Временное логирование в контроллере или middleware для сбора контекста
if ($_SERVER['REMOTE_ADDR'] == 'IP_ТЕСТИРОВЩИКА') {
    error_log(json_encode([
        'timestamp' => date('c'),
        'request_uri' => $_SERVER['REQUEST_URI'],
        'request_params' => $_REQUEST,
        'session_id' => session_id(),
        'user_agent' => $_SERVER['HTTP_USER_AGENT'],
        'backtrace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
    ], JSON_PRETTY_PRINT));
}

Если проблема остаётся неуловимой, подключаю инструменты распределённой трассировки (например, OpenTelemetry) или мониторинга ошибок (Sentry, Bugsnag), чтобы поймать её при следующем возникновении в проде.

Ответ 18+ 🔞

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

Первым делом — окружение. Надо поднять такую же среду, как на проде, до последней запятой в конфиге. Беру Docker, выкатываю контейнеры с теми же версиями PHP, того же Nginx, базу ту же самую. Не «примерно такую же», а один в один, потому что чёрт, как известно, кроется в деталях, а в нашем случае — в разнице версий расширений.

Дальше — сбор улик. Пишу тестировщику или лезу в логи мониторинга. Мне нужно всё: точные шаги, что он тыкал, что вводил, под каким пользователем. Логи ошибок с прода — это святое. Ещё вытягиваю phpinfo() с того сервака и дамп окружения $_SERVER. А самое главное — состояние данных на момент пиздеца. Потому что часто бывает, что баг вылазит только при какой-то ебанутой комбинации записей в базе или сбитом кэше. Без этого — просто пальцем в небо.

И вот начинается самое интересное — детектив. Сравниваю две среды, ищу, где наебнулось. Частые подозреваемые:

  • Версии. На проде PHP 8.2.1, а у меня 8.2.0 — и какая-нибудь херня уже по-другому работает. Проверяю php -v и php -m досконально.
  • Настройки. На продакшене memory_limit зажали, или max_execution_time стоит три секунды, а скрипт на пять тянет.
  • Данные. В базе на проде лежит какая-то манда с ушами, которую десять лет назад засунули, и только сейчас код на неё наткнулся.
  • Пользователь. У него особенные куки, просроченная сессия или права доступа, которых больше ни у кого нет.

Если баг хитрожопый и не ловится, втыкаю в код временные ловушки. Где-нибудь в контроллере или middleware, чтобы логировать всё подряд, но только для IP-адреса того, кто баг воспроизводит.

// Временное логирование в контроллере или middleware для сбора контекста
if ($_SERVER['REMOTE_ADDR'] == 'IP_ТЕСТИРОВЩИКА') {
    error_log(json_encode([
        'timestamp' => date('c'),
        'request_uri' => $_SERVER['REQUEST_URI'],
        'request_params' => $_REQUEST,
        'session_id' => session_id(),
        'user_agent' => $_SERVER['HTTP_USER_AGENT'],
        'backtrace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
    ], JSON_PRETTY_PRINT));
}

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

Ну а если и это не помогло, и баг ускользает как хитрая жопа, тогда пускаю в ход тяжёлую артиллерию. Настраиваю распределённую трассировку, типа OpenTelemetry, или подключаю Sentry. Пусть ловят эту невидимку при следующем появлении на проде. Рано или поздно она вылезет, и тогда я её возьму за ебальник. Главное — методичность и не опускать руки, даже когда овердохуища уже бесит.