Как мы создали продвинутый интернет-банк за 5 месяцев Anton Keks, 17 Dec 2012

Как мы создали продвинутый интернет-банк за 5 месяцев Anton Keks, 17 Dec 201220 ноября Банк Санкт-Петербург запустил новый интернет-банк .

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

Аджайл

Разрабатывали мы его вчетвером, и разработка началась в конце апреля-начале мая. В это время мы столкнулись с новыми для нас системами АБС и процессинга, с которыми было необходимо интегрироваться. К счастью, нам дали прямой доступ к людям внутри банка, кто говорил с этими системами «на ты». Под доступом я подразумеваю Skype контакты, email и номера телефонов. При отсутствии в команде всяческих аналитиков и без необходимости писать какие-либо ТЗ мы начали писать код.

Несмотря на то, что разработчики находились в Таллинне, а остальная часть команды в Питере, мы тесно общались: ежедневные Skype/email переписки, регулярные видео-конференции несколько раз в неделю и обязательные (каждые две недели) встречи в офисе банка в Питере. Летом даже получилось два раза скататься туда на мотоциклах, что, конечно же, добавило фана в проект. А в июне проработали почти 3 недели в банке на месте, чтобы лучше прочувствовать среду, в которой находятся конечные пользователи, разобраться со спецификой российских безналичных платежей (особенно налоговых) и ознакомиться с местными барами и клубами.

Мы работали в двух парах (да-да, парное программирование) и каждый день старались делать ротацию, чтобы было веселее и чтобы у всех было бы хорошее понимание того, где и как в коде что работает. Уже 1 июня — через месяц после начала работы мы установили первый билд интернет-банка в боевую среду, т. е. подключили его к реальным системам. Доступен он был, конечно, только внутри банка, но сотрудники уже могли туда заходить и смотреть, как он работает на реальных данных. Быстрая обратная связь дала нам возможность очень эффективно двигаться дальше, постоянно добавляя новую функциональность.

Иными словами то, что мы делали — можно назвать аджайлом (Agile software development), и если ещё точнее, то XP — экстремальным программированием. Такого рода процесс разработки мы отточили уже на многих наших предыдущих клиентах и очень успешно использовали его и работая в эстонском Swedbank-е.

Архитектура

Вообще, я не люблю это слово. Много раз выступал на IT-конференциях разбивая в пух и прах традиционное понятие IT архитектуры. Иногда даже на Хабр попадают видео с моими выступлениями. Мне очень нравится определение архитектуры Мартина Фаулера (Fowler): архитектура — это та часть дизайна ПО, которая очень тяжело меняется, и поэтому люди используют это слово, чтобы показать насколько она важна. В нашем интернет-банке не должно было быть ничего такого, так что вооружившись религией чистого кода (Clean Code, Bob Martin ) и простоты, мы выбрали Play Framework .

«Что? Play Framework в банке, ты наверно шутишь?» — была первая реакция знакомых разработчиков.

Давайте посмотрим, что повлияло на решение:

  • Мы хотели писать на Java, так как имеем большой опыт с языком и платформой. Java ещё не мертва, если её использовать правильно.
  • У нас была относительно маленькая команда и немного времени, так что мы должны были быть максимально продуктивными. В этом нам помогла также среда разработки Intellij IDEA, кстати, разрабатываемая в Питере.
  • Java запускается на Линуксе, администрирование которого в разы проще и быстрее, чем всяких там Виндовсов. Это мне за пивом подтвердили администраторы, намучившиеся с прежними решениями.
  • Play Framework делает разработку на Java быстрее за счет автоматической перекомпиляции и перезагрузки кода, да и позволяет все это запускать на своей машине за секунды без пожирания огромных количеств оперативной памяти, что свойственно application-серверам в мире Java, и за что её недолюбливают стартапы, где продуктивность цениться превыше всего.
  • Конечно же, как и многие другие фреймворки, Play также решает многие типичные проблемы разработки веб-приложений как структуризация проекта и т. п. очень элегантно, принося лёгкость динамических языков в мир Java. Многие идеи инспирированы Ruby on Rails, Django для Python и даже NodeJS.

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

Чего ещё хорошего дал нам Play?

Play — это полностью stateless framework. Он работает так, как и рассчитан работать HTTP — сервер не знает ничего про сессии пользователей, не выделяет лишней памяти для них, так что приложение масштабируется линейно — при увеличении количества серверов в два раза мы можем обслуживать в два раза больше пользователей. Пока их только два и они совсем несильно нагружены. Серверы находятся в разных датацентрах и между ними идет распределение нагрузки через DNS. Так как информация о сессии пользователя хранится только в cookie, то при падении одного из них пользователи перекидываются на второй без необходимости нового входа.

Это, также, дает нам возможность делать обновления приложения посреди дня, не теряя активных пользователей. В день запуска мы обновляли приложение 4 раза, и все это во время того, как мы получали более 10 запросов в секунду от пользователей. Конечно же, данные пользователя в cookie подписаны, так что пользователь не может их подделать.

Для маскирования невысокой производительности АБС мы используем интеграцию Play с memcached, что далеко не стандартное решение в мире Java. При работе с кешем мы, конечно, учитываем, что записей в нем может и не быть, но если они там есть, то не делаем лишних и, как правило, медленных запросов к АБС. Мы даже подгружаем данные о счетах и картах клиента ещё до того, как он успел ввести СМС код для аутентификации.

Для подгрузки в фоновом режиме нам очень пригодилась родная поддержка Job-ов в Play. работающая на основе Quartz. При вводе правильного пароля мы просто асинхронно запускаем соответствующий Job в отдельном потоке. Job-ы также используются нами и для множества других фоновых задач внутри интернет-банка — от обработки регулярных платежей до принятия банковских сообщений через SMTP протокол.

Если уже начали говорить об асинхронности, то надо отметить и возможность Play обслуживать множество одновременных HTTP запросов в одном потоке, чем тоже очень хвалится NodeJS. Это позволяет экономить ресурсы памяти и обслуживать гораздо большее количество пользователей меньшим количеством процессов. Play содержит отличный асинхронный фреймворк на базе Promise-ов и continuation-ов, выполняя большинство I/O операций асинхронно, высвобождая потоки на это время для других задач (для тех кто владеет темой).

Тестирование

Где Play не соответствовал нашим ожиданиям — это автоматические тесты (а как ещё делать по несколько обновлений в день на боевой системе без регрессий?)

Play содержит поддержку тестирования. но она, на наш взгляд, не совсем адекватна — мы не хотели запускать тесты в браузере, особенно unit-тесты, и мы хотели иметь удобную возможность писать код с помощью методики TDD (Test Driven Development), т. е. писать тест для ещё несуществующего кода и только потом писать свой код, используя тест как критерий готовности. Большинство функциональности можно покрыть unit-тестами, которые запускаются и пробегают быстрее, чем интеграционные. Когда приложение должно работать со сторонними системами, мы делаем к ним тестовый запрос, сохраняем ответ в исходном виде и пишем unit-тест, получающий сохраненный ответ сторонней системы на вход и проверяющий корректность интерпретации входных данных. Это очень удобно как для разработки, так и для документации поведения той системы.

Для остального мы используем библиотеку Selenide. разработанную нашей компанией на базе Selenium WebDriver. Она позволяет писать лаконичные тесты пользовательского интерфейса, управляющие реальным браузером. При запуске тестов с помощью JUnit автоматически стартует весь интернет-банк с тестовой in-memory базой данной, создаются все необходимые таблицы, загружаются туда тестовые данные, запускается браузер и затем выполняются уже сами тестовые сценарии. Всё это занимает всего несколько секунд, что немаловажно для продуктивной разработки. Все тесты, покрывающие большинство функциональности приложения, пробегают чуть больше, чем за 5 минут, что даёт нам достаточно быструю обратную связь в случае возникновения регрессий. Конечно, для достижения такой быстроты мы прошли через много стадий их оптимизации. Главная мотивация оптимизации была в том, что все тесты являются частью билда, а не запускаются отдельно от него, как делается в большинстве проектов.

Билды

Для билдов мы используем Jenkins. Он следит за любыми изменениями в коде и сразу же делает новый билд, предварительно запуская все тесты. Полностью автоматически. Когда нам надо установить какие-либо изменения в боевую среду, то мы просто берём для этого последний удавшийся билд, ничего специально для этого делать не надо. Если нужно быстрое исправление, то достаточно закоммитить изменение в систему версионирования (git), и через 5 минут можно ставить. Как правило, такие срочные билды содержат и другие не очень срочные изменения, то так как они уже на тот момент протестированы, то нет проблем с их попаданием в боевую среду. И, делая обновления достаточно часто, количество изменений устанавливаемых за раз невелико, благодаря чему вероятность большой поломки очень мала и откат на предыдущую версию в случае необходимости тоже проходит с без лишнего риска.

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

Простота / Usability

Несмотря на все навороты и эффективный способ разработки, никто не будет использовать новый интернет-банк, если он сложен или непонятен для пользователя. «Не заставляй меня думать» называется известная книга Стива Круга на эту тему.

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

В процессе упрощения платежных форм (которые в России просто кошмарно сложны) нам пришла в голову идея того, что позже банк стал рекламировать как «умный перевод”: а что, если будем начинать платёж только с одного поля «кому»? Пользователь может просто начинать писать всё, что угодно: имя, название организации, номер счёта или карты — и интернет-банк догадывается, что было введено, и подгружает другие необходимые данные? A-la поиск Google. Начали с прототипа и всем понравилось! Конечно же, идею можно совершенствовать ещё дальше, но это уже дело техники.

Другой немаловажный аспект — это проверять идеи на реальных людях — домохозяйках, школьниках, сотрудниках компаний и т. д. Для этого банк провёл usability-тестирование, во время которого добровольцев просили сделать конкретные задания (например, «переведи 100 рублей своей бабушке») и наблюдали, насколько быстро и легко пользователи с ними справлялись. Это ещё один вид обратной связи, необходимый для создания успешного и интуитивно понятного продукта. Позволяет не писать скучную пользовательскую документацию, которую всё-равно никто не читает.

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

Безопасность

Про безопасность интернет-банков можно писать много. Ограничимся тем, что Play framework при правильном его использовании сразу предотвращает такие наиболее распространенные векторы атак, как XSS (Cross-site scripting) и SQL Injection — всё, что показывается пользователю, проходит через принудительный эскейпинг и всё, что посылается в базу через JPA/JDBC биндится и посылается на сервер отдельно от самого текста запроса, исключая возможности его изменения пользователем с помощью хитро составленных запросов. Это элементарная практика, но, тем не менее, специалисты по аудиту безопасности из одной российской компании сознались, что это было первое приложение из когда-либо тестируемых ими, где не было найдено ни одной серьёзной уязвимости. Нам было приятно, но, конечно, один только фреймворк не может полностью решить все проблемы, связанные с безопасностью. Разработчики всегда должны сами помнить о таких вещах, как принадлежность передаваемых идентификаторов или номеров счетов клиенту в активной сессии. Чтобы не оставить где-нибудь дыру, помогает только внимание к деталям и многолетний опыт разработки веб-приложений. Для этого нам на всякий случай пришлось также менять сам Play и отключать автоматическую загрузку объектов из базы по идентификатору, приходящему из запроса. Также ещё есть более хитрые атаки, как CSRF (Cross-site request forgery) или даже HTTP Response Splitting, перехваты сессий, replay-атаки и т. д. нуждающиеся в предотвращении со стороны программистов.

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

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

Вердикт

В целом, нам было очень интересно и приятно разрабатывать это приложение. Надеемся, что простота и понятность нового интернет-банка будут по достоинству оценены клиентами Банка Санкт-Петербург. А для нас, разработчиков, нет ничего приятнее, чем собственный код, работающий в боевой среде и делающий жизнь пользователей немного удобнее! В данный момент мы продолжаем разработку, так что скоро пользователям будут предоставлены новые интересные возможности.**