- PVSM.RU - https://www.pvsm.ru -

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

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

Автор статьи Антон Кекс [2], опубликовано с его согласия.

Аджайл

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

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

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

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

Архитектура

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

«Что? 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 [7], работающая на основе Quartz [8]. При вводе правильного пароля мы просто асинхронно запускаем соответствующий Job в отдельном потоке. Job-ы также используются нами и для множества других фоновых задач внутри интернет-банка — от обработки регулярных платежей до принятия банковских сообщений через SMTP протокол.

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

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

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

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

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

Билды

Для билдов мы используем Jenkins [12]. Он следит за любыми изменениями в коде и сразу же делает новый билд, предварительно запуская все тесты. Полностью автоматически. Когда нам надо установить какие-либо изменения в боевую среду, то мы просто берём для этого последний удавшийся билд, ничего специально для этого делать не надо. Если нужно быстрое исправление, то достаточно закоммитить изменение в систему версионирования (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 запроса и сессией. Выбор платформы работы интернет-банка (Линукс) в этом случае тоже немаловажен.

Вердикт

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

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

Автор: vadger

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/22871

Ссылки в тексте:

[1] новый интернет-банк: http://i.bspb.ru/

[2] Антон Кекс: http://blog.azib.net

[3] IT-конференциях: http://gotocon.com/amsterdam-2012/speaker/Anton+Keks

[4] в пух и прах: http://vimeo.com/28099011

[5] Clean Code, Bob Martin: http://cleancoder.com

[6] Play Framework: http://www.playframework.org

[7] Job-ов в Play: http://www.playframework.org/documentation/1.2.5/jobs

[8] Quartz: http://quartz-scheduler.org/

[9] тестирования: http://www.playframework.org/documentation/1.2.5/guide10

[10] Selenide: https://github.com/codeborne/selenide

[11] Selenium WebDriver: http://seleniumhq.org/projects/webdriver/

[12] Jenkins: http://jenkins-ci.org/

[13] Источник: http://habrahabr.ru/post/162995/