Как мы в QIWI внедряли Kotlin Multiplatform Mobile Часть 2: Смотрим шире

Это продолжение нашего рассказа о внедрении Kotlin Multiplatform Mobile в QIWI. Если хотите узнать больше про технику, посмотреть на код, переходите в первую часть. В этой статье будет больше контекста про то, как мы принимали решение, готовили прототип и внедряли технологию в команды. Наш опыт может помочь вам «продать» KMM в вашей компании и вашим стейкхолдерам. Я расскажу о плюсах и сложностях, с которыми мы столкнулись на нашем пути.

fba63b2f6006024f70d9490cdb3f035e.jpg

Немного истории

В QIWI мы часто пробуем что-то новое в наших процессах и в разработке. За последние 5 лет мы создали несколько приложений с нуля, используя разнообразные подходы. Например, мы впервые попробовали кросс-платформу, когда делали приложение QIWI Инвестор. Мы использовали инструмент J2ObjC, чтобы конвертировать общий модуль с кодом на Java на Objective-c для iOS. Это решение было смелым, но не самым надежным. По ходу использования было много проблем, самые большие — с конвертацией кода сторонних библиотек. В итоге проект закрылся, как и этот эксперимент. Кросс-платформенный подход оказался рабочим, но мы отложили его в сторону пока не появился более надежный инструмент.

В 2018 году мы перешли от платформенных команд к кросс-функциональным. Это стало самым большим изменением в процессе разработки c внедрения скрама. «Совместное владение кодом» стало нашей новой ценностью. Границы между платформами начали размываться, мы стали внимательнее изучать код на других платформах, вместе принимали решения и делились лучшими практиками.

Конечно, на каждой платформе свой стек, своя архитектура, паттерны и лучшие практики. Мы заметили, что мобильные платформы в последние годы развивались практически симметрично: похожая реализация архитектуры MVVM и разделение на слои.  Стало казаться, что мы просто дублируем код в двух платформах на нескольких слоях: слое данных, доменном и презентационном. Скачиваем одни и те же json«ы, пишем похожую логику обработки, генерим те же состояния. Каждый спринт мобильные разработчики по отдельности решали одни и те же проблемы, каждый на своей платформе. Возможно, это небольшая проблема, когда вы работаете над простыми фичами и верстаете простые экраны. Но чем сложнее бизнес-логика, чем больше компонентов и сложнее навигация, тем больше сюрпризов может возникнуть.

Из-за того что мобильные разработчики работают не сообща, финальная реализация продукта на Android и iOS может отличаться. Чаще всего страдает дизайн, а именно обработка корнер-кейсов, когда состояния ошибок и загрузок работают по-разному, это может сильно удивить ваших дизайнеров. Иногда это приводит к более существенным багам, когда бизнес-логика производит неконсистентные состояния на двух платформах. Если вы собираете аналитику о действиях пользователя, то нет гарантии, что источником событий являются одни и те же триггеры, из-за чего может пострадать качество ваших данных. Существует довольно большой простор для неправильной интерпретации фич и проблем коммуникации. iOS- и Android-разработчики по отдельности могут выдать совершенно разные решения для одних и тех же задач, что может усложнить поддержку кода в будущем. В итоге дизайнеры, QA и аналитики должны тщательно перепроверять фичи на двух платформах, потому что единое поведение не гарантировано.

Кажется, что переход на мультиплатформу — это очевидный win-win. Общий модуль с данными, доменной и презентационной логикой обеспечит консистентность везде. Но не все так просто. 

Для нас все началось с прототипа.

Готовим прототип

В конце 2020 кроме кросс-функциональных команд у нас были выделенные платформенные команды iOS и Android. Они отвечали за технический рост платформы и искали лучшие инструменты для фича-команд, чтобы ускорить цикл разработки продукта. Когда мы решили всерьез попробовать Kotlin Multiplatform Mobile, именно платформенные команды занялись созданием прототипа.

Мы решили, что прототип должен включать в себя самые популярные кейсы в нашей разработке и несколько неочевидных, например, работу с базой данных. Самое главное, что прототип должен был объединить архитектурные подходы на iOS и Android и соответствовать нашим инженерным практикам, чтобы всем было удобно использовать новую базу. 

Таким образом, мы пришли к следующим компонентам в прототипе:

  • Общий HTTP-клиент на основе Ktor.

  • Базовая архитектура для новых экранов на основе MVI.

  • Система для логирования и аналитики с базой данных в качестве кэша.

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

Первые вызовы

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

Первые проблемы не стали для нас сюрпризом. Например, мы столкнулись с хорошо известной проблемой concurrent mutablity, когда из нативного кода на iOS пытались модифицировать frozen-объект. Код, который отлично работал на Android, мог легко крешнуться на iOS. Первые подобные баги было сложно поймать, логи содержали далеко не всю информацию о крешах, а выработанной «чуйки» для дебага новой технологии у нас просто не было. Классические варианты дебага работают не так классно, когда вы пытаетесь поймать креш в модуле, который подключен как внешняя библиотека. А после первых релизов мы столкнулись с проблемой некорректных креш-репортов на iOS, но затем нашли хорошее решение в опенсорсе.

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

Android-разработчики столкнулись с другой проблемой: когда продолжаешь писать код на котлине в общем модуле, легко забыть, что больше нет доступа к стандартной библиотеке Java. Например, год назад не было поддержки DateTime из коробки, приходилось искать свои пути, как работать с датами. Некоторые проблемы, например, генерация UUID, легко решались через actual/expect-функции. 

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

Разнообразие мнений

С переездом на KMM наши Android- и iOS-разработчики начали писать код вместе и ревьюить код друг друга каждый день. С первого взгляда кажется, что у мобильных разработчиков много общего: мы делаем один продукт, похожие приложения, решаем очень похожие проблемы, красим одни и те же кнопки. Но мы быстро заметили, что во многих решениях наши взгляды немного отличаются. При этом для каждой стороны эти решения были уже давно проверены в продакшене и закреплены в инженерных практиках. ViewModel на Android и на iOS, это ведь не одно и то же, правда? Иногда даже небольшие изменения в новой архитектуре приводили к долгим дискуссиям. Например, мы обсуждали, что стоит включать во вьюстейт, как его менять, являются ли всплывающие диалоги частью вьюстейта, как валидировать пользовательский ввод, как передавать данные между вьюмоделями и как шарить ресурсы, и подобное.

Все эти проблемы не связаны напрямую с внедрением KMM, скажу очевидную вещь: больше совместной работы — больше мнений. Теперь мы смотрели на каждую проблему с большего числа углов и генерили больше возможных решений. Не все наши решения работали на ура с первого раза, но мы учились работать и экспериментировать вместе, и в итоге всегда получали лучший результат. Это положительно повлияло на качество кода и продукта.

О будущем

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

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

Если вам близок наш подход к разработке, можете оттачивать свои навыки в рамках наших команд — мы ищем разработчиков на Android и iOS.

До связи!

© Habrahabr.ru