App Initialization
Reference guide to the application startup flow, initialization sequence, and stage-based bootstrap process.
App Initialization
Executive Summary
This document outlines the application's initialization architecture, which manages the staged bootstrapping process from launch to fully interactive UI. Our architecture employs a modular, stage-based initialization sequence that balances immediate user feedback with comprehensive system preparation. This approach optimizes perceived performance while ensuring all necessary services are properly initialized.
Purpose & Scope
This guide serves as the primary reference for understanding how the application initializes, and how different parts of the system should participate in the startup sequence. Its purpose is to:
- Define the core initialization stages and their sequence
- Establish patterns for registering initialization tasks
- Explain how to handle dependencies between initialization steps
- Provide guidance on optimizing startup performance
This document is intended for all developers who need to add initialization logic to the application, manage startup performance, or understand the bootstrap process holistically.
Prerequisites
To fully understand the concepts in this guide, we recommend familiarity with:
- Core Architecture - Overall system architecture
- Domain Architecture Guide - Domain services organization
- Authentication Architecture - Auth token handling
Native App Launch Sequence
Before our JavaScript initialization architecture begins execution, the native platforms (iOS and Android) go through their own launch sequences. Understanding these native processes is important for a complete picture of app initialization.
The iOS app launch sequence follows several distinct phases as documented in Apple's official documentation:
- Pre-launch Phase: System verifies app's signature and prepares its environment
- Launch Phase: UIKit creates the app's process and entry point
- Initialization Phase: App delegate methods are called to set up the app's infrastructure
- Event Processing: App begins processing events and displaying UI
Our JavaScript initialization begins during the later part of the Initialization Phase, after the native iOS components have been set up. The splash screen is displayed by the native side during these early phases, providing immediate visual feedback to users while the JavaScript engine initializes.
Our JavaScript initialization architecture is designed to work harmoniously with these native launch sequences, picking up where they leave off to complete the initialization process.
Initialization Stages
Our application uses a staged initialization approach to optimize both actual and perceived performance:
Stage 1: Pre-UI (Critical)
- Purpose: Initialize critical services required before any UI rendering
- Timing: Blocking - must complete before UI can be shown
- Components:
- Authentication token retrieval
- Critical store hydration (theme, language, etc.)
- Configuration loading
- Characteristics:
- Focused only on essential services
- Optimized for speed
- Blocking the UI render
Stage 2: Initial UI
- Purpose: Render initial UI shell to improve perceived performance
- Timing: As soon as Stage 1 completes
- Components:
- Navigation setup
- Initial route determination
- App shell rendering
- Loading indicators
- Characteristics:
- Focused on visual feedback
- May show placeholders or skeletons
- Navigation guards based on auth state
Stage 3: Background (Parallel)
- Purpose: Initialize non-critical services in parallel
- Timing: Started after Stage 2, non-blocking
- Components:
- API client initialization
- TanStack Query prefetching
- Secondary store hydration
- Analytics setup
- Characteristics:
- Parallel execution where possible
- Progress can be reported to UI
- Can be prioritized by importance
Stage 4: Finalization
- Purpose: Complete initialization and enable full interactivity
- Timing: After all background tasks complete
- Components:
- Update UI with loaded data
- Remove loading indicators
- Enable deferred interactions
- Report completion analytics
- Characteristics:
- Ensures smooth transition to fully interactive state
- May be feature-specific rather than app-wide
- Can be gradual rather than all-at-once
Navigation Integration
Our initialization architecture is designed to work seamlessly with both Expo Router and React Navigation. Below are examples of how to integrate with each navigation system using our abstracted SplashScreen component.
Both approaches follow the same initialization flow but integrate with different navigation systems. The key similarities are:
- Execute Pre-UI initialization first (blocking)
- Hide splash screen with a fade transition once Pre-UI is complete
- Continue with remaining initialization stages in the background
- Provide the same state and services through the InitializationProvider
Initialization Tasks
Modular tasks that can be registered for specific initialization stages:
Initialization Event System
An event system for coordinating activities during initialization:
Initialization Provider Component
React component that manages initialization state in the component tree:
Design Principles
Core Architectural Principles
-
Progressive Initialization
- Prioritize critical paths over complete initialization
- Show UI feedback as early as possible
- Load data and services in stages based on priority
-
Modular and Extensible
- Each domain and feature can register its own initialization tasks
- Clear interfaces for adding new initialization tasks
- Flexible dependency management between tasks
-
Failure Resilience
- Graceful handling of initialization failures
- Critical vs. non-critical error differentiation
- Recovery strategies for non-fatal errors
Trade-offs and Design Decisions
| Decision | Benefits | Trade-offs | Rationale |
|---|---|---|---|
| Staged initialization | Better perceived performance, earlier UI interaction | More complex coordination | User experience prioritized over implementation simplicity |
| Blocking pre-UI stage | Ensures critical services are ready | Can delay initial rendering | Prevents jarring state changes after initial render |
| Parallel background tasks | Faster overall initialization | More complex error handling | Optimizes total initialization time |
| Event-based coordination | Loose coupling between systems | More challenging to debug | Allows dynamic registration of initialization components |
Constraints and Considerations
- Memory Usage: Be mindful of initialization processes that consume significant memory
- Initialization Order: Some services have implicit dependencies that affect initialization order
- Error Handling: Have a strategy for handling initialization failures at each stage
- Timeout Handling: Implement timeouts for initialization tasks to prevent hanging
Core Implementation Reference
This section provides the complete reference implementation of the initialization system. The guides in App Initialization Guides build upon this foundation with stage-specific implementation patterns.
Core Types and Interfaces
Initializer Class Implementation
Usage in React Components
The InitializationProvider component manages initialization state and makes it available throughout the React component tree:
Implementation Considerations
Performance Implications
-
(Do ✅) Measure initialization performance
- Track time spent in each initialization stage
- Identify and optimize bottlenecks
- Consider using performance monitoring tools
-
(Do ✅) Minimize Pre-UI blocking tasks
- Only include truly essential initialization in Stage 1
- Move as much as possible to background stages
- Consider lazy loading for non-critical features
-
(Don't ❌) Load excessive data during initialization
- Defer data loading until components mount when possible
- Use pagination or windowing for large data sets
- Consider background prefetching for critical data only
Security Considerations
-
(Do ✅) Handle authentication state securely
- Store tokens in secure storage
- Validate tokens during initialization
- Clear invalid tokens during startup if detected
-
(Do ✅) Implement proper boot security
- Check for tampered application state
- Verify integrity of critical initialization components
- Consider certificate pinning for API communications
-
(Don't ❌) Store sensitive information in non-secure stores
- Use appropriate storage mechanisms for sensitive data
- Clear sensitive data from memory when no longer needed
Error Handling and Fallback UI
Providing a graceful user experience during initialization failures is critical. Instead of just logging errors, implement fallback UI components that guide users through recovery options.
Error States UI Example
Integration in Root Component
Stage-Specific Error Handling
For finer-grained error management during different initialization stages:
Robust Timeout Handling
Timeout handling is critical during initialization to prevent tasks from hanging indefinitely. Here's a comprehensive approach to implementing timeout handling for initialization tasks:
Design Principles for Timeout Handling
-
(Do ✅) Set appropriate timeouts per task
- Consider each task's expected execution time in different environments
- Set stricter timeouts for critical UI-blocking operations
- Allow longer timeouts for background operations
-
(Do ✅) Implement fallback behaviors
- Provide safe default values when initialization times out
- Consider if partial initialization is acceptable for specific features
- Document fallback behaviors in task implementations
-
(Do ✅) Use graduated timeout responses
- For critical tasks, retry before giving up completely
- For non-critical tasks, gracefully degrade functionality
- Use exponential backoff for network-related timeouts
-
(Consider 🤔) Environment-specific timeouts
- Development environments may need longer timeouts
- Production timeouts should be optimized for user experience
- Testing environments might use very short timeouts to catch regressions
-
(Be Aware ❗) Device-specific considerations
- Older/slower devices may need more generous timeouts
- Consider detecting device capabilities for adaptive timeouts
- Monitor timeout frequency in production across device types
Integration with Progress Reporting
Combining timeout handling with progress reporting provides the best user experience:
Performance Measurement and Optimization
Measuring initialization performance is crucial for identifying bottlenecks and ensuring a positive user experience. Implementing a performance tracking system allows you to monitor initialization times across different stages and tasks.
Performance Tracking Implementation
Integration with Initialization Flow
To use the performance tracker with your initialization process:
Performance Optimization Strategies
Here are the most effective performance optimization techniques for app initialization:
- (Do ✅) Establish performance budgets
- Set maximum time limits for each initialization stage
- Create specific budgets for critical tasks
- Regularly audit performance metrics against budgets
- (Do ✅) Implement progressive initialization
- Prioritize tasks based on UI visibility requirements
- Show meaningful UI as early as possible
- Defer non-critical work to background stages
- (Do ✅) Optimize async operations
- Run independent operations in parallel
- Implement timeout handling for unreliable operations
- Cache results of expensive operations
- (Do ✅) Minimize JavaScript bundle size
- Implement code splitting for non-critical features
- Use dynamic imports for components loaded after initialization
- Remove unused dependencies and code
Performance Best Practices
-
(Do ✅) Prioritize perceived performance
- Set target durations for each initialization stage (e.g., Pre-UI < 500ms)
- Monitor metrics regularly to catch regressions
-
(Do ✅) Profile in production-like conditions
- Test on actual devices, not just simulators
- Measure with production builds, not just development builds
- Test with realistic network conditions
-
(Consider 🤔) Progressive enhancement
- Start with minimal feature set and add features after initial rendering
- Use feature flags to enable/disable performance-intensive features
-
(Be Aware ❗) Of device-specific optimizations
- Low-end devices may need different initialization strategies
- Consider device capabilities when prioritizing tasks
Scalability Aspects
-
(Consider 🤔) Feature-level initialization
- Allow features to register their own initialization tasks
- Define dependencies between initialization tasks
- Create domain-specific initialization modules
-
(Consider 🤔) Environment-specific initialization
- Support different initialization flows per environment
- Enable/disable features based on environment
- Configure initialization timeouts per environment
-
(Be Aware ❗) Of interaction between features
- Features may have dependencies on each other
- Some features may require specific initialization order
- Document dependencies between feature initialization
Adding New Initialization Stages
To add a new initialization stage:
-
Define the stage
- Add the new stage to the
InitStageenum - Document its purpose and timing characteristics
- Consider its relationship to existing stages
- Add the new stage to the
-
Update the Initializer service
- Add the new stage to the initialization sequence
- Implement appropriate execution strategy (blocking vs. parallel)
- Add event emission for stage completion
-
Create a registration mechanism
- Define how tasks register for the new stage
- Document any specific requirements for the stage
- Consider dependencies with other stages
-
Test the new stage
- Verify correct execution order
- Measure performance impact
- Ensure error handling works correctly
Related Documents
- Pre-UI Initialization Guide - Implementation guide for the critical Pre-UI stage
- Initial UI Rendering Guide - Guide for implementing the Initial UI stage
- Background Initialization Guide - Guide for background tasks and parallelization
- Finalization Stage Guide - Guide for completing initialization
- Splash Screen Guide - Setting up and managing splash screens during app launch
- Theme Management - Theme system with white-label support
- Core Architecture - Overall system architecture
- Domain Architecture Guide - Domain-based business logic architecture