«Для нас уже нет смысла использовать Retrofit»: об Android-разработке в Сбербанк Онлайн
У скольки российских приложений в Google Play написано »50 000 000+ установок»? Очевидно, что каждый такой случай — уникальная история со своей спецификой, так что было бы интересно поговорить с разработчиками. А когда у такого приложения ещё и оценка 4,6, это усиливает интерес.
Владимир Теблоев — один из людей, работающих над Android-приложением Сбербанк Онлайн. Весной, когда Сбербанк-Технологии участвовали в нашей конференции Mobius, он выступил там с докладом, а теперь мы решили расспросить Владимира об особенностях его работы.
— Для начала расскажите, чем именно занимаетесь?
— Я в приложении Сбербанк Онлайн занимаюсь сервисом «Диалоги», позволяющим пользователям переводить деньги в один клик и видеть всю историю переводов как на ладони. Сервис доступен всем пользователям приложения — сейчас это 37 миллионов человек.
Работаю в СберТехе с лета 2016-го — тогда в рамках приложения ещё не было разделения на отдельные команды. А позже, когда в рамках перехода к agile стали за отдельными модулями приложения закреплять разные команды, одной из первых стала как раз команда «Диалогов», и с тех пор я в ней.
— У всех слова «Сбербанк» и «мобильная разработка» ассоциируются со Сбербанк Онлайн. Но в такой крупной компании, вероятно, есть ещё и внутренняя мобильная разработка? Она чем-то отличается от внешней?
— Да, приложения для внутреннего использования тоже есть. Я к ним отношения не имею, но знаю, что там активно используется React Native. У внутренней разработки свои требования: нет строгого дизайн-ревью и навороченной анимации, разработка протекает быстрее с применением кроссплатформенного решения.
Когда экспертиза подрастёт, можно будет её и на «боевом» приложении применить. Хотя в том, что Сбербанк Онлайн сможет активно использовать кроссплатформенную разработку, я сомневаюсь. Там много сложностей, а когда у тебя десятки миллионов пользователей, даже редкая проблема может задеть очень многих людей.
— А как это «даже редкая проблема задевает многих» сказывается на работе? Приходится ли сталкиваться с какими-то экзотическими проблемами, которые у приложений поменьше могут проходить под радаром?
— Порой возникают проблемы на каких-нибудь «особенных» девайсах. На одной кастомной, но распространённой прошивке сильно «выстрелило», и нам пришлось долго разбираться. Оказалось, что проблема в драйвере материнской платы самого устройства — он пытался эмулировать библиотеки под ARMv5, хотя в проекте были только под ARMv7.
Когда пользователей много и цена ошибки высока, это приводит к тому, что раскатывать нужно всё «по чуть-чуть», внимательно следя за отчётами. Если там что-то резко возрастает — тут же останавливаем раскатку и делаем хотфикс. Помимо раскатки «на заданный процент пользователей», мы ещё и географически выкатываем всё по частям: в Сбербанке есть понятие «территориальный банк», и фичи могут постепенно раскатываться по регионам.
— Пока какой-нибудь хипстерский стартап может ставить высокий MinSdkVersion и говорить «все остальные не наша аудитория», у вас другая ситуация, вы не можете махать рукой на людей. Какое у вас сейчас значение MinSdkVersion?
— Сейчас 16, и мы подняли его буквально в прошлом году с 14. Мы смотрим на количество клиентов с определенным SDK, и можем поднять версию, если оно становится меньше 5%. Пока что у нас много пользователей на Android 4.4 KitKat, порядка 16% — нам надо их поддерживать.
— А есть ли что-то из новых версий, на что вы сейчас засматриваетесь и думаете «Как только MinSdkVersion повысим, так сразу вот это используем?»
— Конечно, хотелось бы поднять наш минимальный API до Android 5.0, чтобы в полной мере использовать такие нововведения, как, например, анимация переходов, которая будет работать адекватно и везде. Но, в принципе, это не касается написания функциональности, бизнес-логики, поэтому это не критично. В целом анимации прорабатываются нашими дизайнерами, то есть это можно реализовывать вручную. Так что данный вопрос не критичен, он касается комфорта, «душевного спокойствия» разработчика.
Есть некоторые случаи, в которых у нас проверяется версия, например, SSL pinning. Он по-разному работает в разных версиях Android, поэтому мы реализовываем две версии кода, для устройств Android «до 4.4» и «от 4.4».
Конечно, хотелось бы «просто разрабатывать под Android P и ни о чём не думать» —, но это всегда так, от этого никуда не денешься.
— К упомянутому SSL pinning. Очевидно, что для банка очень важны вопросы безопасности. А как это сказывается на вас, чем ваша работа отличается от работы над не-банковским приложением?
— Отличается очень строгим подходом к личным данным пользователя. Какая-либо утечка — это огромные риски. У нас есть отдел безопасности, который перед каждым релизом тестирует наше приложение. Если есть замечания, работа над ними переходит той команде, которая отвечает за функциональность с обнаруженной уязвимостью.
В маленьких компаниях, думаю, зачастую нет отдела безопасности, который будет пентестить приложение. Если какие-то косяки и обнаружатся, они могут всплыть на 4pda.ru или аналогичном ресурсе.
Также к безопасности относится то, что у нас в приложении используется антивирус. Некоторые пользователи недовольны его наличием, но внедрение антивируса дало нам очень заметное снижение мошенничества. Например, в нашем SMS-банке, где можно смской написать «перевести на X карту Y сумму», с антивирусом уровень фрода в данном направлении удалось снизить до минимума.
— Ещё из соображений безопасности банки ограничивают функциональность в случае с рутованными смартфонами. Что у Сбербанк Онлайн для рутованных устройств запрещено?
— В июне мы отказались от ограниченного функционала для владельцев устройств с root-правами. Сейчас всем пользователям Сбербанк Онлайн на Android доступен полный функционал. При этом защита остается на прежнем уровне благодаря системе фрод-мониторинга.
— А приходилось во имя безопасности ограничивать в чём-то не пользователей, а самих себя как разработчиков, отказываясь от того, что иначе использовали бы?
— Когда в 2015-м мы хотели внедрить Retrofit, у него были проблемы с обфускацией, он криво работал со стандартным обфускатором. Наш отдел безопасности указал на данную уязвимость, чреватую кибератакой на банк и на риски взлома кода, так как API торчит наружу. Тогда мы отказались от Retrofit и до сих пор его не используем. Насколько мне известно, сейчас там проблемы со стандартным обфускатором уже поправлены. Но мы с тех пор написали собственный HTTP-клиент, он работает и всех удовлетворяет, для него уже написаны много обёрток для разных команд, работающих с разными серверами. Смысла менять его на Retrofit уже нет.
— Неизбежный вопрос: что у вас с Kotlin?
— Мы идём в его сторону, но неторопливо. Сложность в том, что над приложением работает сразу множество Android-разработчиков разного уровня, кто-то отлично знает Kotlin, кто-то нет. В целом непреодолимых препятствий для внедрения нет, однако сейчас у нас дефицит ревьюеров для отсматривания Kotlin-кода. Если мы все завтра резко начнём писать на Kotlin, то люди «порвутся» на реквестах. Кроме того, в случае с Kotlin возникают проблемы со статическим анализатором кода, который используется в нашем пайплайне.
Так что Kotlin внедряется маленькими шажками: например, мы пишем на Kotlin тесты и используем data classes (так мы экономим время, чтобы не писать тесты на геттеры, сеттеры, equals (), hashCode () и прочее).
Сейчас это потихоньку обкатывается, а на следующем этапе хотим написать свой DSL для тестирования на Kotlin. И параллельно хотим поднимать уровень знания Kotlin в компании: например, с помощью митапов.
— В случае с «Диалогами» вы занимаетесь мессенджингом, но не прямым аналогом WhatsApp. И поэтому интересно:, а насколько вам оказываются полезны чужие решения, лазаете ли в код опенсорсных мессенджеров?
— Полезно было, когда мы захотели добавлять смайлики. У нас появился вопрос, как сделать панель с ними, и в опенсорсе увидели вариант, где всё легко решается попапом над клавиатурой. Тогда и по высоте всё сходится, и бесшовно для пользователя получается.
Но вообще смотреть на чужие решения — не всегда хорошо, эффективнее формировать собственные, учитывая опыт других. Например, на Telegram лучше вообще не смотреть, потому что из-за огромного размера классов в исходниках их Android-приложения непросто разобраться. Пытаемся идти по своему пути, тем более, что взаимодействие с сервером бывает разное: в том же Telegram это MTProto, у нас — обычные WebSockets.
— Я ленивый интервьюер, поэтому решил просто взять список вещей, с которыми вы связаны по работе, и про каждый пункт спросить «расскажите, как именно обстоят дела с этим».
Первый пункт: вы занимаетесь «архитектурой модулей приложения». Про то, что приложение разделено на модули, уже говорили —, а что ещё можете сказать про архитектуру?
— Она у нас развивается итерационно, ведётся версионность, сейчас мы дошли уже до 17-й версии.
На 16-й внедрили Clean Architecture. Согласовали, кто за что отвечает (presentation, domain, data-слой), какие сущности и где должны использоваться, где должны быть конвертеры — в общем, расписали все архитектурные вопросы и внедрили.
Внедряли следующим образом: все новые фичи должны были быть написаны на нашей новой архитектуре. Если в пулл-реквесте что-то отклоняется от заданной нормы, то такой пулл-реквест уходит на доработку. Но при этом не стали сразу бросаться перепиливать всю старую функциональность, потому что это может вызвать много проблем.
Для презентационного слоя мы выбрали стандартом MVP, но некоторые команды у нас используют MVVM. В презентационном слое мы ничем не ограничены. Например, мы перепилили наш чат на MVI — точнее, на свою интересную реализацию MVI, которая кардинально отличается от того, что написал разработчик Mosby.
Потом мы перешли на 17-ю версию архитектуры и внедрили RxJava, что повлекло за собой архитектурные изменения. Если пользоваться строгими определениями, то теперь наша архитектура получилась гексагональной, от Clean мы «форкнулись». Но они схожи тем, что обе работают по принципам SOLID, поэтому одно в другое довольно плавно перетекает. Сейчас работаем над этим.
В будущих версиях архитектуры хотим отказаться от фреймворка Moxy, использованного для реализации MVP, потому что он вызывает некоторые сложности. Проект большой, в нём используется annotation processing, и при внесении изменений в модули «нижнего уровня» время сборки оказывается большим. А мы стремимся облегчить жизнь наших разработчиков.
— Вторым пунктом идёт «оптимизация работы и потребления памяти». Насколько остро стоит этот вопрос, приходится ли ради пользователей со старыми устройствами постоянно о нём думать?
— Этот вопрос находится в фокусе платформенных команд, они разрабатывают инструменты, которыми пользуются фичевые команды. Необходимость заниматься этим, скорее, возникает по надобности одной из команд. Например, у нас в команде «Диалогов» на ранних этапах разработки чат работал очень медленно. Тогда пришлось закатать рукава, начать с профилировщика, просмотреть, где в приложении узкие места, разобраться с причинами их возникновения.
В отношении оптимизации, например, мы отказались от PNG и постепенно вычищаем их из проекта, чтобы использовать только вектор. На этот год запланирована оптимизация графа зависимости в Dagger, чтобы ускорить холодный старт приложения.
— Перейдём к вопросам тестирования: как оно у вас происходит?
— Я могу рассказать только про нашу команду, в других этот процесс может строиться иначе.
У нас в команде изначально был один тестировщик. Впоследствии ему стало скучно просто тестировать. И он начал просить нас помочь разобраться с написанием unit-тестов. Мы показали ему, как пишутся тесты на БД, на сущности, на парсинг — и таким образом он разгрузил нас, снял с нас часть работы. Это хорошо: и ему интересно, и нам.
Со временем мы пришли к тому, что нужно автоматизировать регресс, нужно писать UI-тесты. Первое время проработкой UI-тестов занимались я и мой напарник, а позже к нам подключился департамент качества — наши тестировщики, которые в прошлом тестировали бэкэнд. Они знают Java, а сейчас их подключили на наш проект для автоматизации всего регресса. Мы вместе сели и рассмотрели решения, которые есть: Appium, Espresso, Selenium.
Остановились на Espresso и стали вместе разрабатывать подходы. Чтобы облегчить тестирование, разработали свой фреймворк, что-то наподобие Kakao. Мы занялись этой работой в начале 2017 года, а сейчас мы имеем большой фреймворк, и большинство тестов собираются как конструктор, потому что написано много матчеров и экшенов для различных ситуаций.
Сейчас наши тестировщики активно просят нас научить их писать UI-тесты, потому что проще написать один раз тест, чем на пяти девайсах «протыкивать» одни и те же действия. Но, конечно, всё не автоматизируешь, и некоторые кейсы всё равно надо проверять вручную.
Что касается разработчиков, то в нашей команде каждые две недели проводится ретроспектива. На одной из них мы пришли к мнению, что разработчики должны проводить хотя бы альфа-тестирование после того, как написали фичу. Чтобы не вылезали какие-то совсем базовые баги вида «приложение падает при старте». Таким образом, разработчики тоже подключились к тестированию. Когда у нас готовится крупный релиз и нужно быстро протестировать фичу, все садятся на регресс и вместе проходят тесты по регрессу. При обнаружении бага разработчики отключаются от регресса, быстро фиксят, и по новой.
— Следующий пункт: код-ревью. Тут у вас есть какая-то специфика, или «как у всех»?
— Есть специфика, вызванная количеством разработчиков. Когда мобильных разработчиков в компании десять, то всё могут ревьюить два-три человека. А как ревьюить код сотни человек? Мы разработали «матрицу ревьюера». Отобрали 20–30 человек, про которых мы точно знаем, что они могут хорошо проревьюить, оставить фидбэк и разрулить спорные моменты в комментариях. Взяли этих людей и разделили между ними все команды.
Почему матрица? Это сделано для того, чтобы у всех ревьюеров была одинаковая нагрузка. Как проходит ревью? У нас в команде требуется как минимум три аппрува. Первый — от кого-то из команды. Второй — от кого-то извне, от команды, которая не занимается данной функциональностью. И третий аппрув — от кого-то из смежной команды. В нашем случае есть несколько смежных команд, и они все смотрят наш код. Ну и, соответственно, должны собираться все билды: unit-тесты и UI-тесты должны пройти без проблем. Таким образом у нас проходит код-ревью.
— Следующий пункт — рефакторинг легаси-кода. Насколько систематически он происходит: именно запланированными задачами, или «понадобилось внести изменения в старый код — заодно порефакторили»?
— Вообще у нас действует своеобразный «принцип бойскаута»: если тронул что-то старое — будь добр сделать как надо, ты теперь соавтор. Но запланированный рефакторинг тоже есть. Например, для «Диалогов» понадобился рефакторинг двух направлений: контактной книги, которую мы используем, и переводов. Контактную книгу вынесли, почистили, переписали всю базу данных на Room, вынесли в отдельный модуль. А платежи у нас были написаны давно с помощью RoboSpice, если вы такое ещё помните, и это делало нам больно. Надо сказать, выпиливание этого оказалось неприятной задачей, потому что завязок на него было много. И нужно было тонко вычищать, чтобы не сломать всем остальным функциональность.
— Ещё в «Сбертехе» вы причастны к обучению программистов. Как выглядит обучение внутри компании?
— С сентября планируется проводить такую программу, как переобучение внутренних сотрудников. Сейчас мы уже определили круг тем по Java и Android. Например, у нас есть джаваскриптеры, джависты и аналитики, которые хотят переобучиться на Android. Для них будет организована такая школа, где будет целенаправленное изучение, по расписанию и с лекциями.
А сейчас у нас регулярно проходят митапы. Выбор тем для них не такой, как на конференциях, где обязательно нужно что-то новое и хайповое. Например, если мы знаем, что у разработчиков с чем-то есть проблемы, то об этом важно рассказать. Из недавнего — один из наших разработчиков рассказывал о векторной графике. Не просто о конкретной библиотеке, которая на Android красиво рисует векторы, а начал с того, как вообще работает векторная графика, и дальше шёл в частное. Рассказывали и про Room, и про Java concurrency, с которой у многих разработчиков проблемы, и про Dagger 2.
Ещё в прошлом году у нас была школа Android-разработки, и мы взяли на работу тех, кто успешно её прошёл. Таких людей не стоит сразу же подключать на какие-то проекты и оставлять самостоятельно вариться. Поэтому к каждому новопришедшему сотруднику и также джуниору приставляется ментор, который будет вести его, развивать и помогать. Это внутреннее обучение.
— Собеседования: у вас они проходят «как у всех», или есть специфика?
— Раньше я думал, что «как у всех», но в итоге оказывается, что они всё же немного особенные. По моему опыту, на рынке можно выделить три часто встречающихся подхода. Первый — спрашивают по трём-четырём темам и оценивают исключительно по ним. Например, я пришёл в компанию на позицию Android-разработчика, а меня рассматривают как человека, который должен отлично знать алгоритмы и синхронизацию в Java, и при этом никак не оценивают то, что я делаю супербиблиотеки. Это может быть обусловлено тем, что компании нужен человек, который прекрасно должен знать какую-то узкую часть фреймворка или языка. Второй — когда собеседуют спустя рукава, почти что разговор за жизнь в течение 30–40 минут. Тут, скорее, дело в компетенциях и опыте интервьюера. Третий — когда на собеседовании рассказывают о проблемах компании и пытаются на месте получить какое-либо решение. Минус этого подхода в том, что решение может не совпадать с мнением того, кто этот вопрос задаёт. На мой взгляд, такие подходы встречаются примерно в половине случаев.
Что касается нас, мы проработали методику рассмотрения кандидата по четырём широким направлениям: ООП, OOD (Object Oriented Design, архитектура), Java Core и Android SDK. Методично вопрос за вопросом проходим по всем темам. Если кандидат в целом уверенно отвечает по теме, постепенно начинаем уходить вглубь, задавать более специфические вопросы. Образно это выглядит как дерево: у нас есть корень, откуда мы заходим в каждую тему, и можем в глубину пройти на пять-семь шагов. Потом кандидат оценивается в совокупности по всем пройденным вопросам. Если собеседование проходит быстро, то начинаем спрашивать по библиотекам, например, Dagger 2, RxJava. Если и на это времени хватило, тогда и по Kotlin. Таким образом, кандидат у нас оценивается в целом. Если человек не разбирается в какой-то одной теме, но хорошо знает другую, это не значит, что он плохой программист. Это значит, что за определённый срок ему следует эту тему подтянуть.
— Ещё в числе ваших рабочих задач есть «исследование и ревью новых технологий». Тут хочется попросить конкретный пример какой-нибудь технологии, которую ревьюили.
— Последняя крупная библиотека — это RxJava, мы рассматривали, как она может повлиять на наш проект. Тестировали в локальных ветках, потом внедрили в один некритичный модуль посмотреть, как она поведёт себя на продакшне. После всего этого приняли за стандарт и определили всех писать новую функциональность на ней.
Из неудачных примеров рассматривали Retrofit, о котором я уже говорил: хорошая библиотека, которая решает свои задачи, но её время для нашего проекта прошло. Внедрять её для того, чтобы у нас было много способов вхождения в сеть — плохая практика.
Также рассматривали библиотеку TinyMachine для реализации стейт-машины — библиотека простая, не расширяемая, то есть одну команду она удовлетворяет, но для других не подходит. Поэтому от неё отказались, поскольку если уж и «тащить» библиотеку, то только ту, которая всем подойдёт. В итоге мы приняли решение написать собственную стейт-машину, благо, это не какой-то rocket science, который тяжело реализовать.
— И последнее: ведение документации. В вашем случае, когда разработчиков много и невозможно держать всё в голове, без аккуратного документирования вообще никуда?
— Да. Первый вид документации — это Java-доки. У нас есть локальный мем «проверка Прилуцкого»: нет Java-дока — пулл-реквест не проходит («Прилуцкого» — в честь одного из лидеров в команде Android-разработчиков: на всех реквестах он так часто писал о том, что должна быть описана документация к коду, и без этого код не пройдет в общую ветку, что родился такой мем). Сейчас разработчики уже понимают, что каждый публичный метод, каждый класс, каждый конструктор — всё должно быть описано Java-доками. Весь код должен быть покрыт доками, даже тесты. Чтобы было понятно, на что этот тест написан, чтобы не возникало, например, вопросов «Что это за payment? Это payment из мессенджера или payment из платежей? Что за paymentTest?».
Кроме того, у нас есть документация в Confluence. Когда я сюда приходил, гайдлайны material design у нас были описаны в облаке, и была пара-тройка статей о том, как мы работаем. Сейчас же все глобальные вещи, затрагивающие всех, обязательно описываются в Confluence. Например, нам нужно вставить сертификаты для доступа к репозиторию, и тот, кто это сделал, пишет статью, чтобы потом в чате не писали миллион раз, что делать в случае неработающего сертификата. Ещё пример: было принято решение о внедрении RxJava и в Confluence описываются best practices — как хорошо это делать, как не нужно это делать, и ссылка на образец. Самый простой пример: как располагать методы в классе, чтобы все было по стандарту.
Эти статьи постепенно, но регулярно пишутся. Сейчас наш Confluence вырос уже до 200 статей по разным вопросам. Такой инструмент помогает в том числе новопришедшим разработчикам. Они изучают Confluence, получают представление о внутренней кухне разработки, в случае же возникновения вопросов могут самостоятельно разобраться и принять решение, не всегда привлекая к этому своего ментора.