Understanding Expo's Architecture
Managed vs Bare Workflow
Expo provides two primary workflows: Managed and Bare. The Managed workflow abstracts away native code, letting teams focus on JavaScript. However, this abstraction comes at a cost—limited support for native modules not already included in the Expo SDK.
How It Affects Scalability
In large-scale projects, common needs include integrating proprietary SDKs, altering native behavior, or optimizing performance-critical paths. The Managed workflow becomes restrictive in these cases, forcing teams to eject to the Bare workflow.
Symptoms and Diagnostic Patterns
Common Warning Signs
- 3rd-party SDKs (e.g., Firebase ML Kit) not working as expected
- Build failures related to native dependencies
- App store rejections for missing privacy manifest files (iOS)
- Inability to use background services on Android
Diagnostic Steps
Use expo doctor
to identify mismatched dependencies. Check if needed libraries are supported in Expo Go. Use npx expo prebuild
to preview native build files.
expo doctor npx expo prebuild expo config --type prebuild
Dealing with Unsupported Native Modules
Root Causes
Expo's Managed workflow includes a predefined set of native modules. Libraries requiring custom native code, like background geolocation or hardware integrations, won't work without ejection. The issue compounds when multiple SDKs introduce conflicting Gradle or CocoaPods dependencies.
Resolution Path
To support custom modules, eject the project:
npx expo eject
After ejection:
- Manually configure
android/build.gradle
andios/Podfile
- Use
react-native.config.js
for linking native modules - Maintain a custom development client using
expo-dev-client
Optimizing Post-Ejection Development
Preventing Dependency Drift
After ejection, Expo updates won't automatically propagate. Lock versions of SDKs and scripts. Introduce CI checks to verify Gradle and CocoaPods sync.
Maintainability Tips
- Use
expo prebuild
for previewing managed configs - Track native changes using Git diff before and after upgrades
- Modularize platform-specific logic using platform extensions (.android.js, .ios.js)
Architectural Best Practices
When to Stick with Managed Workflow
- Apps that primarily rely on Expo-supported libraries
- Rapid MVP development or internal tools
- Teams without native development expertise
When to Eject Early
- Complex background tasks or platform-specific SDKs
- Custom UI elements using native views
- Apps requiring advanced permissions or services (e.g., Bluetooth, ARKit)
Conclusion
Expo excels at rapid prototyping and standardized development in small to mid-sized apps. However, when enterprise-scale requirements emerge, architectural decisions must be made early to avoid being boxed in by managed limitations. Knowing when and how to eject—paired with disciplined project structuring—ensures that teams can scale safely without sacrificing developer velocity or platform-specific capabilities.
FAQs
1. Can I return to the Managed workflow after ejecting?
No, ejecting is a one-way transition. You'll need to maintain native configurations manually post-ejection.
2. Is it possible to use custom native code in Managed workflow?
Not directly. You must either use Expo Config Plugins or eject to Bare workflow.
3. What are Expo Config Plugins and when should I use them?
They allow limited native customizations without full ejection. Use them when needing light modifications like adding permissions or manifest entries.
4. How do I debug native code after ejection?
Use Android Studio and Xcode for platform-specific debugging. React Native's Dev Menu still works, but breakpoints in Java/Objective-C require IDE tooling.
5. Does EAS Build support ejected workflows?
Yes, EAS Build supports both Managed and Bare workflows. Configuration becomes more complex post-ejection but is still automatable using eas.json
.