Клиентское приложение FuelBuddy начиналось как прогрессивное веб-приложение (PWA). На Android это работало, но на iOS упёрлось в ограничения платформы: рамки движка WebKit, ненадёжная установка на домашний экран, ограниченное хранилище и отсутствие push-уведомлений. Для FuelBuddy это означало, что PWA не может надёжно обслуживать пользователей iOS, и для стабильного мобильного опыта требовалось нативное приложение. Я был частью инженерной команды, которая переписала приложение на React Native.
Над чем я работал
- С PWA на React Native: переписал приложение как скомпилированное приложение на React Native, достигнув 100% паритета функций по 14 пользовательским сценариям за 8 недель и обеспечив единообразное нативное ощущение и на iOS, и на Android.
- Работа по всему стеку: фокусировался на мобильном фронтенде, одновременно строя нужные CRUD-API прямо на движке Hasura GraphQL, так что разработка функций никогда не упиралась в бэкенд-зависимости.
- Выпуск вместе с командой: тесно сотрудничал с фронтенд-инженерами и продакт-менеджерами, превращая требования в протестированные функции и удерживая релизный цикл в продакшене по графику.
Почему это было важно
Переход на React Native дал единую надёжную кодовую базу для iOS и Android. Выпуск 14 пользовательских сценариев с полным паритетом за 8 недель позволил FuelBuddy обслуживать всю мобильную аудиторию без поддержки отдельных реализаций под каждую платформу.
Команда и роль
Я был одним из инженеров на этом переписывании, работая вместе с фронтенд-инженерами и продакт-менеджерами, чтобы уложиться в полный паритет по всем 14 сценариям в 8-недельное окно, которое команда установила для миграции.
Слой GraphQL
CRUD-API работают на Hasura и потребляются через urql, а не Apollo, а GraphQL Codegen генерирует типизированные операции, 73 отдельных .graphql-файла покрывают реальную поверхность приложения. Локальное состояние работает через Zustand, с MMKV в качестве быстрого key-value хранилища под ним, а Zod валидирует данные на границах.
Геолокация и платежи, а не только CRUD
Отслеживание доставки задействует react-native-maps, отдельный сервис геолокации и Turf.js для настоящей геопространственной математики (расстояния, границы, расчёты маршрутов), а не просто маркер на карте. Платежи идут через два независимых шлюза: Easebuzz, интегрированный как локальный пакет, вложенный в репозиторий, а не зависимость из реестра, и Axis Bank, чей API требует, чтобы каждый запрос подписывался AES-256-CBC (с ключом, производным от PBKDF2) и HMAC-SHA512, что реализовано в написанном вручную нативном Android-модуле (NdpsAESLibrary, связанном с JS через NativeModules), а не в JS-библиотеке криптографии, поскольку требование подписи платёжного шлюза не то, что слой RN может удовлетворить универсальным способом. Firebase покрывает и аутентификацию, и аналитику.
Отчёты, а не просто таблица
Сценарий отчётов рендерит данные о доставках и бизнесе как графики через Victory Native, а затем экспортирует те же данные в CSV: кодирует через json2csv, записывает на диск через Scoped Storage API Android (react-native-scoped-storage, обязательный на Android 10+ с тех пор, как приложения потеряли прямой доступ к файловой системе), и передаёт дальше через нативное меню «поделиться».
Модель данных и масштаб
- 13-14 отдельных контейнеров сценариев (адрес, активы, бизнес, доставка, главная, геолокация, заказ, самовывоз, отчёты, настройки, поддержка, пользователь, кошелёк) организуют навигацию приложения, что соответствует цифре «14 пользовательских сценариев».
- Отдельный нативный Android-модуль отвечает за подпись запросов банковского уровня; всё остальное в приложении остаётся на JS/TypeScript поверх моста React Native.
Стек
React Native, urql с GraphQL Code Generator, Zustand для состояния, MMKV для хранения, Zod для валидации, react-native-maps, Turf.js для геопространственных расчётов, Easebuzz и Axis Bank для платежей (последний за кастомным нативным модулем подписи AES/HMAC), Victory Native для графиков отчётности, Firebase для аутентификации и аналитики, поверх Hasura GraphQL.