В предыдущих постах мы уже обсуждали банковскую карту МегаФона как финансовый продукт и рассказывали о ее возможностях для конечного потребителя. Но, безусловно, за таким проектом стоит огромная работа, которую проделала команда профессионалов. В этот раз мы подробнее расскажем именно о технических особенностях реализации этого проекта и об устройстве программного обеспечения.
Учитывая масштаб проекта, описать все тонкости в рамках одного поста не получится, поэтому начнем мы с рассказа о бэкенде системы ДБО (дистанционного банковского обслуживания). Задача бэкенда — обеспечить рабочую связку всех специализированных систем в рамках единой логики, а также функционирование множества фоновых процессов. А самое главное — удобный и функциональный личный кабинет для пользователей.
Техническим партнером проекта карты Мегафон выбрал компанию TYME. Все время работы специалисты двух компаний работали в тесной связке с поставщиками банковского ПО, биллинга, платежными системами и другими поставщиками, собирая разрозненные функциональные кусочки мозаики будущего проекта в единое целое.
«У нас были сжатые сроки и большой объем работы. Не было лишних дней на долгие обсуждения и права на ошибку при выборе технологии и подходов. Здорово, что нам вместе с компанией TYME удалось реализовать сложный и инновационный проект.»
Ян Кухальский, Генеральный директор МегаЛабс
В 2013 стартовал проект «Терминалы», и с этого времени мы постоянно совершенствовали решение, интегрируя его со своими сервисами и новыми провайдерами услуг и добавляя новые функции для клиентов.
У TYME довольно большой опыт работы в финтех-отрасли, они успешно преодолели все трудности и мы вместе с технологическими партнёрами успешно запустили карту МегаФона. Далее на страницах блога МегаФона даём слово команде TYME, пусть ребята сами расскажут о деталях своей части проекта.
Детали большого проекта
После реализации каждого масштабного проекта мы оглядываемся назад, чтобы оценить проделанную работу. В весьма сжатые сроки нам удалось запустить крайне сложную систему, которая находится в самом сердце финансового продукта федерального масштаба.
Немного цифр для примера:
- 18 внешних систем для интеграции
- 600 бизнес-процессов были реализованы в рамка проекта
- 770 страниц технической документации
- 2500 сценариев для автотестов
- 15000 человеко-часов разработки
Оценить истинный объем задач можно только тогда, когда разработка по проекту заканчивается. Объем задач в ходе работы постоянно увеличивался. Если смотреть на это сейчас, то становится даже немного страшно — как мы в принципе могли подписаться на такой объем в такие сроки.
Ноябрь 2015-го. Проект — на стадии концепции. В переводе на человеческий — у нас есть лишь четко оговоренная дата запуска и приблизительное ТЗ от бизнеса.
Отсекаем мрамор
Возможность изучить заказчика и не доставать его постоянными вопросами у нас появилась благодаря правильно выстроенным отношениям и нескольким годам совместной работы.
Вот некоторые принципы, следование которым нам очень помогло:
- Работать, исходя из текущего состояния требований. Нужно искать конкретные задачи, за которые уже можно взяться прямо сейчас, вместо того, чтобы заниматься скрупулезной детализацией.
- Применять декомпозицию. Нужно распределять работы и вести проектирование и разработку независимо.
- Использовать готовые источники. Одна из наших задач – интеграция с уже готовыми системами, которые на момент начала работ уже были частично запущены. Это прозвучит банально, но то, что позволило нам упростить интеграцию — это вдумчивое изучение документации по функционирующим системам и совместная фиксация принципов работы на ранних этапах проекта.
- Выполнять лидирующую роль в управлении. Залог успех проекта — это инициатива разработчика в процессе формализации требований. То есть работать надо всегда на опережение, искать возможность продвинуться вперед по другим частям плана, если какие-то другие провисают.
Само собой, при таком подходе неизбежно возникает риск для разработчика — ведь ресурсы на аналитику приходится тратить весьма существенные, и по собственной инициативе. Подходит этот путь в тех проектах, в которых вы полностью уверены в своих взаимоотношениях с заказчиком, и если вы хорошо ориентируетесь в самой отрасли.
Agile и долгосрочное планирование
О достоинствах Agile-методологии сказано уже много. Не будем повторяться и сконцентрируемся на тех моментах, которые обычно заказчику тяжелее всего принять.
- Нет жестких сроков окончания большого проекта
- Затраты на большой проект могут вырасти при его проработке
- Непредсказуемые границы проекта, то есть скоуп работ всегда будет относительным, а попытка его финализировать лишь приведет к сильно отложенному старту.
Так как мы работали по SCRUM, то проблема была стандартной – заказчику нужен был «водопадный» план проекта на ближайший год. А детализация проекта была готова только на несколько ближайших спринтов, за которые команда взяла на себя обязательства выполнить описанные задачи.
Часто можно увидеть следующую рекомендацию: если вы хотите получить долгосрочный план, работая по Agile, заведите в Project каждый спринт или итерацию как задачу. В результате на выходе будет примерно следующая картина:
Рекомендация настолько общая, что у нас не прижилась. Этот вариант плана не показывает вехи продукта, значимые для заказчика, и не может быть полноценно использован для обсуждения долгосрочного графика проекта.
Серебряную пулю в 1986-м придумал Барри Боэм, американский военный. Его идея — спиральная модель разработки. Многие айтишники уже знают, что это, но на практике все равно очень часто наблюдаются крайности — или Agile без долговременных планов, или водопад со сроками и бюджетами, которые постоянно изменяются.
Артемий Лебедев в «Ководстве» неплохо высказался на этот счет.
Благодаря спиральной модели мы решили две задачи сразу:
- поддерживали актуальный долгосрочный план, понятный заказчику
- выдавали в разработку задачи итеративно, не превращая при этом SCRUM в водопад.
Проектная же работа строилась так:
- Итеративная детализация требований. Документ, который описывал требования по каждому из модулей системы, имел свою внутреннюю структуру, когда составляющие ее компоненты могут находиться в разной степени готовности. На каждой из итераций детализация требований увеличивалась, давая возможность взять в разработку очередной объем задач.
- Эволюция рабочих продуктов. На каждой итерации разработки мы пересматривали весь стек рабочих продуктов на полную глубину в соответствии с новыми реалиями требований и фидбеком на прототипы. Это заставляло нас иногда переделывать архитектуру продукта. К примеру, на старте проекта планировалось, что все процессы будут обеспечиваться в рамках 40 микросервисов, и в ходе детализации архитектуры их количество было сокращено до 20.Так эволюционировали представления о том, каким образом необходимо декомпозировать бизнес-логику по сервисам. В итоге это дало очень большое преимущество — мы постоянно имели возможность получать фидбек о текущей реализации архитектуры, и практически не накапливали технический долг.
- Регулярное перестроение плана. Долгосрочный план проекта также был «живым», исходя из актуальных требований, информации о графике разработки внешних систем и других вводных. Мы не относились к плану как к константе, закрепленной на старте проекта, для нас это был «маршрут», который должен привести из той точки, где мы находимся, к итоговой цели. Соответственно, если ситуация вокруг меняется (а происходит это регулярно), уточняется и план.
- Ориентированность на ранний результат. Мы постоянно работали над тем, чтобы максимально рано взять задачу в разработку, и как можно быстрее выдать результат для приемки и тестирования. При этом мы старались разбивать зависимости, мешавшие нам это сделать — если не готов внешний сервис, мы не ждем его реализации, а пишем «заглушку»; если существует неопределенность по какой-то части задачи, стараемся декомпозировать ее еще сильнее и выполнить работы по тем составляющим, где определенность есть. Часто команды разработки не идут на подобные затраты, опасаясь сделать лишнюю работу, и зачастую эти опасения обоснованы. В этот раз мы сделали ставку на скорость.
- Разработка по SCRUM. Мы прикладывали максимальные усилия для того, чтобы изолировать разработчиков от неопределенностей в проекте, не нарушая привычный ритм SCRUM. Управление проектом было построено таким образом, что в каждый момент времени был «буфер» понятных команде задач как минимум на один спринт вперед. Это позволило не терять темп на протяжении всего периода активной разработки.
В итоге мы пришли к такому маппингу задач между планом в MS Project для заказчика и Jira для команды разработки
За само управление потоком работ отвечала Jira (но это мог бы быть любой удобный для Agile продукт), а план в MS Project был нужен для контроля глобального статуса работ и его визуализации для заказчика.
Макроэффект от микросервисов
Проект превратился в кучу подпроектов, которые были весьма связаны между собой.
Так как мы еще вначале выбрали подход, обеспечивающий такое дробление — мы могли это себе позволить.
Тренд микросервисов существует в мире разработки уже довольно давно. Огромное количество обсуждений на эту тему происходят на профильных конференциях. Кто-то принципиально отрицает пользу от такого построения систем, есть и те кто занял прямо противоположную позицию и переводит все свои сложные системы на микросервисную архитектуру.
Микросервисная архитектура — это подход к разработке, при котором вместо создания одного большого приложения, может быть, разделенного на слои (GUI, Бизнес-логика, БД), создается множество небольших, изолированных друг от друга компонент, которые называются микросервисами. Не вдаваясь в теоретические детали, которые можно достаточно легко найти на Хабре, хотелось бы остановиться на том, как оказался данный подход полезен в нашем проекте.
Вот главные плюсы такого подхода (на наш взгляд):
- Каждый сервис решает конкретный набор задач, у него определен API, по которому можно обращаться к сервису. Мы отлично изолируем ответственность внутри одного сервиса.
- Один микросервис, при желании, можно легко заменить новой версией или быстро провести безопасный рефакторинг.
- Горизонтальное масштабирование микросервиса при наличии сложностей с быстродействием. Это killer-feature для систем, которые должны работать в режиме 24*7. Масштабирование всегда идет вместе с мониторингом быстродействия каждого сервиса, мы собираем эту статистику и принимаем решение о запуске дополнительных инстансов.
- Особенности корпоративных сетей таковы, что по ИБ мы обязаны работать в закрытом контуре, но при этом часть нашей платформы имеет доступ в интернет, другие сервисы изолированы и работают с десятком внешних систем в рамках отдельных подсетей. Мы выделили сегменты, которые работают в публичном интернете, внутренние сервисы и интеграционные сервисы, которые находятся в специальной зоне с максимально закрытым доступом. В случае монолита, пришлось бы совмещать на одном сервере несколько сетей, что не всегда нравится сотрудникам обеспечивающим ИБ.
Само собой, не удалось избежать и некоторых сложностей:
- Самое сложное решение — о границах микросервиса. Нужно ответить безошибочно на вопрос — какой из существующих микросервисов должен выполнять эту задачу? На старте нам приходилось дублировать некоторые решения в нескольких микросервисах, чтобы сохранить их изолированность. Да, для разработчика это немного непривычная ситуация, так как переиспользование кода — основная задача.
- Кардинально иной подход к обновлению нашего приложения. Без автоматизации этого процесса для администратора усложняется многократно, так как ему пришлось бы в окно обслуживания выполнить в несколько раз большее количество операций, чтобы доставить обновление в промышленную среду.
Разделение системы на набор микросервисов + небольшая команда + разработка по SCRUM: вот рецепт, который помог нам снизить уровень зависимостей и максимально эффективно использовать все наши возможности и компетенции, вести разработку одновременно по нескольким направлениям одновременно, минимизируя влияние каждого сервиса на остальные части системы.
Быть в центре событий
Наша система не только обеспечивает бизнес-логику представления данных банка для клиента, это еще и интеграционная шина, объединяющая front-end и все участвующие внешние системы.
Когда отказывает наш бэкенд - это автоматически означает, что пользователь видит сбой. Когда отказывает любая другая система — у нас есть возможность смягчить проблему и сохранить работоспособность большей части функций.
Это значит, что наша система должна обладать эксплуатационными характеристиками, превышающими эти показатели любой смежной с нами платформы:
- Лучший uptime
- Самый большой запас по производительности
- Лучшая устойчивость по отношению к отказам и сбоям
Мы изначально понимали, что наша система станет главным узлом, с которого будет начинаться диагностика инцидентов, и мы должны обладать исчерпывающей информацией для любых технических расследований.
Эти вводные данные предопределили дополнительные условия для разработки:
- Быстрая доставка обновлений
- Максимальное качество работы каждого сервиса
- Исчерпывающее сквозное лигирование
Обновить за 90 секунд
Когда работаешь с платформой, построенной на микросервисах, важно понимать:
- Обновление нужно максимально автоматизировать. Пора забыть про ручное обновление и максимально устранить человеческий фактор.
- Огромный плюс микросервисов — децентрализованность. Мы сразу запланировали, что наши системы не должны иметь единой точки сбоя, чтобы обеспечить максимальный аптайм.
При этом мы не могли позволить себе вводить какие-то технологические окна на обновления — любые, даже небольшие простои системы в нашем случае крайне нежелательны.
Объединив усилия службы эксплуатации и разработчиков, мы получили решение, которое обладает всеми нужными свойствами:
- Быстрое обновление, которое не требует «ручного» вмешательства разработчиков или администраторов. Мы настроили авто-deploy, который обеспечил выкатывание «одной кнопкой» с учетом всей нашей специфики. Подготовка сборки происходит на ресурсах поставщика, а фактическое развертывание – в защищенном контуре заказчика.
- Во время обновления сервис не останавливается. Так как каждый сервис развернут и работает минимум на двух параллельных серверах, во время обновления мы отключаем от траффика группу обновляемых в текущий момент серверов и распределяем нагрузку по другим экземплярам микросервисов. После обновления — проверяем smoke-тестом работу новой версии сервиса и, если все прошло успешно, обновляем следующую группу сервисов, переводя трафик на уже обновленные сервисы. Так, постепенно поднимая версии, мы получаем работающую систему, без полной остановки платформы.
- Непрерывный контроль деградации функционала. Благодаря покрытию автотестами всех процедур, нам удалось обеспечить возможность очень быстро диагностировать отклонения перед деплоем на бой и осознанно принять решение о проведении обновления.
- Быстрая доставка функционала на бой. Благодаря всем ранее изложенным моментам, мы можем очень быстро выкатывать в промышленную эксплуатацию исправления и новый функционал без ущерба для непрерывности работы.
Есть ряд моментов, которые можно улучшить в процедуре обновления — например, обновление БД каждого из сервисов и обеспечение обратной совместимости для работы старой и новой версий сервиса одновременно.
Если это будет интересно, мы с удовольствием выпустим отдельную статью о тех подходах, которые мы применяем у себя.
Философия качества
Сервис, который не должен останавливаться, и при этом динамично развивается, всегда находится между двух огней. В случае простоя финансовые и репутационные потери будут колоссальными. А если вспомнить про чрезвычайно сжатые сроки и меняющиеся требования в процессе разработки, то степень сложности продолжала повышаться. Поэтому перед отделом качества встала амбициозная задача выстроить тестирование продукта таким образом, чтобы оно было:
- Быстрым. У нас сжатые сроки, короткие спринты и мы не хотим ждать по нескольку дней, пока тестеры проведут регрессионное тестирование по чек-листам. В идеале – нужно получать ответ, какой регресс понесла система после каждого изменения кода.
- Полным. Быстрое осуществление тестов – не самоцель. Настоящая цель – быть уверенными, что если тесты прошли успешно, то наша система готова к обновлению. А значит тесты должны быть не только быстрыми, но и полностью охватывать функционал продукта.
- Гибким. Быстрые и полные тесты это прекрасно. Но если у заказчика есть новые требования, и изменения требуют полного переписывания тестов – это никуда не годится, потому что мы опять теряем скорость. Соответственно, тесты должны быть гибкими к изменениям.
- Дешевым. Ресурс тестировщика при большой скорости разработки всегда будет выходить на первый план, если нам нужно получить высокое качество. Поэтому всё, что описано выше, должно выполняться существующим количеством специалистов по тестированию. Мы здесь лишь подтвердили общее соотношение, когда на 3 разработчиков приходится 1 тестировщик.
В качестве основы автотестов был выбран Python вкупе с фреймворком py.test. Python — это быстрый (в плане разработки) и мощный язык, а py.test с его замечательной подсистемой фикстур и параметризации – гибкий инструмент, позволяющий широко переиспользовать код тестов.
В качестве агрегатора результатов: билд-сервер TeamCity с установленными плагинами для интерпретации итогов проверки от py.test.
Сами тесты пишутся максимально изолированно, выполнение каждого теста никак не зависит от результата выполнения остальных тестов. Если тесту нужно подключаться к БД системы и получать данные, значит, фикстура теста должна эти данные там обеспечить до выполнения теста. Если на тест может повлиять значение, лежащее в кэше, значит другая фикстура должна обнулить кэш до выполнения этого теста. Да, это привело к тому, что много времени потребовалось для разработки систематизированной сетки фикстур, но очень быстро эти инвестиции стали окупаться скоростью добавления новых тестов и, самое главное, стабильностью их результатов. Полный контроль над тестируемой системой означает минимум ложных срабатываний тестов.
Интеграция с сервером сборки TeamCity дала возможность просто нажать одну кнопку, чтобы тесты проверили все процессы платформы. При этом никаких приготовлений делать не нужно, а значит, это может сделать любой член команды. Отчет о тестировании в подробном и наглядном виде отображается на веб-интерфейсе build-сервера.
Не пожалели мы и о полном отказе от автоматизации тестов API через специализированные решения. Да, такие инструменты дают набор функциональности прямо из коробки. Но, во-первых, это недешево, во-вторых, нам всё равно нужно больше возможностей.
К примеру, в определенных тест-кейсах нашего API было нужно получать СМС-код подтверждения на операцию, пробрасывать его в тесты и наблюдать за поведением системы. Вот здесь и выходит на первый план мощь coded тестов, пусть их разрабатывать и дороже, чем, к примеру, собрать test-step’ы в SoapUI.
В итоге, сейчас процесс построен таким образом, что Postman или тот же SoapUI используется тестировщиками только на этапе первичной проверки. В конечном итоге процесс всё равно должен быть покрыт автотестами на Python и внедрен в общий репозиторий. Это закон.
Ни одно изменение функционала системы не обходится у нас без тестирования вообще и без автотестов в частности. Story просто не считается выполненной, пока она не покрыта автотестами. Такой подход требует высокой самодисциплины от команды и производительности от тестировщиков, но результат того стоит: если на билд-машине тесты зеленые, мы уверены в качестве своей системы.
Сейчас количество функциональных тестов перевалило за 1300 и продолжает расти. Выполнение занимает 25 минут.
Правильный выбор инструментов и полное покрытие автотестами с ранних этапов разработки позволило нам поддерживать высокую скорость внедрения новых функций на всем протяжении проекта, оставаться гибкими к изменениям требований, и при этом не жертвовать качеством продукта.
One more thing
Документация — это аспект ИТ-проектов, который часто остается в тени историй про архитектуру, проектирование и общение с заказчиком. Но это очень важная деталь, невнимание к которой может усложнить эксплуатацию и развитие даже самой замечательной, с точки зрения внутренней организации, системы.
Нами был выбран подход, при котором документация эволюционирует по ходу прохождения цикла разработки:
- Начальные требования от заказчика сразу же образуют первые страницы будущей документации
- На этапе проектирования она дополняется техническим описанием планируемого варианта реализации
- Во время оценки и планирования она дополняется ответами на вопрос разработчиков, если они не были учтены во время проектирования
- После того как требования попадают в разработку, они декомпозируются по задачам, каждая задача получает ссылку на раздел документации, в соответствии с которой она должна делаться
- При приемочном тестировании реализованной функции проверяется не только её правильная работа, но и актуальность документации — часто при реализации делаются уточнения и «всплывают» моменты, которые не были очевидны при проектировании, все несоответствия возвращаются аналитику в работу
- По завершении приемки и тестирования разработанная функция имеет набор тестов и итоговую проверенную документацию.
Подобный подход дал сразу несколько преимуществ:
- Работа над документацией оказалась распределена по процессу разработки, мы избежали ситуации, когда по завершении нужно выделить большой ресурс на документирование
- Документация по каждому модулю правилась в тот момент, когда все участники максимально погружены в тему, и это позволило сделать ее максимально точной и не тратить время на попытки вспомнить «а как же оно сделано», которые неизбежны, если бы мы вынесли этот этап за рамки разработки
- В случае каких-то споров и сомнений у нас всегда есть возможность провести трассировку, включающую исходные требования, задачи на разработку и документацию
Самое главное — мы в любое время можем предоставить заказчику документацию как по текущей версии платформы, так и по планируемым изменениям, чтобы обеспечить готовность с их стороны к обновлениям.
Благодаря паре дней, которые ушли на общую редактуру и настройку выгрузки из Confluence, выгрузка итогового документа сейчас может быть выполнена в течение часа по всем компонентам системы.
Вместо заключения
Мы постарались описать общий принцип работы двух компаний и раскрыть внутреннюю кухню разработки.
Было бы здорово услышать от вас сложные вопросы, которые бы мы могли использовать как основу для новых статей!
Спасибо ребятам за такой подробный рассказ! Это только первая часть технической составляющей карты Мегафона, будут ещё истории. Оставайтесь на связи.
Смежные темы
- Микросервисы, хорошие, плохие, злые
- Микросервисы: пожалуйста, не нужно
- Микросервисы (Microservices)
- Переход от монолита к микросервисам
Ссылки
- Банковская карта от «МегаФона»
- Как это работает: карта МегаФона
- Сайт карты МегаФон
- Сайт компании Tyme.ru
Автор: «МегаФон»