The Catalog That Broke Over 48 Hours: An Incident Report, What We Found, What We Fixed

Author: WebGoodPeople

5 min read
Share

Один из клиентов в начале июня прислал сообщение в пятницу вечером: «С утра каталог отдаёт ошибки, конверсия упала в 3 раза, что делать». Сайт был не на нашем стеке — клиент обратился «отдельно, со стороны». У нас было 48 часов до понедельника, когда эта ситуация грозила стоить им кратно больше, чем аудит.

Это не чисто технический разбор — это хроника того, как проводится аварийный аудит, что типично находится и как это чинится. Все детали обобщены, клиент не называется.

Час 0: сигнал


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

Первые три вопроса, которые я задаю:

  1. Что именно «не грузится» — страница отдаёт 500? Пустую страницу? Замедление?
  2. У всех пользователей или у части? Если у части — как определить «этих»?
  3. Когда это началось, и что было релизнуто в последние 48 часов?

Ответы: 500-ки на части страниц категорий, у всех пользователей этих категорий. Началось утром. Накануне был релиз — «обновили плагин для фильтров».

Час 1: доступ и первичный осмотр


Получаем доступ к серверу через SSH и логам через веб-интерфейс хостинга. Первые 15 минут — просто tail -f access-лога и error-лога.

tail -f /var/log/nginx/error.log


В error-логе видим повторяющуюся ошибку:

PHP Fatal error: Uncaught TypeError: Argument #1 ($values) must be of type array, null given, called in /bitrix/modules/iblock/classes/... at line 1247


Фатальная ошибка в plugin-коде фильтров. Код предполагает получить массив значений, получает null, падает.

Час 2: воспроизводим и локализуем


Открываем несколько страниц категорий в приватном режиме браузера. Часть — работают. Часть — 500-я.

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

То есть релиз плагина накануне изменил поведение: раньше при отсутствии значений фильтр скрывался, теперь пытается строиться и падает на null.

Час 3: временный фикс


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

Добавляем условие:

if (!empty($filter_values) && is_array($filter_values)) {
    // вызов плагина
}


Деплой. Проверяем — 500-е исчезли на проблемных категориях.

Время от начала до фикса — 3 часа. Включая установление контакта, доступы, диагностику.

Час 4–24: что нашли во время «расширенного осмотра»


Это та часть, которая редко попадает в постмортемы, но часто самая ценная. Пока мы были на сервере, имело смысл посмотреть на общее состояние. Клиент согласился.

Что нашли за следующие 20 часов (никакой срочности, просто внимательное чтение):

1. API-лог отсутствует


В системе есть access.log от Nginx и error.log от PHP, но нет структурированного лога API-вызовов. Все «инциденты последнего квартала» разбирались через grep по access-логу — медленно и неточно.

Рекомендация: внедрить схему лога из 5 полей (см. наши статьи от апреля-мая). 1–2 дня работы, закрывает целый класс проблем.

2. Бэкапы есть, но не тестировались 14 месяцев


Бэкапы БД делаются каждую ночь. Rolling window — 14 дней. Но последняя тестовая восстановка из бэкапа делалась в апреле 2025 года. Это значит: формально бэкап есть, фактически мы не знаем, работает ли он.

Рекомендация: ежемесячная проверка restore в staging. 2 часа работы раз в месяц.

3. Права БД у веб-юзера: полный доступ


Веб-приложение ходит в MySQL с аккаунтом, у которого есть DROPCREATEALTER. Это стандарт для Битрикс, но избыточно — для runtime ему нужны SELECTINSERTUPDATEDELETE и в некоторых случаях CREATE TEMPORARY TABLE.

Рекомендация: отдельный юзер для админ-операций, отдельный — для рантайма. Снижение blast radius при компрометации.

4. Нет алертов на ошибки


PHP error.log пишется, но никто на него не смотрит в реальном времени. Инцидент начался утром, об этом узнали ближе к вечеру только потому, что клиент сам увидел провал конверсии.

Рекомендация: Loki / Fluent Bit → Grafana Alerting. Алерт на >10 PHP fatal errors в минуту — пейджер on-call. Установка — полдня.

5. Единая точка знаний (bus factor)


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

Рекомендация: инвентаризация системы, документация ключевых процессов, второй инженер с прав admin. 2–3 недели работы.

6. Смешение staging и production


У клиента есть staging-среда, но некоторые тесты проходят напрямую на продакшене. «Это маленькая правка, зачем staging». Плагин фильтров тоже обновляли «маленько» — сразу в prod.

Рекомендация: жёсткое правило — ни одного деплоя минуя staging. Плюс автоматизация деплоя staging → production через CI (снижает frictions).

48 часов: итоговый отчёт клиенту


Документ на 9 страниц:

  1. Что произошло (30 минут чтения)
  2. Что мы сделали немедленно
  3. 6 находок, каждая с приоритетом и оценкой трудозатрат
  4. План на первые 30 дней (то, что чинится быстро)
  5. План на 90 дней (структурные изменения)
  6. Оценка «цены не-действия» (сколько стоит НЕ исправлять каждую проблему)

Самое важное для клиента в отчёте — пункт 6. Без него пункты 1–5 воспринимаются как «список желаний». С пунктом 6 — как «набор решений с конкретной окупаемостью».

Что из этого универсально


Любой Битрикс-проект, который живёт 3+ года, почти наверняка имеет:

  • Отсутствие или слабость структурированных API-логов
  • Нетестируемые бэкапы
  • Избыточные права БД у рантайма
  • Отсутствие алертов на ошибки приложения
  • Высокий bus factor на legacy-компонентах
  • Смешение staging/production в «маленьких правках»

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

Если описанное выше звучит знакомо, у вас те же 48 часов в резерве.

48-часовой аудит без инцидента


Тот же по глубине отчёт можно получить до инцидента, а не после. Мы заходим в проект, смотрим 2 дня, возвращаем 9-страничный документ с 5–10 находками, приоритетом и ценой не-действия.

Стоимость — 0 ₽, это лид-магнит. Если после отчёта вы решите работать с нами по одной из находок — обсудим. Если нет — забираете отчёт и внедряете сами.


webgoodpeople.com/ru/products/headless-next-elasticsearch

Share

Read next

Articles on adjacent topics — from real projects.

Newsletter

Headless migrations & AI pilots, unpacked

Every 2 weeks — real cases, numbers and architecture decisions. No marketing noise.