Algorithm Performance Specifications
**Document ID:** ALGO-PERF-001 **Version:** 1.0.0-DRAFT **Created:** 2026-01-04 **Purpose:** Define performance requirements and benchmarks for all core algorithms **Parent Document:** [04-Algorithms/README.md](./README.md) **Related Documents:** - [Visit-Sequencing.md](./Visit-Sequencing.md) - [Route-Optimization.md](./Route-Optimization.md) - [Delivery-Optimization.md](./Delivery-Optimization.md) - [Decision-Logic.md](./Decision-Logic.md)
Full Public Reader
Algorithm Performance Specifications
Document ID: ALGO-PERF-001
Version: 1.0.0-DRAFT
Created: 2026-01-04
Purpose: Define performance requirements and benchmarks for all core algorithms
Parent Document: [04-Algorithms/README.md](./README.md)
Related Documents:
- [Visit-Sequencing.md](./Visit-Sequencing.md)
- [Route-Optimization.md](./Route-Optimization.md)
- [Delivery-Optimization.md](./Delivery-Optimization.md)
- [Decision-Logic.md](./Decision-Logic.md)
---
Table of Contents
1. [Overview](#1-overview)
2. [Global Performance Requirements](#2-global-performance-requirements)
3. [Visit Sequencing Performance](#3-visit-sequencing-performance)
4. [Route Optimization Performance](#4-route-optimization-performance)
5. [Delivery Optimization Performance](#5-delivery-optimization-performance)
6. [Decision Logic Performance](#6-decision-logic-performance)
7. [Network Performance](#7-network-performance)
8. [UI Rendering Performance](#8-ui-rendering-performance)
9. [Memory Performance](#9-memory-performance)
10. [Battery Performance](#10-battery-performance)
11. [Offline Performance](#11-offline-performance)
12. [Swift App Parity Benchmarks](#12-swift-app-parity-benchmarks)
13. [Performance Testing Strategy](#13-performance-testing-strategy)
14. [Performance Degradation Triggers](#14-performance-degradation-triggers)
15. [Optimization Priorities](#15-optimization-priorities)
---
1. Overview
1.1 Purpose
This document defines measurable, falsifiable performance requirements for all algorithms in the Milk Men Expo migration. Performance is a critical success criterion (EM-P-001 through EM-P-004) and must meet or exceed the Swift app baseline.
1.2 Performance Philosophy
User-Centric Performance:
- Perceived performance > raw speed
- Instant feedback (optimistic UI) > blocking operations
- Progressive loading > all-or-nothing
- Graceful degradation > hard failures
Asymmetric Performance Targets:
- Interactive operations (tap, scroll): <16ms (60 FPS)
- User-initiated operations (button press): <100ms perceived response
- Background operations (sync): No user-facing latency
- Network-dependent operations: Optimistic UI + background fetch
1.3 Performance Measurement Context
All performance measurements assume:
- Device: iPhone 12 or newer (baseline)
- Network: 4G LTE (10 Mbps download, 5 Mbps upload, 50ms latency)
- Battery: >20
- Data Set: Typical organization (500 locations, 50 routes, 10 agents)
- App State: Warm start (not first launch)
---
2. Global Performance Requirements
2.1 App Launch Performance
| Metric | Target | Measurement Point | Validation |
|---|---|---|---|
| Cold Start | <3s | Tap icon → First interactive screen | Time to Interactive (TTI) |
| Warm Start | <1s | Background → Foreground | Resume to interactive |
| Hot Start | <500ms | Killed → Relaunch (cached) | Cache hit TTI |
Baseline: Swift app cold start = 2.1s (measured on iPhone 12)
2.2 Navigation Performance
| Metric | Target | Measurement Point | Validation |
|---|---|---|---|
| Screen Transition | <300ms | Tap → New screen visible | Animation complete |
| Modal Presentation | <200ms | Tap → Modal visible | Fade-in complete |
| Tab Switch | <100ms | Tap tab → Content visible | State restored |
| Back Navigation | <150ms | Tap back → Previous screen | Unmount + remount |
2.3 Data Loading Performance
| Metric | Target | Measurement Point | Validation |
|---|---|---|---|
| Initial Data Load | <2s | Login → Dashboard data visible | First paint with data |
| Incremental Load | <500ms | Scroll → Next page loaded | Pagination fetch |
| Pull-to-Refresh | <1s | Pull → New data visible | Refresh complete |
| Search Query | <300ms | Keystroke → Results update | Debounced search |
---
3. Visit Sequencing Performance
3.1 Visit Calculation Performance
Function: `calculateNextVisitDate(location: Location): Date | null`
| Input Size | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| Single location | O(1) | <1ms | <5ms |
| 100 locations (batch) | O(n) | <50ms | <100ms |
| 1000 locations (batch) | O(n) | <300ms | <500ms |
Rationale: Visit date calculation is simple arithmetic (date addition) with conditional logic. Should be near-instant.
Validation:
describe('Visit Calculation Performance', () => {
it('calculates next visit for single location in <1ms', () => {
const start = performance.now()
const result = calculateNextVisitDate(testLocation)
const duration = performance.now() - start
expect(duration).toBeLessThan(1)
})
it('calculates next visit for 100 locations in <50ms', () => {
const locations = generateTestLocations(100)
const start = performance.now()
locations.forEach(loc => calculateNextVisitDate(loc))
const duration = performance.now() - start
expect(duration).toBeLessThan(50)
})
})3.2 Overdue Follow-Ups Query Performance
Function: `getOverdueFollowUps(agentId: string, today: Date): Location[]`
| Database Size | Query Complexity | Target Latency | Max Latency |
|---|---|---|---|
| 100 locations | O(n log n) | <10ms | <20ms |
| 500 locations | O(n log n) | <30ms | <50ms |
| 2000 locations | O(n log n) | <100ms | <200ms |
Database Optimization:
- Index on `assigned_agent_id`
- Index on `next_visit_due_date`
- Index on `stage` (exclude 'lost' and 'partner')
- Composite index on `(assigned_agent_id, next_visit_due_date, stage)`
WatermelonDB Query:
const overdueLocations = await database.collections
.get<Location>('locations')
.query(
Q.where('assigned_agent_id', agentId),
Q.where('next_visit_due_date', Q.lte(today)),
Q.where('stage', Q.notEq('lost')),
Q.where('is_partner', false),
Q.sortBy('next_visit_due_date', Q.asc)
)
.fetch()Validation: Use WatermelonDB's `.experimentalMeasurePerformance()` to log query time.
3.3 Visit State Machine Transition Performance
Function: `transitionVisitState(location: Location, outcome: VisitOutcome): Location`
| Operation | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| Visit 1 → Visit 2 | O(1) | <5ms | <10ms |
| Visit 2 → Visit 3 | O(1) | <5ms | <10ms |
| Visit 3 → Partner | O(1) + DB write | <50ms | <100ms |
Rationale: State transitions are simple field updates. Database write dominates latency.
---
4. Route Optimization Performance
4.1 Clustering Performance
Function: `clusterNearbyLocations(stops: Stop[], threshold: number): Cluster[]`
| Input Size (Stops) | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| 5 stops | O(n²) | <1ms | <5ms |
| 10 stops | O(n²) | <5ms | <10ms |
| 20 stops | O(n²) | <20ms | <50ms |
| 50 stops | O(n²) | <100ms | <200ms |
Algorithm: Nested loop comparing all pairs → O(n²)
Optimization: Use spatial indexing (quadtree or R-tree) if n > 50 → O(n log n)
4.2 VIP Scoring Performance
Function: `calculateVIPScore(location: Location, today: Date): number`
| Input Size | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| Single location | O(1) | <1ms | <2ms |
| 100 locations | O(n) | <50ms | <100ms |
| 500 locations | O(n) | <200ms | <300ms |
Rationale: Simple arithmetic with conditionals. Should be near-instant.
4.3 Nearest Neighbor TSP Performance
Function: `nearestNeighborTSP(clusters: Cluster[], start: Location): Cluster[]`
| Input Size (Clusters) | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| 5 clusters | O(n²) | <1ms | <5ms |
| 10 clusters | O(n²) | <10ms | <20ms |
| 20 clusters | O(n²) | <50ms | <100ms |
| 50 clusters | O(n²) | <300ms | <500ms |
Algorithm: Greedy nearest neighbor → O(n²)
4.4 2-Opt Improvement Performance
Function: `twoOptImprovement(route: Cluster[]): Cluster[]`
| Input Size (Clusters) | Time Complexity | Target Latency | Max Latency | Iterations |
|---|---|---|---|---|
| 5 clusters | O(n³) | <5ms | <10ms | ~10 |
| 10 clusters | O(n³) | <50ms | <100ms | ~20 |
| 20 clusters | O(n³) | <300ms | <500ms | ~30 |
| 50 clusters | O(n³) | <2s | <5s | ~50 |
Algorithm: Iterative edge swap → O(n³) worst case, but early termination typical
Time Limit: 5 seconds absolute maximum (fallback to nearest neighbor if exceeded)
4.5 Complete Route Optimization Performance
Function: `optimizeRoute(input: RouteOptimizationInput): Promise<OptimizedRoute>`
End-to-End Latency Targets:
| Input Size (Stops) | Clusters | Target Latency | Max Latency | Baseline (Swift) |
|---|---|---|---|---|
| 5 stops | 1-2 | <50ms | <100ms | 42ms |
| 10 stops | 2-4 | <200ms | <500ms | 156ms |
| 20 stops | 4-8 | <1s | <2s | 890ms |
| 50 stops | 8-15 | <3s | <5s | 2.8s |
| 100 stops | 15-25 | <8s | <10s | 7.2s |
Breakdown (20 stops example):
Stage 1: Clustering (20 stops) → 30ms
Stage 2: VIP Scoring (20 stops) → 10ms
Stage 3a: Nearest Neighbor (8 clusters) → 50ms
Stage 3b: 2-Opt (8 clusters) → 300ms
Stage 4: Time Feasibility → 50ms
Stage 5: Directions API (7 segments) → 500ms (network)
---
Total: ~940ms (within 1s target)Performance Optimization Techniques:
1. Early termination in 2-Opt: Stop if no improvement after 5 iterations
2. Parallel scoring: Use Promise.all() for VIP scoring (if n > 50)
3. Lazy directions: Fetch polylines only when route is finalized
4. Caching: Cache distance matrix between clusters (TTL: 1 hour)
4.6 Haversine Distance Performance
Function: `haversineDistance(from: Coordinate, to: Coordinate): number`
| Input Size | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| Single calculation | O(1) | <0.1ms | <0.5ms |
| 100 calculations | O(n) | <10ms | <20ms |
| 10,000 calculations (n²) | O(n²) | <500ms | <1s |
Rationale: Pure math (sin, cos, sqrt). Should be near-instant.
---
5. Delivery Optimization Performance
5.1 Bin Packing Performance
Function: `assignDeliveriesToVehicles(deliveries: DeliveryStop[], vehicles: Vehicle[]): Map<string, DeliveryStop[]>`
Algorithm: First-Fit Decreasing (FFD)
| Input Size (Deliveries) | Vehicles | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|---|
| 10 deliveries | 1 | O(n log n) | <5ms | <10ms |
| 50 deliveries | 2-3 | O(n log n) | <30ms | <50ms |
| 100 deliveries | 3-5 | O(n log n) | <100ms | <200ms |
| 500 deliveries | 5-10 | O(n log n) | <500ms | <1s |
Breakdown:
- Sort deliveries by box count (descending): O(n log n)
- Iterate and assign to first vehicle with capacity: O(n × v) where v is vehicle count
5.2 Vehicle Route Optimization Performance
Function: `optimizeVehicleRoute(vehicle: Vehicle, deliveries: DeliveryStop[], warehouse: Location): DeliveryStop[]`
Same as Route Optimization Section 4.5, but with additional time window validation.
| Input Size (Deliveries) | Target Latency | Max Latency |
|---|---|---|
| 5 deliveries | <100ms | <200ms |
| 10 deliveries | <500ms | <1s |
| 20 deliveries | <2s | <3s |
| 50 deliveries | <5s | <8s |
5.3 Time Window Validation Performance
Function: `validateTimeWindows(route: DeliveryStop[], startTime: Date): boolean`
| Input Size (Stops) | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| 5 stops | O(n) | <1ms | <5ms |
| 20 stops | O(n) | <5ms | <10ms |
| 50 stops | O(n) | <20ms | <50ms |
Rationale: Linear scan with date arithmetic. Should be near-instant.
5.4 Complete Delivery Optimization Performance
Function: `optimizeDeliveryRoutes(input: DeliveryOptimizationInput): Promise<DeliveryRouteOutput>`
End-to-End Latency Targets:
| Input Size (Deliveries) | Vehicles | Target Latency | Max Latency | Baseline (Swift) |
|---|---|---|---|---|
| 10 deliveries | 1 | <500ms | <1s | 420ms |
| 50 deliveries | 2-3 | <3s | <5s | 2.6s |
| 100 deliveries | 3-5 | <8s | <12s | 7.1s |
| 500 deliveries | 5-10 | <30s | <45s | 28s |
Breakdown (50 deliveries, 2 vehicles):
Step 1: Bin Packing (50 deliveries) → 30ms
Step 2a: Vehicle 1 Route (25 stops) → 2s
Step 2b: Vehicle 2 Route (25 stops) → 2s (parallel)
Step 3: Calculate totals → 10ms
Step 4: Directions API (50 segments) → 1.5s (network)
---
Total: ~3.5s (within 5s target)---
6. Decision Logic Performance
6.1 Should Visit Today Performance
Function: `shouldVisitToday(location: Location, agent: Agent, today: Date): VisitDecision`
| Input Size | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| Single location | O(1) | <1ms | <5ms |
| 100 locations (batch) | O(n) | <50ms | <100ms |
| 500 locations (batch) | O(n) | <200ms | <300ms |
Rationale: Simple conditional logic with date arithmetic.
6.2 Get Candidate Locations Performance
Function: `getCandidateLocations(agentId: string, today: Date): Promise<Location[]>`
Database Query Performance:
| Database Size | Result Set Size | Target Latency | Max Latency |
|---|---|---|---|
| 100 locations | 10-20 candidates | <20ms | <50ms |
| 500 locations | 30-50 candidates | <50ms | <100ms |
| 2000 locations | 50-100 candidates | <150ms | <300ms |
Query Optimization:
- Index on `assigned_agent_id`
- Index on `stage` (exclude 'lost')
- Index on `next_visit_due_date`
- Composite index on `(assigned_agent_id, stage, next_visit_due_date)`
6.3 Priority Ranking Performance
Function: `rankLocationsByPriority(locations: Location[], today: Date): Location[]`
| Input Size | Time Complexity | Target Latency | Max Latency |
|---|---|---|---|
| 10 locations | O(n log n) | <5ms | <10ms |
| 50 locations | O(n log n) | <30ms | <50ms |
| 100 locations | O(n log n) | <80ms | <150ms |
Algorithm: Calculate VIP score for each location, then sort → O(n + n log n) = O(n log n)
---
7. Network Performance
7.1 Supabase Query Performance
| Query Type | Example | Target Latency | Max Latency | Timeout |
|---|---|---|---|---|
| Simple Select | Get user by ID | <100ms | <300ms | 5s |
| Filtered Select | Get locations by org | <200ms | <500ms | 10s |
| Join Query | Get locations with agents | <300ms | <800ms | 10s |
| Insert | Create location | <150ms | <400ms | 5s |
| Update | Update location | <150ms | <400ms | 5s |
| Delete | Delete location | <100ms | <300ms | 5s |
| Batch Insert | 100 locations | <1s | <3s | 30s |
Network Assumptions: 4G LTE (50ms base latency + query time)
7.2 Edge Function Performance
| Function | Purpose | Target Latency | Max Latency | Timeout |
|---|---|---|---|---|
| google-places-search | Search businesses | <1s | <3s | 10s |
| google-places-details | Get place details | <800ms | <2s | 10s |
| generate-invite-code | Create invite | <200ms | <500ms | 5s |
| enrich-location | Enrich with Places data | <2s | <5s | 15s |
7.3 Sync Performance
| Operation | Data Size | Target Latency | Max Latency | Timeout |
|---|---|---|---|---|
| Pull (Incremental) | 10 changed records | <500ms | <1s | 30s |
| Pull (Full) | 500 records | <3s | <8s | 60s |
| Push (Small) | 1-5 changes | <300ms | <800ms | 10s |
| Push (Medium) | 10-50 changes | <1s | <3s | 30s |
| Push (Large) | 50-200 changes | <5s | <10s | 60s |
Optimization Techniques:
1. Delta sync: Only fetch changes since last sync (timestamp-based)
2. Batch operations: Group inserts/updates into single transaction
3. Compression: Gzip request/response bodies
4. Pagination: Limit to 100 records per request
7.4 Google Maps Directions API Performance
| Request Type | Waypoints | Target Latency | Max Latency | Timeout |
|---|---|---|---|---|
| Single Route | 2 (origin + destination) | <500ms | <1s | 10s |
| Multi-Stop | 5 waypoints | <1s | <2s | 15s |
| Multi-Stop | 10 waypoints | <2s | <4s | 20s |
| Multi-Stop | 25 waypoints (max) | <4s | <8s | 30s |
Rate Limits: 50 requests per second (Directions API quota)
Optimization:
- Batch waypoints into single request (max 25 waypoints)
- Cache responses for 1 hour (routes don't change frequently)
- Use Haversine for estimation, Directions API for final route
---
8. UI Rendering Performance
8.1 List Rendering Performance
Component: `FlatList` (React Native virtualized list)
| List Size | Visible Items | Target FPS | Target Frame Time | Max Jank |
|---|---|---|---|---|
| 100 items | 10-15 | 60 FPS | <16ms | <3 dropped frames |
| 500 items | 10-15 | 60 FPS | <16ms | <5 dropped frames |
| 2000 items | 10-15 | 60 FPS | <16ms | <10 dropped frames |
Optimization Techniques:
1. Virtualization: Only render visible items + small buffer
2. Memoization: React.memo() on list item components
3. Key extraction: Use stable keys (location.id, not index)
4. getItemLayout: Pre-calculate item heights for instant scrolling
5. removeClippedSubviews: Unmount off-screen items (Android)
Validation:
import { performance } from 'perf_hooks'
const measureScrollPerformance = () => {
const frames: number[] = []
let lastTime = performance.now()
const onScroll = () => {
const now = performance.now()
const frameTime = now - lastTime
frames.push(frameTime)
lastTime = now
}
// Measure for 3 seconds
setTimeout(() => {
const avgFrameTime = frames.reduce((a, b) => a + b, 0) / frames.length
const droppedFrames = frames.filter(t => t > 16).length
console.log(`Avg frame time: ${avgFrameTime}ms, Dropped: ${droppedFrames}`)
}, 3000)
}8.2 Map Rendering Performance
Component: `MapView` (react-native-maps)
| Marker Count | Clusters | Target FPS | Target Frame Time | Max Jank |
|---|---|---|---|---|
| 10 markers | 0 | 60 FPS | <16ms | 0 |
| 50 markers | 5-10 | 60 FPS | <16ms | <3 |
| 100 markers | 10-20 | 60 FPS | <16ms | <5 |
| 500 markers | 20-50 | 60 FPS | <16ms | <10 |
Optimization Techniques:
1. Clustering: Use react-native-maps-super-cluster for 100+ markers
2. Marker reuse: Reuse marker components, don't recreate on pan/zoom
3. Custom markers: Use native images, not complex React components
4. Lazy loading: Load markers only in visible region
5. Debounced region change: Wait for map to settle before updating markers
Pan/Zoom Performance:
- Pan gesture: 60 FPS (no lag)
- Pinch zoom: 60 FPS (smooth scaling)
- Marker tap: <100ms response time
8.3 Form Input Performance
Component: `TextInput` (React Native)
| Metric | Target | Validation |
|---|---|---|
| Keystroke latency | <16ms | No visible lag |
| Debounced validation | 300ms | Validation triggers after typing stops |
| Auto-save | 1s after last edit | Optimistic UI + background save |
Optimization:
- Controlled inputs with `useState` (not Redux for every keystroke)
- Debounced validation (not on every keystroke)
- Optimistic UI (show save success immediately, sync in background)
8.4 Image Loading Performance
Component: `FastImage` (react-native-fast-image)
| Image Size | Source | Target Load Time | Max Load Time |
|---|---|---|---|
| Thumbnail (100x100) | Local cache | <50ms | <100ms |
| Thumbnail (100x100) | Network | <500ms | <1s |
| Full image (800x600) | Local cache | <100ms | <200ms |
| Full image (800x600) | Network | <2s | <5s |
Optimization:
1. Progressive loading: Show thumbnail → full image
2. Lazy loading: Load images only when scrolled into view
3. Image caching: react-native-fast-image with disk cache
4. Image resizing: Serve appropriate sizes from backend (don't download 4K for 100px thumbnail)
---
9. Memory Performance
9.1 Memory Usage Targets
| App State | Target Memory | Max Memory | Baseline (Swift) |
|---|---|---|---|
| Idle (Dashboard) | <80 MB | <120 MB | 72 MB |
| Locations List (500) | <100 MB | <150 MB | 95 MB |
| Map (100 markers) | <120 MB | <180 MB | 110 MB |
| Route Optimization | <150 MB | <200 MB | 138 MB |
| Background | <50 MB | <80 MB | 45 MB |
Validation: Use Xcode Instruments (Allocations, Leaks)
9.2 Memory Leak Detection
Zero tolerance for memory leaks.
Common leak sources in React Native:
1. Event listeners not cleaned up
2. Timers (setTimeout, setInterval) not cleared
3. Subscriptions (WatermelonDB, Supabase) not unsubscribed
4. Navigation listeners not removed
Validation:
useEffect(() => {
const subscription = database.collections
.get<Location>('locations')
.query()
.observe()
.subscribe(locations => {
setLocations(locations)
})
return () => {
subscription.unsubscribe() // CRITICAL: Cleanup
}
}, [])9.3 Image Memory Management
Targets:
| Scenario | Target | Max | Leak Check |
|---|---|---|---|
| 100 thumbnails loaded | <30 MB | <50 MB | No growth after unmount |
| 10 full images loaded | <20 MB | <40 MB | No growth after unmount |
Optimization:
- FastImage with cache eviction policy (max 100 images, LRU)
- Clear cache on low memory warning
- Unmount images when scrolled far out of view
---
10. Battery Performance
10.1 Battery Drain Targets
| Scenario | Duration | Target Battery Drain | Max Drain | Baseline (Swift) |
|---|---|---|---|---|
| Idle (app in background) | 1 hour | <1 | ||
| Active use (browsing) | 1 hour | <8 | ||
| GPS tracking (route) | 1 hour | <15 | ||
| Sync (background) | 5 minutes | <0.5 |
Validation: Use Xcode Instruments (Energy Log)
10.2 Battery Optimization Techniques
1. Reduce GPS accuracy when idle: Switch to "significant location change" mode
2. Batch network requests: Sync every 15 minutes, not every minute
3. Throttle background refresh: iOS Background App Refresh (max 1x per hour)
4. Avoid polling: Use push notifications, not polling for updates
5. Efficient animations: Use Reanimated (runs on UI thread), not Animated API (JS thread)
---
11. Offline Performance
11.1 Offline Operation Latency
| Operation | Target Latency | Max Latency | Notes |
|---|---|---|---|
| Create location (offline) | <50ms | <100ms | Write to WatermelonDB only |
| Update location (offline) | <50ms | <100ms | Write to WatermelonDB only |
| Delete location (offline) | <30ms | <80ms | Soft delete (mark as deleted) |
| Query locations (offline) | <100ms | <200ms | Query WatermelonDB cache |
| Route optimization (offline) | Same as online | Same as online | No network dependency |
Rationale: Offline operations should feel instant since no network is involved.
11.2 Sync Performance When Coming Online
| Pending Changes | Target Latency | Max Latency | Notes |
|---|---|---|---|
| 1-5 changes | <1s | <3s | Push changes to Supabase |
| 10-50 changes | <3s | <8s | Batched push |
| 50-200 changes | <10s | <20s | Multiple batch requests |
| 200+ changes | <30s | <60s | Background sync with progress indicator |
User Experience:
- Show "Syncing..." indicator
- Allow continued use during sync (don't block UI)
- Show success toast when sync completes
- Show error toast if sync fails (with retry button)
---
12. Swift App Parity Benchmarks
12.1 Critical User Flows (End-to-End)
| Flow | Swift Baseline | Expo Target | Expo Max | Parity |
|---|---|---|---|---|
| Login → Dashboard | 2.8s | <3s | <4s | ✅ |
| Create new location | 1.2s | <1.5s | <2s | ✅ |
| Load location detail | 0.8s | <1s | <1.5s | ✅ |
| Optimize route (10 stops) | 0.9s | <1s | <1.5s | ✅ |
| Log visit check-in | 0.6s | <0.8s | <1s | ✅ |
| Sync offline changes (10) | 2.1s | <2.5s | <3s | ✅ |
Parity Definition: Expo app performance is ≤ 110
12.2 Rendering Benchmarks
| Screen | Swift FPS | Expo Target FPS | Notes |
|---|---|---|---|
| Locations List (500 items) | 60 FPS | 60 FPS | Virtualized list |
| Map (100 markers) | 60 FPS | 60 FPS | With clustering |
| Dashboard (stats cards) | 60 FPS | 60 FPS | Simple layout |
| Route Planner (dragging) | 60 FPS | 55+ FPS | Acceptable drop during drag |
12.3 Memory Benchmarks
| Screen | Swift Memory | Expo Target | Expo Max |
|---|---|---|---|
| Dashboard | 72 MB | <80 MB | <120 MB |
| Locations List | 95 MB | <100 MB | <150 MB |
| Map | 110 MB | <120 MB | <180 MB |
---
13. Performance Testing Strategy
13.1 Unit Performance Tests
Jest + Performance Timing:
describe('Algorithm Performance Tests', () => {
it('calculates VIP score in <1ms', () => {
const location = generateTestLocation()
const start = performance.now()
const score = calculateVIPScore(location, new Date())
const duration = performance.now() - start
expect(duration).toBeLessThan(1)
})
it('optimizes route (10 stops) in <1s', async () => {
const stops = generateTestStops(10)
const start = performance.now()
const route = await optimizeRoute({ stops, startLocation, mode: 'driving' })
const duration = performance.now() - start
expect(duration).toBeLessThan(1000)
})
})13.2 Integration Performance Tests
Detox + Performance Monitoring:
describe('User Flow Performance', () => {
it('loads dashboard in <3s', async () => {
const start = Date.now()
await element(by.id('login-button')).tap()
await waitFor(element(by.id('dashboard'))).toBeVisible().withTimeout(5000)
const duration = Date.now() - start
expect(duration).toBeLessThan(3000)
})
})13.3 Benchmarking Suite
Automated Performance Regression Detection:
1. Baseline Measurement:
- Run performance tests on Swift app (current production)
- Record all metrics as baseline JSON file
- Store in git repo (don't allow changes without justification)
2. Continuous Benchmarking:
- Run performance tests on every PR
- Compare to baseline
- Fail CI if performance degrades >10
3. Performance Dashboard:
- Track metrics over time (chart)
- Identify trends (gradual degradation)
- Set alerts for sudden drops
13.4 Real Device Testing
Test Matrix:
| Device | iOS Version | Test Scope | Frequency |
|---|---|---|---|
| iPhone 12 | iOS 17 | Full suite | Every PR |
| iPhone 14 Pro | iOS 18 | Full suite | Weekly |
| iPhone SE (2022) | iOS 17 | Critical flows only | Weekly |
| iPad Pro | iPadOS 17 | UI rendering only | Monthly |
Why iPhone 12? Baseline device (mid-range performance, not too new, not too old).
13.5 Performance Monitoring in Production
Metrics to Track:
1. App Launch Time: Track P50, P90, P99
2. Screen Load Time: Track per screen (Dashboard, Locations, Routes)
3. API Latency: Track Supabase query times
4. Crash Rate: Track by version, device, iOS version
5. Memory Warnings: Track low memory warnings
6. Battery Drain: Track via Xcode Organizer (aggregate data)
Tools:
- Sentry (errors, performance monitoring)
- Firebase Performance Monitoring (screen traces, network traces)
- Xcode Organizer (battery, crashes, hangs)
---
14. Performance Degradation Triggers
14.1 Red Flags (Immediate Action Required)
| Trigger | Threshold | Action |
|---|---|---|
| Crash rate | >1 | |
| Memory leak | Growing memory usage | Fix immediately |
| Battery drain | >20 | |
| ANR (App Not Responding) | >100ms main thread block | Move work to background thread |
| Network timeout | >10s for any request | Add retry logic, increase timeout |
14.2 Yellow Flags (Investigate Soon)
| Trigger | Threshold | Action |
|---|---|---|
| FPS drop | <55 FPS during scroll | Profile with React DevTools |
| Memory growth | >200 MB after 30 min use | Check for leaks |
| Slow screen load | >2s for any screen | Add loading skeleton |
| Slow API | >1s for simple queries | Add caching, optimize query |
---
15. Optimization Priorities
15.1 Optimization Tiers
Tier 1 (Must Optimize):
- App launch time (<3s)
- Screen navigation (<300ms)
- List scrolling (60 FPS)
- Route optimization (<2s for 20 stops)
Tier 2 (Should Optimize):
- Offline sync (<3s for 10 changes)
- Map rendering (60 FPS with 100 markers)
- Form validation (<300ms)
Tier 3 (Nice to Optimize):
- Image loading (<500ms for thumbnails)
- Search query (<300ms)
15.2 Premature Optimization Avoidance
Do NOT optimize until:
1. You have measured the baseline performance
2. You have identified the bottleneck (profiling)
3. The bottleneck exceeds the performance target
"Premature optimization is the root of all evil." - Donald Knuth
Optimize iteratively:
1. Build feature (correct behavior first)
2. Measure performance
3. Identify bottleneck (if any)
4. Optimize bottleneck
5. Re-measure (confirm improvement)
6. Repeat if still below target
---
16. Revision History
| Version | Date | Changes |
|---|---|---|
| 1.0.0-DRAFT | 2026-01-04 | Initial performance specifications |
---
17. Appendix: Performance Testing Checklist
17.1 Pre-Release Performance Validation
Before production release, all of the following must pass:
- [ ] App Launch: Cold start <3s (iPhone 12, iOS 17)
- [ ] Screen Navigation: All transitions <300ms
- [ ] List Scrolling: 60 FPS with 500 locations
- [ ] Map Rendering: 60 FPS with 100 markers
- [ ] Route Optimization: <1s for 10 stops, <2s for 20 stops
- [ ] Delivery Optimization: <5s for 50 deliveries
- [ ] Offline Operations: Create/update/delete <100ms
- [ ] Sync Performance: <3s for 10 changes
- [ ] Memory Usage: <150 MB during active use
- [ ] Battery Drain: <12
- [ ] No Memory Leaks: Memory stable after 30 min use
- [ ] No ANRs: No main thread blocks >100ms
- [ ] Crash Rate: <0.5
17.2 Performance Regression Checklist
Run this checklist on every PR:
- [ ] No new blocking operations on main thread
- [ ] No new network requests without timeout
- [ ] No new setInterval/setTimeout without cleanup
- [ ] No new subscriptions without unsubscribe
- [ ] No new images without lazy loading
- [ ] No new lists without virtualization
- [ ] No new animations on JS thread (use Reanimated)
---
This document defines the measurable performance requirements for the Expo migration. All algorithms and UI components must meet or exceed these targets to achieve parity with the Swift app.
Promotion Decision
Attach run IDs, datasets, metrics, and reproduction commands.
Source Anchor
Milk Men/Documentation/Implementation/04-Algorithms/Performance-Specs.md
Detected Structure
Method · Evaluation · Architecture