Привет, Хабр! На связи — маркетплейс Flowwow. В субботу мы праздновали День программиста. В честь праздника решили не хвастаться успешными успехами, а поговорить о том, что делает нас сильнее, опытнее и немного седее. Мы кинули клич по рынку и собрали истории от коллег — разработчиков из других компаний (про свои тоже не забыли), чтобы, глядя в лицо факапам, сказать уверенно: «Да, было».

Истории о том, как одна строчка кода выключала сеть магазинов, как из-за бага на кастинг пришли одни барабанщики и почему бэкапов много не бывает. Это тот самый случай, когда чужой опыт может избавить от страха своих ошибок. Устраивайтесь поудобнее, мы начинаем сеанс групповой IT-терапии.

Факапы бывают разные: эпичные, тихие, а бывают — просто забавные. Начнем нашу подборку с истории о том, что быть жаворонком в офисе, где все совы — опасно. Можно не только успеть выпить три кофе, но и ненароком стать тем, кто будет чинить баг, созданный не своими руками. Так произошло с Владимиром, тимлидом в Циан.

Я всегда приходил в офис первым и в 9 уже был за компом, а остальные подтягивались ближе к 10-11. Дело было несколько лет назад. В то утро мне нужно было проверить, как работает новая фича, по которой у меня стояла задача. Открываю сайт, нажимаю на нашу знаменитую синюю кнопку «Найти» на главной, и ничего не происходит. Потыкал другие кнопки — тоже не работают. Такого я раньше не видел. Кого-то из коллег в личке попросил проверить, чтобы валидировать баг — та же история. Тогда написал в чат с дежурными.

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

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

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

Никогда не забуду тот кружок из ранних пташек в центре офиса, которые пытались поднять сайт. И когда кто-то приходил в офис в тот день, его сразу засасывало в нашу команду спасения.

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

Павел Жуков, IT Business Partner PR-агентства «Позиция»:

Команда агентства в короткие сроки разработала сайт, на котором должны были регистрироваться музыканты на кастинг. Мы протестировали все системы: сбор анкет, верификацию, регистрацию. При этом, когда выполняли проверки, то всегда заводили себя на роль барабанщиков, т.к. она была первой в списке (всего ролей было 4). 

Наступил первый день промо сайта, мы получили свыше 140 заявок за первые 6 часов каста, но только от барабанщиков. Когда кастинг-менеджеры обзванивали зарегистрированных, по диалогам в духе «Ой, я гитарист» мы быстро поняли, что у нас баг в ролях. В итоге из 140 барабанщиков лишь 5 или 6 действительно играли на ударной установке. 

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

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

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

Смех смехом, а иногда ошибки приводят к куда более серьезным последствиям. Особенно когда речь заходит о данных. Об этом не понаслышке знают ребята из hh.ru.

Алексей Поповичев, администратор баз данных hh.ru:

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

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

Мораль: админы делятся на два вида: те, которые ещё не делают бэкапы, и те, что уже делают.

История коллеги из hh — суровое напоминание, что бэкапы это базовый минимум. И, как показывает практика, лишними они не бывают, особенно когда вы доверяете свою инфраструктуру кому-то еще. Вот кейс от анонимного автора с прошедшего митапа «Пыхап», который это доказывает:

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

Обратились к хостеру. Ответ: его взломали, все виртуалки потерли, а бэкапы зашифровали. Восстановить нельзя.

Нас спасло то, что мы отдельно делали бэкапы клиентских серверов. Но бэкап сайта мы не делали, понадеялись на хостера. В итоге сайт проекта со всеми клиентами и данными канул в Лету. Восстанавливали проект где-то неделю. Хостер тоже оклемался, наладил безопасность. Мы благополучно всё подняли, а сайт перенесли на другого хостера (и, как оказалось, правильно).

Проходит неделя-две, и первого хостера опять ломают и сносят всё под ноль… В этот раз мы восстановили свои серверы и клиентов дня за 2-3.

Мораль сей басни: мало бэкапов не бывает. На хостера надейся, а сам не плошай.

Бывают дни, когда ты уже почувствовал вкус победы, быстро разобравшись с багом, который мешал работать коллегам. Но не все так просто. Как перевести всех клиентов на бесплатный тариф или историю одного обеда рассказал Алексей Поздеев, ведущий инженер-программист, Толк:

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

Ребята просили срочно помочь: „Ошибки валятся, а мы не успеваем починить!“. И я не придумал ничего лучше, чем просто удалить данные из БД. Ведь когда я этот код писал, данных в БД не было, новый код это нормально прожевал, значит если просто удалю, то тоже всё будет ОК, какие проблемы… Надо было в объекте в Mongo убрать 1 поле, но я быстрее вспомнил, как в него вписать null. Вроде одно и то же должно быть, что значения нет в БД — это в коде null, что значение null в БД — это тоже в коде null. Так что поехали.

Запустил я это дело на стейдже, проверил, что запрос корректно отработал, стейдж вроде открывается, можно и в проде запускать. Запустил. „Мавр сделал своё дело, Мавр может удалиться на обед“ — подумал я.

Придя с обеда примерно через час, я обнаружил сообщение в рабочей соц.сети „У клиента тариф сбросился на бесплатный!!“ и куча +1 в комментариях под этим постом. „Какое подозрительное совпадение“ — подумал я, и не зря…

Оказалось, отсутствие поля и null — это не одно и то же. Если поля нет, то оно просто заполняется значением из конструктора по умолчанию, и всё работает… А вот если поле явно null, то и в рантайме будет явно прописан null. И добро пожаловать NullReferenceException, только вас мы и ждали…

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

Мораль сей басни такова — на обед больше не ходим не зная Монгу не суйся к проду.

Есть правило: «Не тестируй в проде». Но иногда уверенность в своем коде (и в документации вендора) настолько сильна, а тестовое окружение ведет себя так странно, что это правило хочется немного… нарушить. Такое случилось с Данилой Соловьевым, руководителем направления PHP в AGIMA.

История такая: У нас в компании собственная модель управления проектами — Agimaban. Мне поступает задача: автоматизировать старт работы над проектами. Фактически это значило следующее: как только лид переходит в колонку Success, у нас должны автоматически создаться проект и три канбан-доски в трекер — для управленческих задач, для сбора требований и для производства. Я разобрался с API, собрал сервис и начал тестить. Запросы на чтение проходили, на изменение — не сразу, но в итоге я получил заветный HTTP 200. Однако, привязав воркфлоу к тестовому проекту, я увидел в настройках белый экран. Я списал это на криво развернутую тестовую среду. В конце концов, я же всё делал по документации, а API отдавал 200. Что я сделал? Правильно, решил кинуть тот же запрос в прод.

У меня были все необходимые права. Я создал тестовый проект на проде, стрельнул запросом для привязки воркфлоу... и всё сломалось. Легли все проекты компании, работающие по этому воркфлоу, а это абсолютное большинство. Работа фактически встала.

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

Хорошо, с бэкапами разобрались. Но иногда «апокалипсис» можно устроить буквально одной строчкой кода. Об этом рассказывает Алексей Новиков, тимлид команды RTE VendorApp Магнит OMNI.

История на тему „хотели как лучше“. Нам нужно было синхронизировать статусы магазинов между двумя системами. Я передал коллеге список ID активных магазинов из нашей системы, где статус хранился в колонке is_active. Он залил его к себе, но не учел, что в его системе та же логика хранилась в колонке с противоположным названием — disabled.

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

Есть и истории про использование нейросетей — куда сейчас без них. И таким факапом поделился Сергей Наумов, ML Engineer в Профи.ру:

Нужно было откатить правки в нескольких файлах рабочей директории репозитория, команду не вспомнил — спросил у GPT5 и бездумно скопировал команду из ответа: git clean с «какими-то там» флагами. Оказалось, что один из параметров отвечал за удаление игнорируемых файлов (в .gitignore): вместе с ненужными изменениями удалились все локальные конфиги с персональными доступами и целиком питоновское окружение. В итоге приключение по восстановлению на весь оставшийся день. Кажется, стоит хотя бы чуть внимательнее читать ответы LLMки.

Бессонные ночи и бесконечная битва за стабильность — знакомо? Надеемся, что нет. А вот Олег Протасов, инженер-программист из команды внешних интеграций Контура не понаслышке знает, насколько тяжелым может стать, на первый взгляд обычный, переезд:

Моя фейл-история началась год назад, когда ко мне, вышедшему из пеленок стажерства, джуну, подошел наставник и предложил перевести один из ключевых сервисов аутентификации на рельсы Linux'а. Я сразу же согласился, отправился читать код, и осознал, что он написан на Net Framework, который на это не способен. Пару недель заняло у меня переписывание на Net 6, которое в итоге продлили мое мучение на 2 недели дебага, ибо умерло почти все. Но как бы то ни было, раскатка на тестовом стенде показала, что работа далека от завершения — нестабильная работа сервиса стала моим бременем еще на неделю, пока мы не переехали на Net 7. После переезда все заработало очень стабильно, и мы со спокойной душой ушли пить чай.

Через 2 недели настало время отчетности, которое убило почти все реплики этого дивного сервиса вместе с попытками переезда на Linux на целый год.

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

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

  • «По моей ошибке в if у клиентов не списывались тренировки с абонементов. Клиенты были рады, а стейкхолдеры не очень».

  • «Есть тарифы списания денег по привязанным картам за аренду. Разработчик пропустил одно условие if, и мы не заметили, как списалось более 500 тысяч рублей с карт пользователей за 30 минут. Я пропустил это при ревью. Чисто человеческий фактор. А главная проблема, что на такой важный функционал не был написан тест. Пишите тесты на важный код, они действительно важны!».

  • «Хотел создать папку на сервере с правами 777, но случайно указал путь к / (естественно, под sudo). Успел заметить неладное, прежде чем завершил сессию ssh. Новые подключения по ssh больше было нельзя открыть, работало только мое текущее, поскольку Ubuntu среагировала на избыточные права в некоторых критических папках. Посовещались с девопсом, решили подключить этот диск как внешний на другом сервере (хорошо, что всё крутилось в AWS) и чинить уже там. Вечер обещал быть томным, а было время обеда. Так что пошли сначала покушать, а потом приступили к починке. Я параллельно зашел на какой-то другой сервер и начал сравнивать поштучно файлы и папки, и где надо — выставлял такие же».

Но случаются кейсы, от которых, как говорит автор, «потеют зубы». Олег Мифле, автор телеграм-канала «Позовите Олега»:

На одном из первых крупных e-commerce проекте я поставил себе задачу — напишу код без единого бага. Перед запуском тимлид попросил очистить тестовые данные. Я быстро написал команды для очистки в первом попавшемся файле в редакторе в IDE,  просто в таблице удалить нельзя из-за сложной структуры, нужно чтобы выполнялись все связанные события. Скопировал код, выполнил в песочнице и после — отвлекся и оставил как есть. На код-ревью его тоже никто не отсмотрел — залили как было. После запуска клиент начал жаловаться на пропажу товаров и заказов. Мы долго не могли понять, в чем дело, грешили на клиента. А потом я нашел тот самый код... в асинхронном обработчике формы обратного звонка. Каждый раз, когда кто-то просил перезвонить, мой скрипт подчищал прод. Ощущение было, будто зубы вспотели. Думал, уволят, но обошлось парой седых волос.

Пока мы собирали эти истории, у нас в команде не раз проскакивало: «О, жиза», «Ха, у нас было почти так же». Так что не думайте, что мы просто коллекционируем чужие ошибки. У нас у самих есть пара историй, которые мы нет-нет да вспоминаем на дейли. Например, кейс от Максима, бэкенд-разработчика Flowwow, где факап мог бы стать поводом для изучения иностранного языка.

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

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

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

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

Комментарии (0)


  1. Dhwtj
    15.09.2025 12:48

    Надо было сделать опрос среди уволенных за факапы.

    А так... Никто не уволен, даже премии не лишили, вероятно

    И окажется, что список реальных факапов возглавляет "повысил голос на начальника"

    ...

    Начальник смены, виновный в Чернобыльской аварии отсидел 5 лет. А за Фукусиму вообще никого не наказали.


  1. ahdenchik
    15.09.2025 12:48

    Алексей Поповичев, администратор баз данных hh.ru:

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

    Зато конкурс к ним, небось, 8 собеседований и 3 литкода