🚀 Lessons from Scaling a React Native App to 1M+ Users

Lav PranjaleLav Pranjale
4 min read

When you’re building for a few hundred users, development feels straightforward: add a feature, test it, release it, repeat. But when your app crosses 1 million users, that simplicity disappears — fast. Suddenly, every decision has scale implications, and tiny cracks in your architecture become glaring system faults.

This post isn’t a collection of best practices you’ve already read on Medium. These are real lessons — earned through late-night outages, debugging marathons, and growing pains — from scaling a React Native app to over 1 million users.


1. Performance Is a Feature — Treat It Like One

At 10K users, we were proud of our animations. At 100K users, we started seeing lag reports. At 1M users? Crashes on older Android devices and rising uninstall rates.

One specific issue: our cold start time on low-end Android phones exceeded 6 seconds, and 15% of sessions crashed due to memory overflows. That was a serious wake-up call.

What helped us:

  • Removed bulky, unmaintained libraries.

  • Introduced Flipper, React DevTools, and why-did-you-render to catch render bottlenecks.

  • Replaced FlatList with FlashList for better virtualization performance.

  • Lazy-loaded screens and large image assets.

Takeaway: What works at dev scale breaks at real-world scale. Measure cold starts, memory usage, and unnecessary re-renders early and often.


2. Architecture Must Evolve — Or You’ll Sink in Your Own Code

As feature velocity picked up, we started drowning in technical debt. Every new feature caused regressions. Developers hesitated to refactor. Productivity tanked.

So, we re-architected from scratch using Clean Architecture principles:

cssCopyEdit📦 src/
  ┣ 📁 features/
  ┃ ┗ 📁 chat/
  ┃   ┣ 📁 components/
  ┃   ┣ 📁 hooks/
  ┃   ┣ 📁 services/
  ┃   ┗ 📁 screens/
  ┣ 📁 shared/
  ┣ 📁 core/ (infrastructure, types, constants)

We also ditched Redux in favor of Zustand, which offered:

  • A hook-first API

  • Minimal boilerplate

  • Scoped state slices for better separation of concerns

Takeaway: You don’t scale code by writing more of it. You scale it by organizing it for change.


3. Release Strategy Isn’t Just DevOps — It’s Damage Control

A single crash in production now meant thousands of users affected in minutes. One bad push cost us our Play Store rating.

We needed to ship safely, not just quickly.

What saved us:

  • Feature Flags via Firebase Remote Config for instant kill switches.

  • Expo OTA Updates + EAS for hotfixes without waiting for app store review.

  • Multi-environment CI/CD with Bitrise: staging, QA, beta, production.

We even rolled out a release checklist that all engineers had to follow.

Takeaway: Releases aren’t about shipping features. They’re about preserving trust.
💬 Question: How do you manage emergency rollbacks in your app?


4. Crash Reports ≠ User Experience

Crash reports tell you what broke. They don’t tell you what feels broken to users.

We started tracking:

  • Cold Start Time and Time to First Paint

  • UI blocking operations

  • Funnel drop-offs using Amplitude

  • Custom logs for “rage clicks” and long response waits

This helped us detect pain points before they turned into reviews or tickets.

Takeaway: If you’re only looking at crashes, you’re already too late.


5. Your Team Will Be Your Bottleneck (or Superpower)

Code scales faster than humans. We learned this when onboarding new devs took 3+ weeks, and PRs became unreviewable beasts.

What helped:

  • Introduced codeowners and feature ownership.

  • Used ESLint, Prettier, and commit hooks for consistency.

  • Documented architecture, dev processes, and gotchas in Notion.

  • Scheduled weekly refactoring time as a team-level investment.

Takeaway: An unscalable team produces unscalable code.
💬 Question: How do you onboard new devs efficiently in large React Native projects?


6. Backend Isn’t “Separate” — It’s Half of the App

Early on, we treated the backend as “another team.” This caused misaligned assumptions, slow iterations, and nasty API surprises.

To fix that:

  • We switched to GraphQL, giving mobile devs more flexibility and fewer over-fetches.

  • Created a shared API schema via Zod and Swagger.

  • Added end-to-end tests for mobile+backend contracts.

  • Held weekly syncs between frontend/backend leads to align product direction.

Takeaway: You don’t scale React Native in isolation. You scale the entire ecosystem behind it.


🔚 Final Thoughts

Scaling a React Native app to a million users isn’t just about writing efficient code — it’s about building systems, making smart decisions, and learning faster than your problems grow.

If you’re somewhere on that path — early, mid-scale, or hitting architecture limits — I hope this helped shortcut some of your pain.

Let’s not pretend this journey is easy — but it is possible, and incredibly rewarding.


💬 Your Turn

Which of these lessons resonates most with you?
What would you do differently based on your own scale journey?

Drop a comment — I read every one.


👋 Stay Connected

Follow me for more posts on:

  • React Native at scale

  • Mobile architecture & performance

  • Real-world dev leadership

You can also find me on LinkedIn.

1
Subscribe to my newsletter

Read articles from Lav Pranjale directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Lav Pranjale
Lav Pranjale