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

«Анна Каренина» Л.Толстой.

Проведя сотни аудитов проектов в IT и расследуя десятки инцидентов, перефразируя классика, можно сказать: «Все проекты счастливы по-своему, а несчастны одинаково». Успех проекта — это уникальное сочетание параметров и людей. А несчастья приносят одни и те же проблемы или совокупность проблем, небольших упущений, которые в итоге приводят к пике. Об одном таком случае сегодня расскажу вам я — эксперт Службы качества SimbirSoft Юлия. 

Обычно мало кто реагирует на небольшие отступления от стандартов ведения проектов, соблюдения процессов: «Ничего же страшного не произойдет». Это чревато большими проблемами, когда специалисты не понимают риски, не осознают, зачем то или иное правило, договоренность, и что может произойти в итоге, если этого не делать. Потому что по сути процессы и правила гибкие (в лучших традициях agile :)) Но мелочи и, казалось бы, незначительные вещи на самом деле могут оказать серьезное влияние на весь проект. И привести к катастрофе…

У нас в SimbirSoft есть процесс фиксации и анализа инцидентов. Сначала, конечно, нужно как можно скорее устранить последствия. Но основная цель — понять, почему инцидент произошел и что нужно сделать, чтобы он не повторился в будущем. Как в авиации проводят расследование авиакатастроф, так и у нас в IT — расследование инцидентов, которым занимается, в том числе, служба качества компании (иногда мы в шутку называем себя «следственный комитет»). Но вы не подумайте, допросов с пытками не устраиваем :) Мы в компании развиваем культуру качества, когда можно открыто говорить об ошибках, делать выводы и учиться на них. 

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

26.12.2019

Последняя в году рабочая неделя. Николай, backend-разработчик одного из проектов, давно планировал новогоднюю поездку в Петербург. Остался последний рывок — релиз новых фич. 

QA-специалист Гуля написала в чат, что регресс завершен, мелкие баги, которые были, уже поправлены, и пора выкладывать приложение в AppStore и Google Play. 

Мобильное приложение для online-тренировок очень нравилось команде: и тренировки, и подход его владельца. Наша компания разрабатывала приложение «под ключ»: от ТЗ, дизайна, архитектуры до иконки в телефоне тех, кто мечтает сделать фигуру мечты. По классике оно состояло из двух частей — серверной и мобильной клиентской (Android и iOS). В команде разработки были аналитик, дизайнер, backend, Android- и iOS-разработчики, QA-специалист и PM. Дрим-тим увлеченных работой и продуктом людей. Приложение уже было в сторах. Первый релиз состоялся 20 октября. И только за первый месяц работы уже было более 50 000 скачиваний, более 4100 оценок и отзывов. 

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

У нас все было готово. Ждали только окончания проверок от AppStore и Google Play.

30.12.2019

PM проекта Анна сообщила заказчику, что приложение успешно прошло проверки и можно проводить релиз. 

Клиент решил провести его 31 декабря. Новый год — новая жизнь! А мы были молоды и отчаянны :)

31.12.2019

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

19:54 (+3 GMT) Первый алерт о высокой нагрузке на базу данных (БД). Потом еще и еще. 

20:35 (+3 GMT) DevOps Максим написал в чат команде о том, что сервер «себя плохо чувствует», и он уже смотрит логи, пытаясь разобраться в произошедшем. 

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

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

«Вечер перестает быть томным» :) Новогодний вечер! У клиента 11:00 31 декабря. Разница с командой 8 часов. И срыв выхода нового курса тренировок.

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

Backend-разработчик Николай в это время гулял по Санкт-Петербургу — на 30 и 31 декабря он оформил отгул. Но читать рабочие чаты в любое время дня и ночи и даже в отпуске — это зависимость большинства айтишников. Связался с Максом (Devops), и по его описанию возникли подозрения сразу на несколько проблем. Николай написал PM и своему руководителю, указал, что необходимо донастроить кэширование на стороне СУБД. 

В команде приняли решение срочно подключить другого backend-разработчика Сергея (представляете его лицо? :)), поскольку Николай не может внести правки. Также решили увеличить производительность сервера. 

21:53 (+3 GMT) На экране приложения появилось сообщение «Ошибка приложения». Бэк полностью «умер». 

22:46 (+3 GMT) Кэширование уже на проде, «железные» мощности тоже уже добавили. Приложение «завелось». Но стало понятно, что это временная передышка и лишь возможность выиграть время на поиск и исправление проблемы. Кэширование и увеличение производительности сглаживали ситуацию, но не решали ее. 

00:00 (+3 GMT) С Новым годом! 

Да-да, каждый член команды праздновал у компа или телефона, чтобы в любой момент подключиться при необходимости! 

1.01.2020

Сергей нашел основную проблему приложения и уже вносил правки, попутно консультируясь с Николаем. 

Исправленный код на тесте. QA-специалист оперативно подключилась и проверила, нашла пару замечаний. Снова правки и снова тест. Потом мини-регресс (да-да, именно мини, пошире смоука, но времени на полноценный нет). 

02.01.2020

Все исправления на проде. Можно было выдохнуть, но все же нужно было еще понаблюдать за нагрузкой на СУБД. 

03.01.2020

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

04.01.2020

18:40  (+3 GMT) Приложение упало на 3 минуты, произошел сброс кэша пользователей, сделали новый вариант сброса и работы кэша.

05.01.2020

Мониторинг без аномалий. Полет нормальный. Выдохнули. Поставлена задача реализовать API 2.0.

9.01.2020

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

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

25.02.2020

Релиз обновленного приложения. Были доработаны и бэк, и мобильное приложение (Android, iOS). Единый запрос на всю структуру тренировок был разбит на несколько, в соответствии с уровнями вложенности. Прогресс по тренировкам тоже «ушел» в отдельные роуты.

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

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

Допущенные ошибки, и как их можно было избежать

1. Неправильное построение запросов

PM предположила, что заниматься спортом по тренировкам из приложения пользователи могут в местах, где не очень хорошая связь (привет тренажеркам в подвалах от детей 90-х :)). Backend-разработчик согласился на предложение PM сделать один большой запрос на загрузку всей информации о всех тренировках, не предполагая, насколько после возрастут объемы загружаемой информации. Это упростило работу мобильным разработчикам, но нарушило принцип REST — вместо множества GET-запросов для получения разных уровней вложенности тренировок, был один запрос, загружающий сразу все уровни.

Кроме структуры курсов, ответ от этого запроса содержал и прогресс пользователя по тренировкам, то есть ещё дополнительные запросы к базе. К тому же само получение структуры тренировок добавило проблему N+1 из-за использования ленивых ассоциаций. 

Этого не было видно на тестовых данных. Однако на проде проблема расцвела во всей красе.

Как надо было сделать: 

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

2. Тестовые данные не соответствовали реальным

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

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

Как надо было сделать: 

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

  • Определить ограничения по типу, объему, структуре данных. Согласовать с командой и клиентом. 

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

  • Задавать наводящие вопросы: «А какие еще в перспективе могут быть изменения в приложении?», «Планы по развитию?», «Как это отразится на данных?».

3. Не было реализовано закрытие соединения к Redis

В явном виде не было закрытия соединения, само закрывалось, но долго висело. Реализация функциональностей базировалась на документации Symphony и по сути реализовывалась «в лоб», без углубления в особенности работы фреймворка и доктрины.

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

Как надо было сделать: 

Этот пункт возник из-за первых двух и экстренных правок. Правильно выстроить работу с кэшем и подсветить первые две проблемы позволило бы своевременное проведение нагрузочных тестов.  

Что в итоге мы изменили у себя в процессах

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

  2. При тестировании фокусируемся на релевантных тестовых данных. Актуализируем примеры при разработке новых фич. 

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

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

  5. Ввели мораторий на релизы в предпраздничные дни и по пятницам. 

Что могу сказать в заключении. Когда я села писать историю об этом кейсе, у меня даже мысль возникла: «А как мы это упустили? У нас же есть контроль на уровне архитектуры и сбора требований». А потом поняла — да это же как раз после этого инцидента ввели :)))

Разработка без ошибок — это в идеальном мире. Самое главное — делать выводы, учиться на ошибках и непрерывно улучшать процессы создания программных продуктов. Всем добра!   

Спасибо за внимание!

Больше полезных материалов для разработчиков и управленцев в IT мы также публикуем в наших соцсетях – ВК и Telegram.

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


  1. nronnie
    08.06.2023 09:32
    +3

    К новому году команда запланировала релиз новых фич и публикацию нового курса тренировок

    Это, похоже, была первая ошибка. Знающие люди такое не то что на Новый Год, а даже просто на пятницу никогда не планируют :)


    1. SSul Автор
      08.06.2023 09:32

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


  1. DMGarikk
    08.06.2023 09:32
    +1

    Что в итоге мы изменили у себя в процессах

    забавно что я работал в компании которая ходит по кругу вокруг этих косяков
    1) начинали они также как описанный кейс
    2) ввели все исправления предложенные вами
    3) начали отменять эти "исправления" потому что они задерживают релизы, жрут время и деньги
    4) идем к п.1 и далее по кругу


    а релиз в пятницу это святое, также как бекапы (точнее их отсутствие/хранение рядом с бекапируемым сервисом/отсутствие тестов на развертывание/отсутствие плана восстановления)


    ничто не меняется в этом мире..


    Ввели архитектурный надзор в каждом производственном направлении разработки.

    вы это отмените как только у архитектора и топовых сеньоров кончатся свободные слоты в календаре...sad but true

    кстати добавте пункт в план — не выкатывать новый и очень востребованный (или существенно измененный) сервис сразу на 100% аудитории даже после всех тестов…
    у меня прям свежее впечатление
    (строгий b2b ентерпрайз внутри сложной бюрократической структуры) в пятницу потестили на 5 клиентах… в понедельник выкатили на 5000 человек… разом… люди в поддержке через полчаса начали писать что готовы из окна выпрыгивать уже от звонков из-за того что все работает не совсем так как клиенты думали и есть кейсы которые тестирование не покрыло… но фарш обратно уже не провернуть… я прям вспомнил молодость, затыкая дырки по горячему в проде


    1. Aquahawk
      08.06.2023 09:32

      Плавный раскат через A/B тест под контролем метрик действительно спасает. Далеко не все проекты готовы к этому инфраструктурно и не все готовы за это платить.


      1. DMGarikk
        08.06.2023 09:32
        +1

        Далеко не все проекты готовы к этому инфраструктурно и не все готовы за это платить.

        1) далеко не все проекты архитектурно готовы к полноценному ревью кода
        2) далеко не все проекты могут себе позволить нагрузочное тестирование


        а про не готовы платить… ну вот тут заплатили за это таким авралом
        это все понятно почему так происходит, просто надо стремится к идеалу


        1. Aquahawk
          08.06.2023 09:32

          1) далеко не все проекты архитектурно готовы к полноценному ревью кода

          2) далеко не все проекты могут себе позволить нагрузочное тестирование

          Согласен и с первым и со вторым, но надо понимать что не всем это и нужно.

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

          А тут надо очень аккуратно определять что мы делаем и зачем. Делая Опель не нужно пытаться получить качество Мерседеса, а делая Мерседес E класса не нужно пытаться сделать Майбах. Потому что у всего есть своя цена, и не всем нужно платить за Майбах. Нужно управлять качеством и понимать что магии за 3 рубля не бывает. Есть решения, есть затраты и прибыль, и куча ситуаций когда понятно как, но по деньгам продукт не вывезет.


          1. DMGarikk
            08.06.2023 09:32

            конечно
            у сожалению мое текущее nda не позволяет мне привести очень жизненный пример на тему "не всем это нужно"
            скажем так произошла авария, и из за того что сервис был неважный, качество никому не нужно, тестирование тоже и т.п. были потеряны практически все данные внутри него, а в процессе восстановления данных часть данных не может быть восстановлена правильно потому что архитектуру делали какието наркоманы… ну прально этож не майбах ;)


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

            ну как грится, не можешь… ть не мучай попу.


    1. SSul Автор
      08.06.2023 09:32

      С проблемой нехватки слотов у архитекторов/senior мы тоже сталкивались. Но процедура проходит по новым процессам железно, не отменяем.

      А замечание про выкатку на ограниченную аудиторию дельное. Спасибо!)


  1. cthtuf
    08.06.2023 09:32

    Позвольте свои мнение на ваше "пике":
    0. Никогда не релизиться, если на дату релиза, + несколько следующих дней, у ключевых сотрудников есть планы. Вижу как вы осознали это через боль.
    1. Ответы на запросы могут включать дополнительные сущности, просто вы, на тот момент, это реализовали некоррекно. Посмотрите в сторону GraphQL, очень распространенное решение, в котором из коробки есть возможность в одном запросе вернуть все что нужно. Если они смогли - и вы у себя сможете.
    2. Тут я вижу проблему в отсутствии нагрузочного тестирования. Субъективно - если у вас клиентов больше пару сотен, это уже мастхев. Ну вы уже внедрили, да.
    3. Извечная проблема инвалидации кэша, вроде как волшебной таблетки тут нет, зато есть куча чужих опытов, на которых можно научиться делать как нужно. Прогрев кеша решает только часть проблем, при росте сущностей в проекте, он не поможет.

    Вообще было бы интересно посмотреть на цифры - потеряли ли вы клиентов, количество обращений в ТП лавинообразно выросло и т.п., на чем отразились эти неправильные решения, иначе не совсем понятно в чем тут "пике"? Плохие релизы бывают у всех =D


  1. Aquahawk
    08.06.2023 09:32

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


  1. usego
    08.06.2023 09:32

    Раскатывать релиз на 5% трафика, не?


  1. 18741878
    08.06.2023 09:32

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


    1. SSul Автор
      08.06.2023 09:32

      Спасибо за внимательность! Исправили


  1. funca
    08.06.2023 09:32
    +1

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

    Это похоже на finger pointing, что не конструктивно.

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

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


  1. Thomas_Hanniball
    08.06.2023 09:32
    +1

    Что нужно было сделать на самом деле:


    • Поговорить с клиентом и выкатывать релиз после праздников, когда вся команда будет на рабочем месте.
    • Перед релизом на широкую аудиторию проводить нагрузочное тестирование, чтобы понимать, сколько пользователей потянет текущая инфраструктура. При внезапном взлёте скачиваний, например, где-то была реклама этого приложения, иметь план по оперативному увеличению ресурсов.
    • Выкатить приложение в прод с новым функционалом, но скрыть его временно от пользователя. Подтянуть до нужной версии инфраструктуру. Проверить, что функционал успешно работает.
    • Постепенно включать новый функционал новым группам пользователей и смотреть, как всё работает. Смотреть системы мониторинга и анализировать возможные узкие места. Через несколько этапов включить функционал всем пользователям.
    • Нанять на работу DBA, т.е. администратора баз данных, который будет находить все кривые и большие запросы к базе данных и будет бить по рукам отдельных разработчиков. Базами данных и их оптимизацией должны заниматься DBA, а не backend разработчик.


  1. Thomas_Hanniball
    08.06.2023 09:32

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

    Архитектура проекта закладывает ещё до начала работ на нём. Делает это системный архитектор или группа из самых опытных разработчиков + кто-то от бизнеса. Никакой PM в середине проекта при всё своём желании не сможет как-то существенно эту архитектура изменить.


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