Skip to content

Mobile Engineer

Build iOS and Android apps that feel smooth, work offline and ship safely

This map goes from choosing a path (React Native + Expo, Flutter or native) through OS fundamentals, UI, offline data, native modules, performance, testing, release and security, ending with on-device AI. It is for web developers, especially React developers, who want to take mobile seriously rather than stop at a working demo.

Start at stage 01
stages
10stages
topics
50topics
weeks, part-time
~34weeks, part-time
  • Foundation
  • Core
  • Advanced
  • ReactReact Native
  • ExpoExpo
  • TypeScriptTypeScript
  • SwiftSwift / SwiftUI
  • KotlinKotlin / Jetpack Compose
  • FlutterFlutter
  • SQLiteSQLite
  • ExpoEAS

My take

I am a frontend developer who writes React and TypeScript every day, not a professional mobile engineer. I explored React Native and Expo through side projects and worked alongside mobile teammates when integrating APIs, WebSocket and login flows shared between web and app. Those moments showed me exactly where mobile differs from the web: app lifecycle, permissions, build signing, store review and the fact that you cannot hotfix whenever you like. This map is the path I would follow if I started the Mobile Engineer role today, checked against official docs as of late 2026.

What I wish I had known earlier

  • What I wish I had known earlier is that React Native is not the web running on a phone: there is no DOM, no CSS cascade, and every touch of native code is a different world.
  • When I started I tested on the newest simulator and felt fine, while real users were holding mid-range Android phones on flaky networks; get a real low-end device from day one.
  • Shipping an app is its own skill: code signing, store review, OTA updates and staged rollouts deserve the same serious study as writing components.
  • Being able to read a little Swift and Kotlin made me less afraid of native modules and let me read native crash stack traces, even when most of the code is still TypeScript.
0 of 10 stages passed

Foundation01–03

  1. 01Stage 01, 2 weeksChoose a path: cross-platform or nativePick a stack that fits your team and product, and explain the trade-offs of that choice.
    I chose React Native + Expo because it reuses React and TypeScript, but that is a contextual choice, not the right answer for everyone. Taking time to understand the other paths made conversations with native colleagues much easier.

    What to learn

    React Native + Expo
    You write UI in React and TypeScript, it renders real native views, and you share most code across iOS, Android and possibly web. Expo is the framework React Native recommends for new projects, handling builds, routing, OTA updates and native libraries. The downside is dependence on the library ecosystem, and sometimes you still drop down to Swift or Kotlin.
    Flutter
    Flutter uses Dart and draws the entire UI itself with the Impeller engine, so the look is consistent across platforms and animation performance is strong. In exchange, widgets are not the OS's native components, so matching the iOS or Android feel takes deliberate work, and the team learns a language that is rarely used outside Flutter.
    Native: SwiftUI and Jetpack Compose
    SwiftUI and Jetpack Compose are both declarative UI toolkits with a mindset close to React: state flows down, events flow up, UI is a function of state. Native gives the best experience and day-one access to new APIs, which suits graphics-heavy apps, deep OS integration or teams staffed for two platforms.
    Kotlin Multiplatform
    KMP lets you share logic (networking, data, business rules) in Kotlin between Android and iOS while keeping native UI. Compose Multiplatform is now stable on iOS, so you can share UI too if you want. It is a natural fit for teams that are already strong on Android.
    Decision criteria
    Weigh the team's existing skills, how much native API access you need, how platform-specific the UI must be, whether web should share the codebase, and long-term maintenance cost. An internal forms-and-lists app is a very different problem from a camera or real-time map app.

    Do

    • Write a short decision record (ADR) explaining why you chose the stack and what would make you change your mind.
    • Prototype the same small screen in two stacks before committing if the team is undecided.
    • Check that must-have libraries (payments, maps, push) are well supported on the chosen stack before you start.

    Watch out for

    • Choosing a stack only because it feels familiar while ignoring real product needs like background processing or Bluetooth.
    • Believing cross-platform means you will never have to open Xcode or Android Studio.

    Checkpoint project: Compare stacks with one screen

    Build the same list screen with pull-to-refresh and a detail screen in Expo and in one other stack (Flutter, SwiftUI or Compose). Done when both run on real devices on both platforms and you have a one-page ADR comparing developer experience, app size and startup time.

    • ReactReact Native
    • FlutterFlutter
    • SwiftSwiftUI
    • KMP

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4StarterWhy does a Flutter app not automatically feel native on iOS and Android?
    2. Question 2 of 4StarterA team picks React Native with Expo. Which expectation is realistic?
    3. Question 3 of 4StarterAn app needs background Bluetooth Low Energy sync with a medical device, and the team has not agreed on a stack. What is the most sensible next step?
    4. Question 4 of 4StarterA team strong in Android and Kotlin wants to share networking and business logic with iOS while keeping native UI on each platform. Which option fits best?
  2. 02Stage 02, 3 weeksMobile platform fundamentalsUnderstand app lifecycle, permissions, threading and memory on iOS and Android so real-world behavior does not surprise you.
    Coming from the web, I was used to a browser tab living forever. On mobile the OS can suspend or kill your app at any moment, and understanding that changed how I design state.

    What to learn

    App and screen lifecycle
    Android has the Activity lifecycle (onCreate, onStart, onResume, onPause, onStop), and the process can be killed under memory pressure and later restored. iOS has active, inactive, background and suspended states, managed through scenes. In React Native, AppState tells you whether the app is in the foreground or background so you can pause polling, save drafts or refresh data on return.
    Permissions
    Camera, location, notifications and photos are requested at runtime, and users can deny permanently or grant partially, such as approximate location or a few selected photos. iOS requires purpose strings in Info.plist; Android needs manifest declarations and handling for when the user must go to Settings. Ask when the user needs the feature, not all at once on launch.
    Threading
    Both platforms have a main (UI) thread, and blocking it too long causes jank or an ANR on Android. Swift uses async/await and actors, Kotlin uses coroutines with Dispatchers. In React Native, JS runs on its own thread, while libraries like Reanimated run animations on the UI thread via worklets.
    Memory and resources
    Mid-range devices have little RAM, and large images or long lists easily get your app killed by the OS. iOS uses ARC, so watch for retain cycles; Android has a garbage collector but can still leak through long-held contexts. Resize images, clean up listeners on unmount, and monitor memory with Xcode Instruments or the Android Studio Profiler.
    Background work and notifications
    Operating systems tightly restrict background execution to save battery: iOS uses BGTaskScheduler, Android uses WorkManager. Push notifications go through APNs and FCM and need token handling, behavior when the app is open, backgrounded or killed, and deep links from a notification to the right screen.

    Do

    • Persist important state such as form drafts instead of trusting the app not to be killed.
    • Request permissions in context, with an explanation screen before the OS dialog.
    • Turn on Don't keep activities in Android Developer options to test state restoration.
    • Move heavy work such as parsing large JSON or processing images off the UI thread.

    Watch out for

    • Requesting every permission on the first screen so users deny them and they are hard to ask for again.
    • Assuming the app is always in the foreground, so timers, WebSockets and polling keep running in the background.

    Checkpoint project: A resilient notes app

    Build a notes app with photo attachments and local notification reminders. Done when the app restores drafts after being killed by the OS, handles every permission-denied case, and stops WebSocket or polling when it goes to the background.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4StarterOn Android, a user fills a long form, switches to the camera app and comes back to an empty form. What is the most likely cause?
    2. Question 2 of 4StarterWhy should you not request every permission on the first screen?
    3. Question 3 of 4StarterThe app polls the API every 5 seconds and users complain about battery drain. What is the right fix?
    4. Question 4 of 4StarterParsing a 30MB JSON file when a screen opens freezes the UI for seconds in a React Native app. What is the right fix?
  3. 03Stage 03, 3 weeksReact Native New Architecture and ExpoSet up and run a modern Expo project on the New Architecture and understand what happens underneath.
    At first I only ran Expo Go and thought that was enough. When I needed a native library, I finally understood why development builds and config plugins are the standard way to work.

    What to learn

    New Architecture: JSI, Fabric, TurboModules
    Since React Native 0.82 the New Architecture is the only architecture: the old asynchronous bridge is replaced by JSI, which lets JS call C++ directly and synchronously. Fabric is the new renderer that supports React's concurrent features, and TurboModules are lazily loaded, typed native modules generated with Codegen. Libraries that do not support the New Architecture are now a real risk, so check before adding them.
    Hermes
    Hermes is the default JS engine; it compiles JS to bytecode at build time, so startup is faster and memory use lower. Since React Native 0.84, Hermes V1 is the default. Debug with React Native DevTools, which is based on Chrome DevTools, instead of the older debugging tools.
    Expo Router
    File-based routing in the app directory, close to the Next.js mental model: nested layouts, stacks, tabs, modals, deep links and typed routes out of the box. Recent versions add native tabs that use the OS tab bar, data loaders and API routes. Every screen has a URL, so deep links and universal links come almost for free.
    Development builds, CNG and config plugins
    Expo Go can only run the libraries bundled inside it; real apps should use a development build so they can include any native code. Continuous Native Generation (prebuild) generates the ios and android folders from app.config, and config plugins change Info.plist, AndroidManifest or Gradle in code rather than by hand. This makes SDK upgrades far less painful.
    EAS: Build, Submit, Update
    EAS Build builds the app in the cloud and manages signing credentials, EAS Submit pushes to App Store Connect and Google Play, and EAS Update ships JS updates over the air. Expo releases about three SDK versions a year, each tied to one React Native version, so make regular upgrades a habit.

    Do

    • Switch to a development build as soon as the project grows past a demo instead of staying in Expo Go.
    • Do not commit the ios and android folders generated by prebuild; route every native change through a config plugin.
    • Run npx expo-doctor and npx expo install --check after adding libraries or upgrading the SDK.
    • Follow the Expo and React Native changelogs and upgrade one SDK at a time rather than skipping several.

    Watch out for

    • Hand-editing native files in a prebuilt folder and losing every change on the next prebuild.
    • Adding a library that does not support the New Architecture without checking React Native Directory.

    Checkpoint project: A standard Expo app skeleton

    Create an Expo app with Expo Router (tabs, stack, modal), strict TypeScript, a development build and a custom config plugin that adds the camera permission. Done when it builds through EAS for both platforms, a deep link opens the right detail screen, and prebuild --clean loses no configuration.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4StarterWhat does JSI in the New Architecture change compared with the old bridge?
    2. Question 2 of 4StarterWhy should a real project move from Expo Go to a development build?
    3. Question 3 of 4StarterA developer hand-edits ios/<App>/Info.plist in a project using Continuous Native Generation, and the change disappears on the next prebuild. What is the right approach?
    4. Question 4 of 4StarterOn React Native 0.82 and later, what should you check before adding a new native library?

Core04–07

  1. 04Stage 04, 4 weeksUI, navigation and accessibilityBuild interfaces that feel right on each platform, navigate naturally and work with screen readers.
    When I started, I tended to build mobile screens with web habits, and they looked like a shrunken website. Studying the HIG and Material 3 showed me why back gestures, tab bars and system font sizes matter so much.

    What to learn

    Layout and styling in React Native
    Layout uses Flexbox through Yoga with flexDirection defaulting to column, and there is no CSS cascade, so styles are passed explicitly through components. You must handle safe areas, keyboards covering inputs and edge-to-edge, which is now enforced on recent Android. StyleSheet, Unistyles or NativeWind all work, as long as you have consistent design tokens.
    Platform conventions: HIG and Material 3
    Apple's Human Interface Guidelines and Material 3 describe navigation, type sizes, spacing and minimum touch targets (about 44pt on iOS, 48dp on Android). iOS 26 introduced the Liquid Glass design language, and Android has Material 3 Expressive. You do not need two different UIs, but details like the back button, dialogs and pickers should follow the platform.
    Navigation and deep links
    Stacks, tabs and modals should use native navigators so you get swipe-back and Android predictive back. Design URLs for every important screen and configure universal links (iOS) and App Links (Android) so links from the web and notifications open straight into the app. Handle deep links into screens that require login.
    Animation and gestures
    Reanimated runs animations on the UI thread so they stay smooth while JS is busy, and Gesture Handler processes gestures natively. Layout animations and shared element transitions make the app feel more native. Respect the user's Reduce Motion setting.
    Accessibility
    Use accessibilityLabel, accessibilityRole, accessibilityState and accessibilityHint so VoiceOver and TalkBack announce things correctly. Support Dynamic Type and font scaling without breaking layout, with enough color contrast and a sensible focus order. Test for real by turning on a screen reader and completing a main flow.

    Do

    • Test every screen at the largest system font size and with VoiceOver or TalkBack.
    • Use native navigation components instead of building a stack in JS.
    • Define design tokens (color, spacing, typography) with dark mode support from the start.
    • Make every touch target meet the minimum size in the HIG and Material guidelines.

    Watch out for

    • Copying a web UI to mobile with hover states, tiny menus and buttons packed too closely.
    • Hard-coding text heights so the layout breaks when users increase the font size.

    Checkpoint project: An accessible news reader

    Build a news reader with tabs, a list, a detail screen, sharing and deep links from the web. Done when the main flow can be completed using only VoiceOver and TalkBack, the layout holds at the largest font size, dark mode is supported and back gestures work correctly on both platforms.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4IntermediateIn React Native, how does <View><Text>A</Text><Text>B</Text></View> with no styles lay out A and B?
    2. Question 2 of 4IntermediateWhy does an animation written with Reanimated stay smooth while the JS thread is busy?
    3. Question 3 of 4IntermediateUsers raise the system font size and button text gets clipped because buttons have height: 40. What is the right fix?
    4. Question 4 of 4IntermediateAn icon-only trash button is announced by TalkBack as just "button". What is the right fix?
  2. 05Stage 05, 4 weeksOffline-first data and networkingDesign a data layer that works without a connection, syncs when the network returns and resolves conflicts sensibly.
    On the web I was used to treating the server as the only source of truth. On mobile a flaky network is normal, so I had to learn to treat the local database as the primary data source for the UI.

    What to learn

    Local storage
    Use expo-sqlite (or op-sqlite) for structured data, often paired with Drizzle ORM for typed schemas and migrations. Use MMKV for small, fast key-value data like settings instead of the slower AsyncStorage. Sensitive data such as tokens does not belong here; it goes into the Keychain or Keystore.
    Offline-first architecture
    The UI reads from the local database, and writes go to local storage first, then into a queue (outbox) that is sent to the server when online. Each operation needs a client-generated id and an idempotency key so retries never create duplicates. Track connectivity with NetInfo, but do not fully trust it; handle request errors as usual.
    Sync and conflict resolution
    Sync deltas by timestamp or version instead of reloading everything. Pick an explicit conflict strategy: last-write-wins, per-field merge, or CRDTs for collaborative data. Engines such as PowerSync, WatermelonDB or CRDT-based solutions save you from rewriting the hardest part yourself.
    Networking and caching
    TanStack Query on React Native needs focusManager wired to AppState and onlineManager wired to NetInfo so it refetches at the right moments. Use timeouts, retries with backoff, cursor pagination and compressed payloads. Persist the query cache so the app shows stale data instantly while fresh data loads.
    Real-time on mobile
    WebSocket or SSE connections drop when the app goes to the background, so you need reconnect with backoff and a catch-up sync when returning to the foreground. For important updates while the app is closed, use push notifications (possibly silent push) to trigger a sync instead of holding a connection open.

    Do

    • Design a local schema with versioned migrations from the very first release.
    • Give every write a client-side id and an idempotency key.
    • Show sync status clearly (pending, failed, saved) so users trust their data.
    • Test with airplane mode, throttled networks and network switches mid-request.

    Watch out for

    • Storing the whole app state as one big JSON string in AsyncStorage and rewriting it on every change.
    • Forgetting migrations so users on an older version crash after updating.

    Checkpoint project: An offline-first task app

    Build a todo app with projects, labels and sync against a NestJS API or an existing backend. Done when you can create, edit and delete offline, sync correctly once online, retries never create duplicates, and at least one schema migration runs on existing data.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4IntermediateIn an offline-first architecture, why does each write in the outbox need a client-generated id and an idempotency key?
    2. Question 2 of 4IntermediateWhy should you not fully trust NetInfo's connectivity state?
    3. Question 3 of 4IntermediateThe app stores 5,000 tasks as one big JSON string in AsyncStorage and rewrites all of it on every edit. It keeps getting slower. What is the right fix?
    4. Question 4 of 4IntermediateTanStack Query on React Native does not refetch when the app returns to the foreground. What is the right fix?
  3. 06Stage 06, 4 weeksNative modules and device APIsUse the camera, location and biometrics, and write a small native module in Swift and Kotlin when libraries fall short.
    The part I dreaded most at first was opening Xcode. Writing a small Expo Module in Swift and Kotlin was when I realized native is not as scary as I imagined.

    What to learn

    Existing device libraries
    The Expo SDK includes expo-camera, expo-location, expo-local-authentication, expo-notifications, expo-image, expo-file-system and many more modules that support the New Architecture. Before writing native code, check React Native Directory and the Expo SDK. Read each module's permission setup and background limits carefully.
    Expo Modules API
    Write native modules in Swift and Kotlin with a concise DSL: declare functions, async functions, events and even native views. Modules come with config plugins, autolinking and work well on the New Architecture. It is the easiest way for a React developer to touch native code.
    Turbo Native Modules and Codegen
    The standard React Native approach: write a TypeScript spec, let Codegen generate C++ and native interfaces, then implement them in Objective-C++/Swift and Kotlin. Understanding this helps you read third-party libraries. Nitro Modules is a community, JSI-based option optimized for performance when you call native code very frequently.
    Native views and UI integration
    When you need an OS component such as a map, video player or picker, you wrap a native view and pass props and events across. Expo UI lets you use SwiftUI and Jetpack Compose components directly inside the React tree. Be careful with sizing, layout and the native view's lifecycle.
    Reading enough Swift and Kotlin
    You do not need to be an expert, but you should read optionals, closures and async/await in Swift, and null safety, coroutines and extensions in Kotlin. Knowing how to set native breakpoints in Xcode and Android Studio, read Logcat and read crash logs saves a huge amount of time.

    Do

    • Prefer well-maintained libraries that support the New Architecture before writing native code yourself.
    • Keep native module APIs small, asynchronous and fully typed.
    • Write a config plugin for every native change a module requires.
    • Test native modules on real devices for both platforms, including the oldest OS version you support.

    Watch out for

    • Making synchronous native calls for heavy work that block the JS or UI thread.
    • Forgetting to remove native listeners and subscriptions, which leaks memory.

    Checkpoint project: Biometrics and battery module

    Write an Expo Module in Swift and Kotlin that returns battery level and charging state and emits events on change, plus a lock screen using Face ID or fingerprint. Done when the module has TypeScript types, events are removed correctly on unmount, and it runs on real devices on both platforms.

    • Native Modules
    • SwiftSwift
    • KotlinKotlin

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4IntermediateA native module can be called synchronously through JSI. Why still avoid sync calls for heavy work like video compression?
    2. Question 2 of 4IntermediateA component subscribes with const sub = Module.addListener('progress', onProgress) in useEffect but returns no cleanup. What happens?
    3. Question 3 of 4IntermediateIn Kotlin, what does val len = user.nickname?.length ?: 0 return when nickname is null?
    4. Question 4 of 4IntermediateAn Expo app needs biometric authentication (Face ID, fingerprint). What is a sensible first step?
  4. 07Stage 07, 3 weeksPerformance: startup, lists and jankMeasure and improve startup time, scroll smoothness and animation using real profiling tools.
    When I started I tended to optimize by feel on a simulator, and the results were close to meaningless. The lesson: always measure on a release build and a real low-end device.

    What to learn

    Startup time
    Distinguish cold, warm and hot starts; Android Vitals tracks time to initial display. Reduce startup work: lazy-load screens, defer analytics SDKs, avoid large synchronous storage reads. Hermes bytecode already helps a lot; the rest is discipline about what runs before the first screen.
    Long lists
    FlatList is fine for moderate lists, while FlashList or Legend List recycle views and stay smoother for large ones. Memoize items, keep keyExtractor stable, and avoid creating functions and objects inside renderItem. Use expo-image with properly sized, cached images instead of full-size originals.
    Jank and wasted renders
    The goal is a steady 60 or 120 fps, so any JS-thread work while scrolling or animating is suspect. React Compiler reduces wasted renders, but you still need to split state and avoid overly broad contexts. Run animations on the UI thread with Reanimated instead of calling setState in a loop.
    Profiling tools
    React Native DevTools has a Profiler and Performance panel for renders and JS timing. On the native side, use Xcode Instruments (Time Profiler, Allocations, Hangs) and the Android Studio Profiler or Perfetto. Turn on the Performance Monitor to watch JS and UI thread FPS while interacting.
    App size and assets
    App size affects install rates, especially in markets with slow networks. Track the JS bundle with Expo Atlas, compress and choose sensible image formats, enable R8 on Android, and check sizes in App Store Connect and Play Console.

    Do

    • Measure performance on release builds and mid-range Android devices, never on debug builds.
    • Set budgets for startup time and app size and check them in CI.
    • Profile before optimizing and record numbers before and after every change.
    • Move animations and gestures to the UI thread with Reanimated and Gesture Handler.

    Watch out for

    • Judging speed in development mode, which is much slower than a release build.
    • Wrapping everything in useMemo and useCallback without measuring, while the real problem is oversized images.

    Checkpoint project: A smooth photo feed

    Build a feed of about 5,000 items with images, likes and infinite scroll, then profile and optimize it. Done when you have startup time and scroll FPS numbers before and after optimization on a mid-range Android device, with notes on which changes made a difference.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4IntermediateWhy should you not judge React Native app performance on a debug build?
    2. Question 2 of 4IntermediateWhy are FlashList or Legend List usually smoother than FlatList for very long lists?
    3. Question 3 of 4IntermediateThe profiler shows the list janks because it decodes 4000px originals to show 64px thumbnails. What is the right fix?
    4. Question 4 of 4IntermediateA progress bar is animated with setInterval(() => setProgress((p) => p + 1), 16) and janks while the app loads data. What is the right fix?

Advanced08–10

  1. 08Stage 08, 3 weeksTesting: unit, component and E2EBuild a test suite you trust enough to release often, from unit tests to E2E on real devices in CI.
    I was used to Vitest and Playwright on the web, so I was surprised by how much slower and flakier mobile E2E can be. Picking the right test layer for each kind of bug saves a lot of effort.

    What to learn

    Unit testing logic
    Extract business logic, data sync and formatting into pure functions and test them quickly with Jest using the jest-expo preset. Mock native modules minimally and test behavior rather than implementation details. Offline sync and conflict handling are where unit tests pay off the most.
    Component testing
    React Native Testing Library renders components and queries by role, label and text the way users perceive them. Querying by role and accessibility label tests behavior and pushes you toward better accessibility at the same time. Test loading, error and empty states, not just the happy path.
    E2E with Maestro
    Maestro defines flows in YAML, runs on simulators, emulators or real devices, and waits for the UI to settle, so tests are less flaky. It suits key journeys like login, checkout and onboarding. You can run it in EAS Workflows or your own CI on every pull request.
    E2E with Detox
    Detox is a gray-box framework written in JavaScript that synchronizes with app state (network, animations, timers) to reduce flakiness. It is powerful when you need deep control but harder to configure than Maestro. Pick one tool and invest in it rather than running both.
    Devices and the test matrix
    You cannot test every phone, so build a matrix from analytics: the oldest supported OS, a weak Android device, a small-screen device, and tablets if relevant. Device farms such as Firebase Test Lab or similar services complement the real devices on your desk.

    Do

    • Keep E2E for a few critical journeys and push the rest down to unit and component tests.
    • Add stable testIDs or accessibility labels to elements that E2E tests interact with.
    • Run tests on a release-like build in CI for every pull request to the main branch.
    • Seed data and reset app state before each E2E flow.

    Watch out for

    • Using fixed sleeps in E2E instead of waiting for conditions, which makes tests slow and flaky.
    • Mocking so much that tests pass while the real app is broken.

    Checkpoint project: A test pipeline for the task app

    Add unit tests for sync logic, component tests for the list screen and three Maestro flows (login, create a task offline, sync again). Done when everything runs automatically in CI on every pull request and the E2E flows pass ten times in a row.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4AdvancedWhy keep E2E tests to a few critical journeys such as login or checkout?
    2. Question 2 of 4AdvancedWhy is offline sync and conflict resolution logic where unit tests pay off most?
    3. Question 3 of 4AdvancedA Maestro flow waits a fixed 5 seconds after tapping Login before checking the Home screen, and sometimes fails in CI. What is the right fix?
    4. Question 4 of 4AdvancedThe team can only afford a few test devices. Which matrix makes the most sense?
  2. 09Stage 09, 4 weeksRelease: signing, store review and OTAShip to the App Store and Google Play repeatably, with OTA updates, staged rollouts and crash monitoring.
    On the web I deploy several times a day and roll back in seconds. On mobile a bad build can sit on users' phones for weeks, so the release process has to be far more careful.

    What to learn

    Code signing
    iOS needs an Apple Developer account, certificates, provisioning profiles and entitlements; Android needs an upload key, with Play App Signing holding the app signing key. EAS can manage credentials, but you still need to understand them to handle expiry or team changes. From 2026 Google also requires developer verification for apps installed on certified Android devices, starting in a few countries.
    Store review and policies
    The App Store Review Guidelines and Google Play policies cover privacy, in-app payments, content and sign-in. You need a privacy manifest and privacy labels on iOS, the Data safety form on Play, and you must meet Play's new target API level requirement each year. Prepare a demo account for reviewers and explain sensitive features clearly.
    OTA updates
    EAS Update ships JS and asset changes without review, but only when the runtime version is compatible with the native code already installed. Use fingerprinting to derive the runtime version, separate channels for preview and production, and always have a rollback plan. OTA is for fixes and small improvements, not for getting around store policy.
    Staged rollouts and feature flags
    Google Play supports percentage rollouts, the App Store has a 7-day phased release, and EAS Update can roll out by percentage too. Combine these with feature flags to toggle features without a new build. Watch the crash-free rate before increasing the percentage.
    Crash reporting and observability
    Use Sentry, Firebase Crashlytics or EAS Observe to capture both JS and native crashes, and upload source maps and dSYMs so stack traces are readable. Track crash-free users, ANRs, startup time and network errors per version. Automate the full build, test and submit chain with EAS Workflows or Fastlane in CI.

    Do

    • Automate builds, version bumps and submission in CI instead of building by hand on a personal machine.
    • Always upload source maps and debug symbols for every release.
    • Release by percentage and set a clear crash-free threshold for halting a rollout.
    • Record the runtime version of each build so OTA never sends JS to incompatible native code.

    Watch out for

    • Pushing an OTA update that depends on new native code, crashing the app on launch.
    • Letting a certificate or provisioning profile expire right when you need an urgent release.

    Checkpoint project: A complete release pipeline

    Set up a pipeline with EAS Workflows or GitHub Actions: build, test, submit to TestFlight and an internal testing track, plus channel-based EAS Update and Sentry. Done when a commit to the release branch automatically produces a test build, crashes have readable stack traces, and you have successfully rolled back an OTA update.

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4AdvancedWhy can an OTA update that uses a newly added native module crash the app on launch?
    2. Question 2 of 4AdvancedAn app uses Play App Signing and the team loses its upload key. What happens?
    3. Question 3 of 4AdvancedA new release is at 10% rollout and crash-free users drop from 99.8% to 98.5%. What should you do?
    4. Question 4 of 4AdvancedCrash reports show minified JS and unsymbolicated native frames. What is the right fix?
  3. 10Stage 10, 4 weeksSecurity and on-device AIProtect user data following OWASP MASVS and integrate on-device AI features responsibly.
    At first I assumed app code was secret, until I learned anyone can unpack an app file. On-device AI is the part I find most interesting from my LLM work, because it tackles both latency and privacy.

    What to learn

    Secure storage
    Store tokens and secrets in the iOS Keychain and Android Keystore via expo-secure-store or an equivalent, never in AsyncStorage or unencrypted MMKV. Sensitive data can be bound to biometrics. Everything in the JS bundle is readable, so never embed a secret API key in the app.
    Network security
    Enforce HTTPS with App Transport Security on iOS and Network Security Config on Android. Certificate pinning (public key pinning) reduces man-in-the-middle risk but needs a key rotation plan, or the app can lose connectivity for everyone. Use OAuth 2.0 with PKCE through the system browser for sign-in.
    OWASP MASVS and app integrity
    OWASP MASVS groups requirements into storage, crypto, auth, network, platform, code, resilience and privacy, with the MASTG for testing. Use the Play Integrity API and App Attest so your server can verify requests come from the genuine app. Obfuscation and root or jailbreak detection only slow attackers down; critical logic must be checked on the server.
    On-device AI: OS APIs
    Apple's Foundation Models framework lets you call the on-device Apple Intelligence model from Swift, with guided generation and tool calling. Android offers ML Kit GenAI APIs and the Prompt API using Gemini Nano through AICore, currently on a subset of devices and only while the app is in the foreground. Both require checking device support and having a fallback.
    On-device AI: bring your own model
    In React Native you can run small models with ExecuTorch, LiteRT (TensorFlow Lite) or Core ML through native modules. Weigh model size, RAM, thermals and battery; models are usually downloaded after install rather than bundled. Design a hybrid architecture: light and sensitive tasks run on device, heavy ones go to the cloud.

    Do

    • Review the app against the OWASP MASVS checklist before every major release.
    • Keep all secrets on the server and issue short-lived tokens to the app.
    • Check the device's AI capabilities at runtime and always provide a fallback when unsupported.
    • State clearly in the privacy policy which data is processed on device and which is sent to the server.

    Watch out for

    • Embedding an LLM provider API key in the JS bundle, where it gets extracted and abused.
    • Enabling certificate pinning without a backup key, so the app loses connectivity when the certificate changes.

    Checkpoint project: Secure note summaries

    Add biometric lock, secure-store token storage and on-device note summarization (Foundation Models or Gemini Nano) to the notes app, falling back to an API call through your own backend. Done when no secrets are in the bundle, the app passes the MASVS storage and network checks, and the AI feature works or falls back correctly on unsupported devices.

    • Security
    • MASVS
    • On-device AI

    Quick check

    0/4 answered · 0 correct

    Get at least 3 of 4 right to mark this stage as passed.

    1. Question 1 of 4AdvancedWhy must you never embed an LLM provider API key in the app, even when obfuscated?
    2. Question 2 of 4AdvancedThe app has root and jailbreak detection and obfuscated code. Why must critical logic like balance checks still live on the server?
    3. Question 3 of 4AdvancedThe team is about to enable certificate pinning. What must come with it?
    4. Question 4 of 4AdvancedA summary feature uses Gemini Nano through ML Kit GenAI on Android. What is the right way to handle unsupported devices?

What matters right now, and where things are heading in the next few years.

Now

  • The New Architecture is the only option

    Since React Native 0.82 the legacy architecture is gone, and Hermes V1 has been the default since 0.84. The work now is cleaning out old libraries and making use of React's concurrent features and JSI for native modules.

  • Expo is the default way to start

    Expo Router, development builds, config plugins and EAS have become the standard workflow for new React Native projects. Most teams no longer maintain native folders by hand but generate them with CNG.

  • Native UI inside cross-platform apps

    Expo UI and native tabs let you use SwiftUI and Jetpack Compose components directly, while Liquid Glass on iOS and Material 3 Expressive on Android raise expectations for native feel. Cross-platform apps are judged more strictly on platform fidelity.

  • Stricter release processes

    Google is requiring developer verification for Android apps starting in late 2026, and stores keep tightening privacy requirements. Crash monitoring, controlled OTA and staged rollouts are now baseline skills, not extras.

Next

  • On-device AI becomes a standard feature

    Apple's Foundation Models and Gemini Nano on Android will reach more devices, bringing expectations of offline summarization, classification and assistants. Learn to design hybrid on-device and cloud architectures and to evaluate output quality.

  • Cross-platform boundaries blur

    Compose Multiplatform on iOS is stable, Expo brings SwiftUI and Compose into React, and React Native keeps moving closer to web standards. The valuable mobile engineer understands both the JS and native layers and picks the right tool per screen.

  • Agents writing mobile code and tests

    Xcode, Android Studio and tools like Expo are integrating AI agents that generate code, run builds and test on simulators. Mobile engineers will spend more time on review, architecture and real-device testing, so reading native code and writing E2E tests matter even more.

  • Privacy and security by regulation

    Data and app security regulations keep tightening, and MASVS is increasingly showing up in contracts and audits. Prepare by putting security and privacy checks into CI now.

By the end of this map you can

  • Choose and defend a choice between React Native + Expo, Flutter, native and Kotlin Multiplatform for a specific product.
  • Build an Expo app on the New Architecture with navigation, deep links and accessibility that meet platform standards.
  • Design an offline-first data layer with SQLite, safe sync and versioned migrations.
  • Write small native modules in Swift and Kotlin and read native crash logs.
  • Profile and improve startup time and scroll smoothness on real low-end devices.
  • Run a test and release pipeline with code signing, OTA, staged rollouts and crash monitoring.
  • Protect data according to OWASP MASVS and ship on-device AI features with a fallback.

Related articles

All articles

List Virtualization from Scratch: Rendering 100,000 Rows Smoothly

Building a virtual list in React + TypeScript by hand: windowing math with real numbers, overscan, variable heights with prefix sums and binary search, measuring with ResizeObserver, preventing scroll jumps, accessibility — and when not to virtualize at all.

Performance16 min

Other roles

Roadmap

Frontend Engineer

My main role

Building web interfaces that stay fast, accessible and maintainable as products grow

10 stages, 51 topics, ~34 weeks

Designs, builds and runs the APIs, data and services behind every product

10 stages, 52 topics, ~37 weeks

Find risk before users do and turn quality into something the team can measure

10 stages, 50 topics, ~36 weeks

Builds infrastructure, pipelines and platforms so teams ship fast, safely and reliably

10 stages, 56 topics, ~42 weeks

Build products on top of LLMs: APIs, RAG, agents, evals and running it all in production

10 stages, 58 topics, ~31 weeks