Привет, Хабр! Меня зовут Александр Меркулов, я занимаюсь разработкой более 20 лет, с 2011 года пишу на Ruby. Сейчас я — backend-техлид в Учи.ру. Наша команда использует большой монолит, который написан на Rails. Также мы создаем и поддерживаем микросервисы и у нас есть нативная мобилка: целых четыре приложения по два на платформу.
Сегодня расскажу как раз про разработку бэкенда мобильного приложения и про полезные лайфхаки, которые помогут вам ее ускорить, минимизировать ошибки и сделать процесс более прозрачным.
Расширенный взгляд на Code Ownership
Начнем с принципа Code Ownership, который позволяет упростить разработку и четко разделить зоны ответственности между командами разработчиков. Важно подчеркнуть, что Code Owners — это именно команды, а не отдельные разработчики (поскольку сотрудники могут переходить из проекта в проект, а структура команд остается относительно постоянной).
В условиях крупных проектов со множеством команд ручная проверка файлов на соответствие определенным владельцам кода становится неэффективной. Именно поэтому мы реализовали автоматическую систему, которая при каждом новом pull request анализирует, какие файлы были изменены, и сверяет это с базой данных Code Owners. Если изменения не соответствуют файлу Code Owners, разработчик должен внести соответствующие коррективы перед тем, как его изменения будут влиты в основную ветку кода.
Как это организовано у нас:
owner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: uchiru/codeowners-validator@v0.8.8
with:
checks: "files,duppatterns,syntax"
experimental_checks: "notowned,avoid-shadowing"
owner_checker_allow_unowned_patterns: true
owner_checker_owners_must_be_teams: true
not_owned_checker_skip_path_patterns: "db/migrate"
Система Code Ownership также позволяет нам реализовать тактику Bug Ownership, когда за каждым обнаруженным багом автоматически закрепляется ответственная команда. Это способствует более быстрому и эффективному решению проблем.
Кроме того, автоматически назначается ответственный за ревью PR, что значительно ускоряет процесс проверки кода и его интеграции в основную кодовую базу. В этом контексте Code Ownership становится инструментом для разделения кода на логические домены — они управляются соответствующими командами.
Эти методики позволяют не только ускорить процессы разработки и интеграции нового кода, но и сделать их более прозрачными и управляемыми на всех уровнях.
Аутентификация: четыре аспекта интеграции
Первоначально мы столкнулись с проблемой интеграции различных сервисов между веб-версией и мобильным приложением. Это произошло потому, что мобильное приложение использовало JWT для аутентификации, а веб-приложение полагалось на cookie.
Сложности были преодолены с помощью аутентификационного Middleware. Программный слой, реализованный на Roda/Sequel, принимает данные аутентификации от мобильного приложения и генерирует соответствующие заголовки (headers) для веб-сервисов. Это позволяет интегрировать в сервисы подходящую для мобильного приложения систему аутентификации без необходимости переписывания большого количества существующего кода.
Один из основных плюсов использования Middleware в данном контексте — его гибкость. В будущем можно легко добавить поддержку других методов аутентификации. Это особенно полезно при дальнейшем расширении функционала или при интеграции новых сервисов.
Стратегия обновления в мобильных приложениях
Обновления мобильных приложений — это не всегда простой процесс выкладывания новой версии в магазин приложений. Реальность гораздо сложнее: истекающие сертификаты, смена платежных провайдеров, критические баги. Все эти факторы требуют быстрого и централизованного управления обновлениями, чтобы мотивировать пользователей переходить на новую версию.
Существует несколько подходов к решению этой проблемы.
Первый — Hard Update или Force Update. В этом случае при старте приложения система на бэкенде проверяет версию приложения у пользователя. Если она не соответствует последней стабильной версии, сервер отправляет команду на принудительное обновление, и у пользователя на экране возникает соответствующее сообщение. Соответственно, либо пользователь обновит приложение, либо не сможет его открыть.
Альтернативный вариант — это Soft Update. В этом случае у юзера появляется всего лишь рекомендация обновиться, но пользователь может просто закрыть это сообщение и полноценно пользоваться приложением.
Этот метод менее навязчив, но и менее эффективен. В большинстве случаев он остается неиспользованным, так как существуют более гибкие инструменты для управления версионностью: например, Feature Flags и А/B-тесты.
Чтобы координировать различные типы обновлений, мы используем административную панель. В ней можно выбирать конкретное приложение, его платформу и версию, с которой будет начинаться либо Soft Update, либо Hard Update. Это делает управление процессом более гибким, позволяет быстро реагировать на различные ситуации, которые могут возникнуть в жизненном цикле мобильного приложения.
Работа с Push-уведомлениями в мобильных приложениях
Работа с push-уведомлениями в мобильных приложениях может быть непредсказуемой и сложной задачей. Скорость их появления зачастую зависит от плана маркетологов или менеджеров по продукту, а разработчикам приходится с этим планом считаться.
Для агрегации и отправки push-уведомлений мы применяем event-driven-архитектуру. То есть, события агрегируются на специализированном сервисе и отправляются пачками на другой сервис, который занимается их фактической отправкой. Это позволяет нам сгладить пики и поддерживать стабильность работы.
Ключевой момент в организации отправки — асинхронность. Синхронная отправка может привести к высокому проценту ошибок, например, из-за устаревших push-токенов или недоступности провайдера. Асинхронная отправка снижает подобные риски и делает систему более устойчивой к сбоям.
Мы разделяем уведомления на категории в зависимости от их критичности. Срочные push-уведомления высылаются как можно быстрее, а остальные, менее важные уведомления могут быть отправлены позже. Это позволяет нам оптимизировать процесс и учитывать различные бизнес-потребности.
Важным аспектом является управление push-токенами. Пользовательские токены могут устаревать (и нужно такой токен исключать) и заменяться на новые токены (их важно сохранять). Этот процесс у нас также автоматизирован и интегрирован в общую систему управления push-уведомлениями, что значительно снижает риски сбоев и улучшает общую производительность мобильного приложения.
Контрактная разработка в мобильных приложениях
В мобильной разработке мы активно применяем контракты. Это означает, что перед началом работы над новым функционалом (или над изменением существующего) команды фронтенда и бэкенда согласуют контракт. Это позволяет вести параллельную разработку, ускоряя процесс и уменьшая риски несоответствия между клиентом и сервером.
Контракт в нашей практике — это не просто документ, описывающий взаимодействие между фронтендом и бэкендом. Он является ключевым инструментом для поддержания обратной совместимости. При любых изменениях в контракте есть возможность вернуться и перепроверить, как эти изменения отразятся на уже существующем функционале. Это снижает риск «поломки» мобильного приложения при изменениях, особенно учитывая тот факт, что веб-разработка обычно движется быстрее мобильной.
Также контрактная разработка позволяет автоматизировать ряд процессов. Например, она делает возможной автоматическую проверку тестов на бэкенде при создании PR или merge в master.
Автогенерация клиента также значительно упрощает разработку: позволяет ускорить ее и уменьшить вероятность ошибок.
В итоге контракты становятся не просто методологией, но и инструментом для более эффективного и безопасного процесса разработки мобильных приложений.
Оптимизация процесса тестирования: интеграция и автоматизация
Тестирование в мобильной разработке у нас проходит по принципу доменного flow. Это значит, что весь процесс тестирования разбит на домены или функциональные блоки — например, «олимпиады» или «карточки». При внесении изменений в определенный домен QA-инженер фокусируется именно на этом блоке, что не только ускоряет процесс, но и повышает его эффективность, так как позволяет более глубоко проникнуть в детали конкретной функциональности.
Автотесты играют ключевую роль в оптимизации тестирования. Они позволяют минимизировать время, затрачиваемое QA-инженером на ручное тестирование, и выявлять проблемы на ранних этапах разработки. Если автотест «падает», инженеру по тестированию и разработчику сразу становится ясно, где требуется исправление.
Одним из наиболее перспективных направлений в оптимизации тестирования является использование контрактов. Контракты позволяют не переписывать с нуля всю логику автотестов при изменении функциональности. Их можно интегрировать в существующие фреймворки для автоматической проверки различных механик. Это особенно удобно, когда в команде работают T-shaped-специалисты, которые могут быть как разработчиками, так и QA-инженерами.
Вышеописанные методы и инструменты создают сильный фундамент для надежного и эффективного процесса тестирования в условиях постоянно меняющегося процесса разработки мобильного приложения.
Успехи и прогресс: реальные цифры
Одним из ключевых успехов, которым я хотел бы поделиться, является сокращение среднего времени выкатки новых фич для мобильного приложения. Ранее этот процесс занимал около 60 дней с момента начала разработки до полного релиза. Однако благодаря оптимизациям на всех этапах — от интеграции веб-функционала в мобильные приложения до улучшения механизмов push-уведомлений и тестирования — сейчас это время сократилось до 16 дней. Это улучшение почти в четыре раза демонстрирует эффективность применяемых нами подходов.
В планах на будущее у нас — создание дополнительных автотестов. Полагаем, что это поможет еще быстрее проходить этапы тестирования и улучшит качество покрытия нашего кода.
В заключение хочу подчеркнуть, что успех в разработке мобильного приложения возможен только при комплексном подходе, который включает в себя эффективное взаимодействие между фронтендом и бэкендом, а также интеграцию между веб-версией и мобильными платформами. Ведь они, по сути, являются частями одного большого продукта.
Следуя этой стратегии и оптимизируя каждый этап разработки, можно значительно сократить время выкатки новых фич и повысить эффективность работы над мобильным продуктом.
Наша команда разработки растет. Если хочешь развивать школьный EdTech вместе с нами, присоединяйся!