Разработка
Производительность и скорость: как в Duolingo внедрили MVVM на Android
Преданность Duolingo своей миссии сделала их лучшим приложением в мире для изучения языков. Их стремление к совершенству приложений — созданию передовых образовательных программ без ущерба для доступности — вот что их поддерживало.
Приложение Duolingo начало испытывать проблемы роста из-за сложностей масштабирования их Android-архитектуры. Они смогли решить эти проблемы с производительностью и восстановить продуктивность разработчиков за счет рефакторинга до архитектуры Model-View-ViewModel и использования Android Jetpack Dagger и Hilt для внедрения зависимостей.
Вступление
Duolingo — самое популярное в мире приложение для изучения языков, которое ежедневно запускает более десяти миллионов человек. Компании удалось сделать простым, легким и веселым то, что люди считали пугающим. Этот успех основан на постоянном потоке инноваций и обновлений, а также на бесперебойной работе приложения, способного их реализовать. Для Duolingo даже одно не отвечающее приложение на устройстве в любой точке мира означает, что учащийся потенциально разочарован. Это обязывает их делать совершенные приложения, особенно на устройствах Android, которыми пользуются 60% учащихся, включая их генерального директора, который работает с приложением на телефоне начального уровня. И поэтому, когда в команде Android-разработчиков Duolingo зафиксировали рост числа ошибок типа App not Responding, потерю кадров — и даже получили написанную вручную жалобу — они немедленно приняли меры.
Их ситуация не была такой уж необычной. Приложения, в которых отсутствует масштабируемая архитектура и лучшие практики, часто вначале работают хорошо, но по мере роста технический долг начинает негативно влиять на них. Кодовая база Duolingo на Android была разработана, чтобы позволить им быстро добавлять и выпускать новые фичи, но отсутствие согласованной архитектуры проявлялось во все более частом падении производительности. Приложение начало страдать от нестабильной частоты кадров, визуально несовместимых или прерванных взаимодействий и растущего набора новых ошибок. Все эти ошибки не только причиняли неудобства учащимся, но и стоили команде значительных усилий по их диагностике и исправлению. Команда Android-разработчиков Duolingo поняла, что если они хотят продолжать выпускать новые функции, обеспечивая при этом правильный уровень взаимодействия с пользователем, необходим новый подход к кодовой базе.
Поиск
Во-первых, нужно должны были разобраться в том, что именно происходит. Подробное изучение цифр показало, что по мере добавления новых функций производительность рендеринга приложения снижалась на 5–10% каждый месяц. Например, в одном особенно неудачном выпуске количество сбоев увеличилось на 10%, рендеринг кадров замедлился на 25%, а уроки на устройствах начального уровня запускались на 70% медленнее.
Дальнейший анализ кода привел их к выводу, что большинство проблем приложения можно проследить до одного узкого места: глобального объекта состояния под названием DuoState, который отвечал за поддержание состояния для различных функций приложения. Ряд популярных функций (например, очки опыта и ежедневное отслеживание пройденных уроков) использовали его для доступа к важной информации. Подобная централизация данных когда-то позволяла команде быстро выполнять итерации. Они просто добавляли свойства в DuoState всякий раз, когда требовалась новая функция для обмена информацией в приложении. Но теперь неоптимизированный и частый доступ к объекту приводил к ухудшению производительности.
DuoState был настолько тесно связан со всей кодовой базой, что даже небольшие изменения могли повлиять на остальную часть приложения. Команда опасалась, что даже небольшая новая функция может иметь непредвиденный побочный эффект в виде запуска множества внутренних обновлений приложения, из-за чего весь выпуск будет слишком медленным для многих устройств. Эти спады производительности стали более частыми по мере роста приложения, и команда привлекала новых инженеров, чтобы не отставать от ускоряющегося развития продукта. В 2020 году по мере того, как компания добавляла все новых разработчиков, команда начала замечать значительное падение производительности, возникающее каждые 90 дней. При более внимательном рассмотрении вероятность регресса в данной версии была пропорциональна количеству внесенных в нее изменений. При таких темпах эти регрессии могли полностью сорвать развитие продукта в течение нескольких лет.
Устаревшая архитектура стала узким местом как для производительности приложения, так и для скорости работы команды. После долгих внутренних дебатов в компании прекратили разработку новых функций, в том числе тех, которые тесно связаны с чистой прибылью. В течение двух месяцев команда разработчиков Duolingo занималась рефакторингом своего Android-приложения, которое они назвали «Android Reboot».
Перезагрузка Android
Одним из первых ключевых выводов команды было то, что в их коде не было четких границ. Объект DuoState был легко доступен в любой точке кода, что заставляло разработчиков часто обращаться к нему неэффективными способами. Им нужно было создать лучшее разделение кода в базе и решено было выделить каждую функцию в отдельный, четко определенный модуль, используя архитектурный шаблон Model-View-ViewModel. MVVM позволил удалить вызовы монолитного объекта DuoState и позволил многим модулям работать в отдельных потоках.
Знакомство команды с MVVM и поддержка его со стороны Google сделали этот выбор очевидным. Это позволило четко задокументировать, какая логика в каких файлах должна работать (включая view, view model и репозитории). Это помогло сделать архитектуру более последовательной. Получив четкий путь, команда быстро начала рефакторинг своего монолитного кода в наборы классов с четкими границами и обязанностями.
Наряду с MVVM команда использовала Dagger и Hilt (также включенные в Android Jetpack) для реализации шаблона Репозиторий для замены DuoState. Dagger генерирует четкий читаемый код, который обеспечивает подробное логгирование ошибок, что помогает разработчикам точно понять, что делает их код и устраняет следы ненужного стека для reflected properties. А Hilt сокращает количество стандартного кода, необходимого для этого.
Эта новая архитектура позволила команде разделить DuoState на более мелкие объекты. Это сразу уменьшило ненужную связь между доменами. Например, код, отвечающий за отслеживание прогресса пользователя, теперь может получать доступ к очкам опыта, но не к количеству входов в систему в течение месяца. Эта новая архитектура означала, что, хотя ни одна вещь не была слишком сложной для изменения, для ее изменения во всем приложении требовались координация и планирование. Внедрение новой архитектуры во всей кодовой базе в совокупности привело к значительному увеличению производительности.
Архитектура MVVM облегчает разделение задач между данными предметной области, интерфейсом, который видят учащиеся, и логикой взаимодействия этих двух сфер. Это дало разработчикам Duolingo более продуманный способ контролировать реакцию приложения на внутренние обновления состояния. Теперь они могли разрабатывать более сложный пользовательский интерфейс без риска вызвать регрессию или повлиять на лежащие в основе бизнес-правила.
Продуктивность разработчиков
В прошлом непоследовательное применение шаблонов разработки затрудняло понимание и поддержку различных частей кодовой базы. Без консенсуса, каждый разработчик реализовывал код по своему усмотрению.
MVVM, Dagger и Hilt сформировали у команды более детальное понимание того, как должны быть реализованы новые функции. С этими лучшими практиками код стал проще и предсказуемее. Теперь разработчики могут помочь в отладке функций, над которыми они изначально не работали. А новых разработчиков можно вовлекать более эффективно — так как они изначально понимают архитектуру, они могут сразу вносить значимый вклад. Эта новая ясность значительно повысила скорость разработки команды.
Обеспечение качества
Важно отметить, что новая архитектура также показала, что некоторые функции анимации в приложении плохо работали на устройствах начального уровня. Соответственно, другой основной задачей Android Reboot было сокращение количества мусора, пропущенных кадров и ошибок «Приложение не отвечает» (ANR). Команда использовала шаблон Репозиторий, чтобы упростить обмен данными между потоками. Эти шаблоны гарантировали, что можно использовать ресурсы устройства более эффективно с помощью многопоточных модулей. Перенос работы из основного потока повысил скорость отклика, общую частоту кадров и привел к более плавной анимации на устройствах начального уровня. Производительность на флагманских устройствах также улучшилась.
Лучшее общее впечатление от Android
За шесть месяцев работы с новой архитектурой Android-команда Duolingo продолжила выпускать новые фичи без значительного снижения производительности. Дни, когда им приходилось останавливать разработку функций для поиска и исправления ошибок, остались позади.
Ежедневный показатель ANR приложения упал на 41%. Процент времени, в течение которого частота кадров приложения опускалась ниже целевого, снизился на 28%. И что немаловажно, у пользователей на 40% увеличилась скорость прокрутки уроков, таблицы лидеров и историй в приложении.
Перезагрузка позволила Duolingo распространить свой бренд увлекательного, эффективного и забавного опыта изучения языков на гораздо более широкий спектр устройств с Android.
Заключение
Преданность Duolingo своей миссии сделала их лучшим приложением в мире для изучения языков. Их стремление к совершенству приложений — созданию передовых образовательных программ без ущерба для доступности — вот что их поддерживало.
Если вы заинтересованы в том, чтобы ваша команда сделала собственную перезагрузку Android, ознакомьтесь с кратким тематическим исследованием для владельцев продуктов и руководителей, ссылка на него приведена здесь.
-
Новости2 недели назад
Видеозвонки с Лили, Приключения и пианино — обновления Duolingo
-
Новости2 недели назад
Видео и подкасты о мобильной разработке 2024.39
-
Видео и подкасты для разработчиков5 дней назад
Lua – идеальный встраиваемый язык
-
Разработка2 недели назад
Android сломался или я чего-то не понимаю? — Обсуждение на Reddit