Записки разработчика: чему меня научила первая критическая ошибка в продакшене

Когда-то я тоже думал: «Ну что может случиться? У нас же тесты, стейдж, code review». А потом одной ночью продакшен лег так, что нас разбудили все возможные алерты и половина компании в мессенджерах. С той ночи я намного аккуратнее отношусь к коду, логированию и деплою.

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

---

Что такое «критическая ошибка в продакшене» в нормальном человеческом языке

Сначала определимся с терминами, но без излишней бюрократии.

Продакшен (production) — это боевое окружение, где живет приложение, которым пользуются реальные пользователи, платят деньги, теряют время и ожидают стабильной работы. Ошибка здесь — это не просто «красный тест» в CI, а реальное нарушение бизнес-процесса.

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

Формально можно описать так:
> Критическая ошибка в продакшене — это инцидент с высоким приоритетом (P0/P1), который требует немедленной реакции, обхода или отката релиза, влияет на доход, имидж компании или юридические обязательства.

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

Это не был «баг где-то в углу интерфейса». Это был самый важный сценарий системы.

---

Как мы вообще довели до этого: архитектурная «диаграмма на салфетке»

Чтобы понять, как все развалилось, опишу систему простым текстовым «диаграммным» языком. Представьте архитектуру так:

- Пользователь → Web / Mobile клиент
- Web / Mobile клиент → Backend API (gateway)
- Backend API →
- сервис платежей
- сервис заказов
- сервис уведомлений
- внешний платежный провайдер

Если нарисовать текстовую диаграмму, получится что-то вроде:

- `Client`
- отправляет запрос `POST /pay`
- → `API Gateway`
- валидирует запрос
- вызывает `PaymentService`
- создает сущность `payment`
- вызывает внешний `PaymentProvider`
- на успех:
- вызывает `OrderService`
- вызывает `NotificationService`

В оригинальном коде был один небольшой, но очень коварный участок:

```pseudo
if (payment.status == "SUCCESS") {
createOrder()
sendNotification()
}
log.info("Payment processed", paymentId)
```

На тестовом окружении все работало. В продакшене — нет. В чем подвох? В том, что статус `SUCCESS` в реальности приходил не сразу, а через асинхронный callback от платежного провайдера. В момент запроса статус был «PENDING», и заказ просто… не создавался. Пользователь платил, деньги зависали, заказ не появлялся.

Таким образом, одна неверная предпосылка в бизнес-логике привела к массовой критической ошибке.

---

Почему тесты не спасли: разбор по слоям

Записки разработчика: чему меня научила первая критическая ошибка в продакшене - иллюстрация

Формально у нас было «все правильно»:

- unit-тесты покрывали логику `PaymentService`;
- интеграционные тесты ходили в тестовый стенд платежного провайдера;
- был стейдж, на котором команда щелкала сценарии руками.

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

Эксперты по QA потом очень спокойно сказали: «Вы тестировали не то, что работает в реальности». И это честный вердикт: проблема была не в отсутствии тестов, а в неправильной модели поведения внешней системы.

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

---

Диаграмма инцидента: как это выглядело ночью

Записки разработчика: чему меня научила первая критическая ошибка в продакшене - иллюстрация

Опишу хронологию в виде текстового «sequence diagram»:

1. `User` → `Client`: нажимает «Оплатить».
2. `Client` → `API`: `POST /pay`.
3. `API` → `PaymentService`: создать платеж.
4. `PaymentService` → `PaymentProvider`: запрос на оплату.
5. `PaymentProvider` → `Bank`: блокировка средств.
6. `PaymentProvider` → `PaymentService CALLBACK`: статус «PENDING».
7. Наш код:
- ожидает `SUCCESS`,
- так как статус `PENDING`, заказ не создается.
8. `User` получает сообщение «Оплата прошла» со стороны провайдера (страница банка).
9. Возвращаясь в наше приложение, пользователь не видит заказ.
10. Пользователь:
- перезаказ,
- гневной письмо в саппорт,
- иногда чарджбэк.

В логах это все выглядело красиво:
`INFO Payment processed`
хотя по сути он был не «processed», а «initiated». Неверное логирование дополнило картину: по логам все «успешно», по бизнесу — катастрофа.

---

Чему это меня научило: техничные выводы человеческим языком

Из одной критической ошибки в продакшене я вынес несколько устойчивых правил.

1. Никогда не путать «обработано» и «принято в обработку».
Статусы должны быть точными: `INITIATED`, `PENDING`, `SUCCESS`, `FAILED`, а не «ну вроде прокатило».

2. Асинхронность должна быть явной в коде и документации.
Если внешний сервис работает через callback, это должно быть видно в диаграммах, описаниях и в тестах.

3. Логи — это часть интерфейса системы, а не мусорка для строк.
Лог `Payment processed` в момент, когда оплата в состоянии `PENDING`, — это дезинформация, а не лог.

4. Стейдж, который не имитирует продакшен, — это дорогая иллюзия безопасности.

---

Сравнение: «идеальный мир» vs реальный деплой

В обучающих материалах по dev и devops все выглядит примерно так:

- корректная модель данных;
- детерминированные интеграционные тесты;
- контролируемый деплой;
- rollback по кнопке.

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

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

Отсюда и интерес к формальным практикам: обучение devops для разработчиков онлайн стало не просто модной темой, а почти необходимостью. Там хотя бы проговариваются базовые принципы: контроль конфигураций, feature-флаги, canary-релизы, blue-green деплой.

---

Что можно было сделать заранее: рекомендации от более опытных коллег

После инцидента мы провели нормальный postmortem с участием senior-разработчиков, devops-инженеров и QA-лидов. Их рекомендации (переведу с «корпоративного» на практичный язык):

- Моделировать реальные сценарии внешних систем.
Не верить в «счастливый путь». Если провайдер дает 5 статусов и два типа колбэков — нужно протестировать все.

- Внедрить фиче-флаги и staged rollout.
Новую логику критических сценариев сначала включаем на небольшой процент трафика, а не на всех.

- Отдельно прогонять негативные сценарии:
- задержки ответа;
- частичная недоступность;
- рассинхрон статусов.

Именно такие рекомендации легли потом в нашу внутреннюю программу обучения. По сути, это были небольшие тренинги по предотвращению критических ошибок в продакшене, основанные на реальных фейлах, а не на абстрактных примерах.

---

Где здесь место формальному обучению и курсам

Часть ошибок решается не «талантом разработчика», а системным опытом, который проще получить, когда:

- есть наставник;
- есть структурированное обучение;
- есть симуляции инцидентов.

Здесь реально помогают:

- курсы по отладке и мониторингу веб-приложений — учат смотреть не только в код, но и в метрики, логи, трассы запросов, корректно настраивать алерты;
- курсы повышения квалификации разработчиков backend — закрывают пробелы в архитектуре, работе с транзакциями, внешними интеграциями;
- обучение devops для разработчиков онлайн — дает понимание про CI/CD, окружения, стратегии деплоя и откатов.

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

---

Конкретные технические привычки, которые спасают продакшен

Чтобы не быть голословным, вот практичные привычки, которые я вынес из того инцидента:

- Явные контракты со внешними сервисами
- читать не только публичную документацию провайдера, но и уточнять детали через поддержку;
- фиксировать протоколы взаимодействия у себя в репозитории: схемы статусов, таймауты, ретраи.

- Отдельные интеграционные тесты под асинхронные сценарии
- тесты, которые имитируют задержанные колбэки;
- тесты, в которых SUCCESS не наступает вовсе (отмена, ошибка, таймаут).

- Стандарты логирования
- для каждого бизнес-события — один «ключевой» лог с понятным форматом;
- четкое разделение: `initiated`, `pending`, `completed`, `failed` в логах и метриках.

Эти мелочи потом очень помогают, когда приходится «по горячим следам» разбирать, почему продакшен внезапно стал вести себя странно.

---

Мини-диаграмма «как должно быть»

Перепроектированная версия взаимодействия с провайдером в текстовой форме выглядела так:

1. `Client` → `API`: `POST /pay`.
2. `API`:
- создает запись `payment` со статусом `INITIATED`.
- отвечает клиенту: «платеж инициирован, перенаправляю к провайдеру».
3. `Client` → `PaymentProvider`: переадресация.
4. `PaymentProvider`:
- взаимодействует с банком;
- по завершении:
- вызывает `CallbackEndpoint` нашего `PaymentService`:
- обновляет статус `PENDING` → `SUCCESS` или `FAILED`.
- на `SUCCESS` → вызывает `OrderService` → `createOrder`.
5. `OrderService`:
- создает заказ в атомарной операции;
- отправляет событие в `NotificationService`.

Теперь заказ создается только после того, как оплата гарантированно прошла, а не «где-то там в процессе».

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

---

Почему разговоры про культуру важнее, чем поиск «козла отпущения»

После инцидента было сильное искушение найти одного виноватого:
«кто это закодил?», «кто это заапрувил?», «почему QA пропустили?».

Но самая полезная часть разборов инцидентов — это переход от вопроса «кто виноват?» к вопросу «почему система позволила это сделать?».

В нашем случае система позволила:
- не смоделировать реальное поведение провайдера;
- отправить в прод функциональность без постепенного раскатывания;
- иметь логи, в которых SUCCESS и PENDING выглядели одинаково.

Именно такие вопросы рождают изменения в процессах, а не просто новые «строгие правила» в чате.

---

Что бы я посоветовал себе тогда и вам сейчас

Если суммировать весь опыт в несколько тезисов:

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

И, пожалуй, самое важное:
первая критическая ошибка в продакшене — не приговор, а сильный ускоритель профессионального роста. Если из нее честно вытащить уроки, а не просто замять инцидент, вы начинаете смотреть на систему глазами не только разработчика, но и человека, который отвечает за ее поведение в реальном мире.

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

Прокрутить вверх