Mobius bike — это сервис по прокату велосипедов и самокатов, разработанный для Таллина (на данный момент запланировано расширение географии).

Гипотеза первого релиза — «приложение по прокату велосипедов, будет востребовано на рынке Европы». В декабре 2019 была пересмотрена основная гипотеза и теперь она звучала так — «может ли сервис по прокату велосипедов и самокатов получить максимальное распространение за счет удобства для конечного пользователя и франчайзи?» Для того, чтобы ответить на этот вопрос, нужно было реализовать следующее:

  • провести работы по оптимизации структуры приложения (как внутренней — backend, так и внешней — дизайн и frontend) для внедрения нового типа транспорта и возможного масштабирования в будущем
  • сделать условия аренды регулируемыми в админке

Любовь Никифорук — менеджер проекта




Ключевые экраны приложения Mobius bike до редизайна

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

Приближался запланированный, двухмесячный рефакторинг кода и мы решили показать новый дизайн заказчику, чтобы оптимизировать код уже под новый интерфейс, если, конечно, заказчик согласится на редизайн. Мы презентовали макеты, объяснили как новый дизайн может упростить взаимодействие с приложением и какие выгоды это принесет бизнесу. И уже в процессе презентации мы поняли, что дизайн «зашёл». Редизайн одобрили и теперь мы выходили на рефакторинг уже с новым интерфейсом и переработанным UX.



Варианты ключевых экранов, которые были созданы в процессе редизайна

Организация работ


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



Agile во всей красе



Тестовый самокат в нашем офисе

Навигация


Первое, что мы решили изменить — это навигацию по приложению. Вместо «бургера» мы сделали нижнее таб-меню (bottom navigation). Наверное вы заметили, что многие приложения переходят именно к этому типу навигации. И это понятно, так как таб-меню упрощает доступ к основным разделам приложения даже если человек держит телефон одной рукой. Но это не единственная причина использования данного типа навигации. Приложение разрабатывается на React Native, поэтому нам пришлось учитывать особенности этой платформы:

Основные навигаторы это так называемые стеки (стопки). Глубокая вложенность стеков ведёт к потенциальным ошибкам, поэтому вложенности, по возможности, нужно избегать, или, хотя бы, не делать её глубокой. При большом количестве экранов сделать это сложнее. Тут на помощь приходят табы. В каждый таб можно вложить стек, и таким образом навигация работает предсказуемее

Никита Ренёв — front-end разработчик




До редизайна — меню «бургер». После редизайна — навигация снизу (bottom navigation)

Интерфейс, компоненты, UI-kit


Кроме изменения навигации, мы отказались от контролов масштаба на экране приложения. Оставили изменение масштаба с помощью жестов. Заменили модальные окна, диалоги и часть экранов на swipe-up экраны. Опять же потому, что так удобнее использовать приложение одной рукой — такой экран можно просто «смахнуть» вниз и закрыть его.



Примеры swipe-up экранов в приложении Mobius bike после редизайна

Также мы упростили процесс регистрации в приложении, сделав вход через Google и Facebook. А кроме привязки банковских карт добавили возможность оплаты с помощью Apple Pay и Google Pay.



Экран входа в приложение и оплаты с помощью Apple Pay

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



UI-kit приложения Mobius bike

Рефакторинг и переход на GraphQL


Основными целями редизайна были:

  • создать более гибкую и масштабируемую систему компонентов, которая бы легко позволяла добавлять новый функционал, например еще один вид транспорта, без ущерба внешнему виду и общей логике взаимодействия с приложением
  • максимально облегчить и упростить интерфейс
  • «освежить» внешний вид приложения


Но кроме обновления интерфейса приложения, видимой стороны редизайна, очень важной частью был переход с RESTful API на GraphQL.

Если по простому, GraphQL — это синтаксис, который описывает способ получения клиентом (телефоном/приложением) данных с сервера, посредством специальных запросов. В зависимости от запроса сервер отдает те или иные данные.

Артём Бухтояров — DevOps/Backend-разработчик


GraphQL имеет три основные характеристики:

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


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

Кроме того, в процессе рефакторинга мы выделили следующие достоинства применения GraphQL:

  • удобная документация для разработчика
  • удачное решение для WebSocket, как для backend, так и для frontend
  • с GraphQL разработчику проще ориентироваться в структуре кода
  • более гибкая разработка, по сравнению с RESTful API, позволяет быстрее и проще добавить что-то новое (в нашем случае — это новый вид транспорта)


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

Изначально, когда у нас был только один вид транспорта, сервер для подключения велосипедов работал вместе с основным бэкендом. Затем, мы вынесли сервер для велосипедных замков как самостоятельный сервис, который можно разворачивать на отдельных серверах — это позволило снизить нагрузку на основной сервер и дало дополнительные возможности для масштабирования. Для передачи сообщений между сервисами мы используем RabbitMQ. И было очень удобно, что у него есть плагин для mqtt — протокол на котором работают наши самокаты, что позволило без труда добавить новый вид транспорта.

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

Франшиза и администрирование


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

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

Проблемы


Конечно не обошлось и без трудностей. Мы отказались от визуализации маршрута в истории поездок, так как. gps-модуль в замке велосипеда, не всегда корректно передает координаты. И вместо красивой линии маршрута, как на дрибловском шоте, мы получали вот это:



Ожидание VS реальность

Была проблема в технической реализации Google карт под React Native. Маркеру на карте нужно постоянно перерисовываться, для того, чтобы изменить своё положение на карте. Перерисовка векторной графики в большом количестве не оптимизирована и поэтому, при определённых настройках, всё очень сильно тормозило. И мы решили заменить векторные маркеры на карте на растровые в формате png. Это дало значительный прирост к скорости работы приложения.

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

В итоге остановились на следующем варианте тарификации: время поездки разбивается на неравные отрезки и каждый отрезок времени тарифицируется отдельно. Например, первые 15 минут поездки стоят €2, если катаешься больше 15 минут, но меньше получаса, то стоимость поездки €3.5, от 30 минут до часа — €5 и т.д. И чтобы пользователю было проще ориентироваться в тарифах, мы решили визуализировать расчет стоимости и сделали его в виде прогресс бара. Теперь пользователь может видеть как изменяется стоимость поездки в реальном времени.



Визуализация начисления стоимости в виде прогресс бара



Экраны с тарифами и абонементами

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

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

Выводы


Frontend


Одной из главных особенностей разработки на React Native является отслеживание производительности компонентов. Чтобы не происходило снижение fps, стоит:
  • следить за количеством перерендеров (перерисовок). Компонент должен перерисовываться только в том случае, если поступающие в него новые данные действительно должны изменить состояние интерфейса
  • использовать нативную анимацию Animated из стандартного пакета React Native с использованием ключа useNativeDrive в конфиге анимации или пакет Reanimated
  • проводить ревью кода JS пакетов или компонентов добавляемых в проект. Не все доступные пакеты могут оказаться оптимизированными, или правильно использующими особенности React Native. Также в разработке на React Native стоит учитывать отличия платформ. Один и тот-же код на iOS и Android может работать совершенно по разному

Сильной стороной React Native можно назвать его универсальность. Нет необходимости иметь 2 разные команды разработки на iOS и Android. Также, зачастую, можно реализовать дизайн-концепции, которые сложнее в разработке на нативных платформах.

Слабая сторона React Native — это его не полная нативность. Необходимо постоянно следить за тем, чтобы не перегружать JS-thread. Болезненность обновлений и установки нативных пакетов до 0.60 версии, пока не ввели autolinker.

Backend


Переход на GraphQL в большинстве случаев упростил разработку API. Она стала понятнее, как для разработчика, так и для клиента.

Достоинства GraphQL:
  • есть возможность автоматически документировать API и это очень удобно
  • позволяет более гибко работать с API. Клиент выбирает только те данные которые ему нужны в данный момент
  • поддержка web sockets «из коробки», что было очень важно так как у нас часто обновляются данные в реальном времени
  • мы можем легко писать скалярные типы если это нужно и в дальнейшем переиспользовать их


Слабые стороны GraphQL:
  • сложнее делать фичи по типу пагинации. Приходится применять дополнительные технологии
  • «из коробки» нет namespace. Нужно самому позаботиться о разделении API. В то же время есть поддержка deprecated-полей, что позволяет в дальнейшем упростить жизнь разработчику
  • необходимо следить за уровнями вложенности для запросов. Можно написать запрос с большой вложенностью и потом долго ждать ответ


Интерфейс


Работая над редизайном нужно помнить, что не стоит делать дизайн ради дизайна. В нашем случае новый интерфейс позволил упростить взаимодействие пользователя с приложением, в том числе, если человек держит телефон одной рукой. Мы учли особенности React Native и, для того, чтобы сократить вложенности, перешли от «бургера» к bottom navigation. Также необходимо заложить возможность масштабирования структуры приложения. Здесь может помочь создание библиотеки компонентов и применение атомарного дизайна.

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