Intermittent Connectivity Patterns
Intermittent connectivity patterns are architectural approaches that enable applications to function correctly when network availability is unpredictable. These patterns treat connectivity as a resource that varies in availability and quality rather than a binary present-or-absent state. Field operations in humanitarian contexts routinely encounter connections that drop mid-transaction, bandwidth that fluctuates from adequate to unusable within minutes, and outages lasting hours or days. Systems built without these patterns fail in ways that corrupt data, frustrate users, and undermine programme delivery.
Problem context
Network connections in field environments exhibit characteristics that break assumptions embedded in conventional application design. A connection that reports as “available” may deliver 50 kilobits per second with 40% packet loss, making it functionally useless for synchronisation while still allowing small requests to succeed unpredictably. Satellite links impose latency of 600-800 milliseconds per round trip, causing timeout failures in applications designed for terrestrial networks. Mobile networks in congested areas drop connections when tower capacity is exceeded, often during working hours when usage peaks. Power instability causes networking equipment to restart, creating brief outages every few hours that interrupt any long-running operation.
The fundamental problem is the mismatch between application expectations and network reality. Conventional web applications assume that a request sent will receive a response within seconds, that a sequence of requests will complete atomically, and that failure is exceptional rather than routine. These assumptions produce cascading failures in field environments: a form submission that times out leaves the user uncertain whether data was saved; a synchronisation interrupted at 80% completion may restart from zero; an application that requires connectivity to display cached data becomes unusable during outages.
Forces driving pattern selection:
Connection quality varies across multiple dimensions simultaneously. Availability determines whether any connection exists. Bandwidth determines throughput for bulk transfers. Latency determines responsiveness for interactive operations. Stability determines whether connections persist long enough to complete operations. Predictability determines whether patterns can be detected and exploited. A pattern suitable for low-bandwidth stable connections differs from one suitable for high-bandwidth unstable connections.
Data characteristics constrain which patterns apply. Small, independent records synchronise differently than large, interrelated datasets. Append-only data like survey submissions presents different challenges than mutable data like case records. Data with external dependencies like beneficiary lookups requires different handling than self-contained records.
User workflow shapes acceptable behaviour. Data collection staff entering records expect immediate local saves with background sync. Programme managers reviewing dashboards expect current data even if slightly stale. Finance staff submitting payments require confirmation of successful transmission.
When these patterns do not apply:
Systems that genuinely require real-time connectivity, such as live video conferencing or real-time collaborative editing, cannot be made to work through these patterns. The patterns enable delayed consistency and eventual synchronisation, not instantaneous communication. Applications where data conflicts cannot be resolved automatically, such as financial ledgers requiring strict ordering, need different approaches that may involve manual reconciliation or restricted concurrent access.
Solution
Five core patterns address intermittent connectivity. Each solves a specific aspect of the problem, and robust field applications typically combine several patterns.
Store-and-forward
Store-and-forward decouples data creation from data transmission by persisting records locally before attempting network delivery. The user completes their action against local storage, receives immediate confirmation, and the system handles transmission as a separate background concern. This pattern eliminates the dependency between user action and network availability.
+-----------------------------------------------------------------------+| USER DEVICE |+-----------------------------------------------------------------------+| || +-----------------+ +-----------------+ +-----------------+ || | | | | | | || | Application +---->+ Local Store +---->+ Outbound | || | (user action) | | (persistent) | | Queue | || | | | | | | || +-----------------+ +-----------------+ +--------+--------+ || | || (when connected) | |+-----------------------------------------------------------------------+ | v+-----------------------------------------------------------------------+| NETWORK |+-----------------------------------------------------------------------+ | v+-----------------------------------------------------------------------+| SERVER |+-----------------------------------------------------------------------+| || +--------+--------+ +-----------------+ +-----------------+ || | | | | | | || | Receive +---->+ Validate +---->+ Acknowledge | || | Endpoint | | and Store | | Receipt | || | | | | | | || +-----------------+ +-----------------+ +--------+--------+ || | |+-----------------------------------------------------------------------+ | v (acknowledgement flows back)Figure 1: Store-and-forward architecture separating user action from network transmission
The local store must survive application restarts and device reboots. Browser applications use IndexedDB; native applications use SQLite or platform-specific databases. The store records not just the data but metadata needed for transmission: creation timestamp, retry count, priority, and any context required to construct the server request.
The outbound queue processor monitors network availability and attempts transmission when connectivity exists. Successful transmission removes records from the queue and updates local state to reflect server acknowledgement. Failed transmission increments retry counters and schedules the next attempt with exponential backoff: first retry at 30 seconds, second at 2 minutes, third at 8 minutes, subsequent retries at 30-minute intervals up to a maximum of 24 hours.
Server acknowledgement must be idempotent. Network failures during the acknowledgement phase leave the client uncertain whether the server received the record. The client will retry, and the server must recognise duplicates. Unique record identifiers generated client-side, typically UUIDs, enable the server to detect and deduplicate repeated submissions.
Optimistic interface with background synchronisation
Optimistic interfaces present users with immediate feedback assuming operations will succeed, while background synchronisation handles the actual data transfer. The interface updates instantly when users take action; synchronisation status indicators show transmission progress separately.
This pattern differs from store-and-forward in its treatment of server responses. Store-and-forward considers local save as provisional until server acknowledgement. Optimistic interfaces treat local state as authoritative, with server synchronisation providing backup and cross-device consistency rather than validation.
The user experience changes substantially. Users see records immediately in lists and can continue working without waiting. A subtle indicator shows synchronisation status: a small icon distinguishing “synced”, “pending sync”, and “sync failed” states. Users learn to trust local operations while understanding that “pending” records exist only on their device until synchronisation completes.
Conflict detection becomes essential. When the same record is modified on multiple devices before synchronisation, the system must detect the conflict and apply a resolution strategy. Common strategies include last-write-wins based on timestamps, field-level merging for non-conflicting changes, and flagging conflicts for user resolution.
Connection-aware behaviour
Connection-aware behaviour adjusts application functionality based on current network conditions. Rather than attempting the same operations regardless of connectivity, the application detects connection state and quality, then adapts its behaviour accordingly.
+---------------------+ | | | APPLICATION | | | +----------+----------+ | v +----------+----------+ | | | CONNECTION | | MONITOR | | | +----------+----------+ | +-------------------------+-------------------------+ | | | v v v +----------+----------+ +----------+----------+ +----------+----------+ | | | | | | | OFFLINE | | DEGRADED | | ONLINE | | | | | | | | - Local data only | | - Essential sync | | - Full sync | | - Queue all writes | | - Compress assets | | - Fetch updates | | - Disable lookups | | - Defer media | | - Load media | | - Show cached UI | | - Reduce requests | | - Enable lookups | | | | | | | +---------------------+ +---------------------+ +---------------------+Figure 2: Connection state machine with three operational modes
The connection monitor assesses network state through multiple signals. The Network Information API in browsers provides connection type and estimated bandwidth. Active probes to known endpoints measure actual round-trip time and success rate. Historical data establishes baseline expectations for the current network.
State transitions trigger behaviour changes. Moving from online to degraded might disable automatic image loading and switch to text-only synchronisation. Moving from degraded to offline disables all network operations and displays cached data with clear indication of staleness.
Quality thresholds define state boundaries:
| State | Bandwidth | Latency | Success Rate |
|---|---|---|---|
| Online | Above 500 kbps | Below 500 ms | Above 95% |
| Degraded | 50-500 kbps | 500-2000 ms | 70-95% |
| Offline | Below 50 kbps | Above 2000 ms | Below 70% |
These thresholds require calibration for specific applications. Data collection applications with small payloads tolerate lower bandwidth than applications synchronising images or documents.
Graceful degradation
Graceful degradation maintains core functionality when network conditions prevent full operation. The application identifies which features require connectivity and provides meaningful alternatives when those features cannot function normally.
Feature decomposition categorises capabilities by connectivity requirement:
Core features that must work offline include viewing previously accessed records, creating new records with local storage, editing existing local records, and navigating between screens. These features use only local data and local processing.
Enhanced features that require connectivity include searching central databases, synchronising records with the server, downloading reference data updates, and viewing records from other devices. When unavailable, the application explains the limitation and offers alternatives.
Degradation must be visible but not obstructive. A search feature that requires server connectivity should show a clear message explaining why search is unavailable and suggest browsing cached records instead. Disabled features should appear greyed out with explanatory tooltips rather than disappearing entirely or producing cryptic errors.
Sync windows
Sync windows concentrate synchronisation activity into scheduled periods when connectivity is expected to be available and reliable. Rather than attempting continuous synchronisation, the application batches changes and transmits them during designated windows.
+-------------------------------------------------------------------+| 24-HOUR CYCLE |+-------------------------------------------------------------------+| || 00:00 06:00 12:00 18:00 24:00 || | | | | | || | OVERNIGHT | MORNING | MIDDAY | EVE | || | (generator off) | WINDOW | WINDOW | WIND | || | | | | | || +------------------------+----+-------+-----+------+---+----+ || | | | || v v v || +--------+ +--------+ +--------+ || | Full | | Delta | | Delta | || | Sync | | Sync | | Sync | || +--------+ +--------+ +--------+ || |+-------------------------------------------------------------------+
WINDOW CONFIGURATION: Morning (06:00-08:00): Full sync, priority on downloads Midday (12:00-13:00): Delta sync, priority on uploads Evening (17:00-19:00): Delta sync, balancedFigure 3: Sync window scheduling across a typical field office day
Window timing aligns with operational patterns. Field offices with generator-based power have predictable connectivity periods during generator hours. Offices with solar power may have best connectivity midday when batteries are fully charged. Offices sharing limited bandwidth work better when synchronisation is scheduled to avoid contention.
Within windows, prioritisation determines what synchronises first. New records awaiting initial upload typically take priority over updates to existing records. Critical data types like protection cases synchronise before routine administrative records. Small records synchronise before large attachments to maximise successful completions within limited windows.
Between windows, the application queues changes locally without attempting transmission. This conserves battery and bandwidth while ensuring changes are ready when the next window opens.
Implementation
Implementing these patterns requires decisions about storage, network handling, conflict resolution, and user interface treatment.
Local storage architecture
Local storage must persist across application restarts, survive device reboots, handle concurrent access, and support queries needed by the application. IndexedDB provides these capabilities in browsers with storage limits typically between 50MB and unlimited depending on browser and available space. Native applications use SQLite with explicit schema management.
The storage schema mirrors server-side structure with additions for synchronisation metadata:
// IndexedDB schema for offline-capable recordsconst schema = { records: { keyPath: 'localId', indexes: [ { name: 'serverId', keyPath: 'serverId', unique: true }, { name: 'syncStatus', keyPath: 'syncStatus' }, { name: 'modifiedAt', keyPath: 'modifiedAt' }, { name: 'type', keyPath: 'recordType' } ] }, syncQueue: { keyPath: 'queueId', indexes: [ { name: 'priority', keyPath: 'priority' }, { name: 'createdAt', keyPath: 'createdAt' }, { name: 'retryCount', keyPath: 'retryCount' } ] }, syncLog: { keyPath: 'logId', indexes: [ { name: 'timestamp', keyPath: 'timestamp' }, { name: 'status', keyPath: 'status' } ] }};Each record carries synchronisation state: localId for device-local identity, serverId for server-assigned identity after first sync, syncStatus indicating pending/synced/conflict states, localVersion incrementing on each local change, serverVersion tracking last known server version, and modifiedAt timestamp for conflict resolution.
Network request handling
Network requests must handle timeouts, retries, and partial failures gracefully. Timeout values appropriate for field conditions exceed typical defaults: 30 seconds for small requests, 120 seconds for larger synchronisations, 300 seconds for bulk downloads.
// Network request with field-appropriate settingsasync function fieldRequest(url, options = {}) { const controller = new AbortController(); const timeout = options.timeout || 30000;
const timeoutId = setTimeout(() => controller.abort(), timeout);
const config = { ...options, signal: controller.signal, headers: { ...options.headers, 'X-Client-Timestamp': Date.now().toString(), 'X-Retry-Count': (options.retryCount || 0).toString() } };
try { const response = await fetch(url, config); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId);
if (error.name === 'AbortError') { throw new NetworkTimeoutError(url, timeout); } throw new NetworkError(url, error.message); }}
// Retry wrapper with exponential backoffasync function withRetry(operation, maxRetries = 5) { let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await operation(attempt); } catch (error) { lastError = error;
if (!isRetryable(error)) throw error;
const backoff = Math.min(30000, 1000 * Math.pow(2, attempt)); await delay(backoff + Math.random() * 1000); } }
throw lastError;}Retryable errors include timeouts, network failures, and server errors (5xx status codes). Non-retryable errors include authentication failures (401), authorisation failures (403), and validation errors (400). Distinguishing these categories prevents wasting bandwidth on requests that will never succeed.
Conflict resolution
Conflicts arise when the same record is modified on multiple devices or when a record is modified locally while the server version has changed. Detection occurs during synchronisation when the client’s serverVersion does not match the current server version.
Resolution strategies depend on data characteristics:
Last-write-wins uses timestamps to select the most recent version. This works for data where recency is the primary concern and losing earlier changes is acceptable. Configuration requires synchronised clocks or server-assigned timestamps.
function resolveLastWriteWins(local, server) { return local.modifiedAt > server.modifiedAt ? local : server;}Field-level merge combines non-conflicting changes from both versions. If local changes modified fields A and B while server changes modified fields C and D, the merged result contains all four changes. True conflicts where both versions modified the same field still require a tiebreaker.
function resolveFieldMerge(local, server, base) { const merged = { ...base };
for (const field of Object.keys(local)) { const localChanged = local[field] !== base[field]; const serverChanged = server[field] !== base[field];
if (localChanged && !serverChanged) { merged[field] = local[field]; } else if (serverChanged && !localChanged) { merged[field] = server[field]; } else if (localChanged && serverChanged) { // Both changed: use timestamp tiebreaker merged[field] = local.modifiedAt > server.modifiedAt ? local[field] : server[field]; merged._conflicts = merged._conflicts || []; merged._conflicts.push(field); } }
return merged;}Manual resolution flags conflicts for user review. This suits data where automatic resolution might cause harm, such as financial records or protection cases. The application presents both versions and lets the user choose or combine them.
User interface considerations
Interface design communicates system state without requiring users to understand the underlying architecture.
Synchronisation status appears consistently across the application. A global indicator shows overall connection state and pending sync count. Record-level indicators show individual record status. Both use visual language that works across literacy levels: green checkmarks for synced, yellow/orange for pending, red for failed, with optional text labels.
+------------------------------------------------------------------+| BENEFICIARY LIST [PENDING: 3] [!] |+------------------------------------------------------------------+| || +------------------------------------------------------------+ || | [x] Ahmed Hassan Last visit: 2024-01-15 | || | Status: Active [✓] | || +------------------------------------------------------------+ || || +------------------------------------------------------------+ || | [x] Fatima Osman Last visit: 2024-01-14 | || | Status: Active [~] | || +------------------------------------------------------------+ || || +------------------------------------------------------------+ || | [x] Mohammed Ali Last visit: 2024-01-14 | || | Status: Pending verification [!] | || +------------------------------------------------------------+ || || Legend: [✓] Synced [~] Pending [!] Needs attention |+------------------------------------------------------------------+Offline mode is explicit, not inferred. When connectivity drops, the application displays a clear banner indicating offline operation. Attempting network-dependent actions shows specific explanations rather than generic errors. The application never leaves users guessing why something did not work.
Queued operations are visible on demand. A sync queue view shows pending uploads with their status, retry count, and age. Users can force retry, cancel, or delete queued items. This visibility builds trust and enables troubleshooting without technical support.
Consequences
These patterns provide meaningful benefits while imposing real costs.
Benefits
Users complete work regardless of connectivity. Data collection continues during outages, field visits to areas without coverage, and transit between connected locations. Programme delivery does not stop when networks fail.
Data loss from connectivity failures is eliminated. Local persistence ensures that completed work survives any network interruption. Acknowledgement-based synchronisation ensures that transmitted data is confirmed received.
User frustration decreases. Immediate local feedback replaces waiting for network responses. Clear status indicators replace uncertainty about whether actions succeeded. Predictable behaviour replaces seemingly random failures.
Bandwidth usage becomes more efficient. Batched synchronisation reduces connection overhead. Compression and delta sync reduce data transfer. Scheduled windows avoid contention on shared connections.
Costs
Development complexity increases substantially. Applications must handle multiple states (online, offline, degraded), multiple code paths (local operations, synchronisation, conflict resolution), and multiple failure modes. Initial development time increases by 40-60% compared to online-only applications.
Testing requirements expand. Beyond functional testing, applications require testing under simulated network conditions: complete outages, partial connectivity, high latency, intermittent failures. Test automation must simulate these conditions reproducibly.
Storage requirements grow. Local databases duplicate server data. Sync queues accumulate during outages. Conflict tracking requires storing multiple versions. Device storage limits may constrain how much data can be available offline.
Conflict resolution introduces edge cases. However carefully designed, some conflicts cannot be resolved automatically. Users may encounter situations requiring manual intervention. Support staff need training to assist with conflict resolution.
Data freshness guarantees weaken. Offline-first design means users may work with stale data. Reference data lookups return cached results that may be outdated. The application must communicate data age clearly and refresh strategically.
Variants
Pattern implementation varies by application type and operational context.
Mobile data collection
Data collection applications like KoBoToolbox, ODK Collect, and CommCare prioritise the store-and-forward pattern. Records are created locally, validated against local rules, and queued for upload. Submissions are append-only, eliminating conflict concerns. Reference data (choice lists, calculations) downloads during scheduled sync windows.
The minimal variant for severely constrained devices stores submissions as flat files, queues uploads in a simple list, and provides basic retry logic. This runs on low-memory devices and survives aggressive battery optimisation that terminates background processes.
The full variant adds encryption at rest, photo/attachment handling with resumable uploads, sophisticated queue prioritisation, and detailed sync analytics. This suits tablets with stable power and moderate connectivity.
Case management
Case management applications handle mutable records with complex relationships, requiring all five patterns. Optimistic interfaces enable responsive case updates. Conflict resolution handles simultaneous edits by multiple workers. Connection-aware behaviour adjusts between full and limited functionality.
Protection case management adds survivor safety considerations. Sync timing may be restricted to avoid creating network traffic patterns that reveal case activity. Local storage encryption is mandatory. Conflict resolution must never discard survivor-reported information.
Dashboards and reporting
Read-heavy applications displaying aggregated data use connection-aware behaviour primarily. Online mode fetches current data from server APIs. Degraded mode shows cached data with staleness indicators. Offline mode displays last-cached state with clear age indication.
Cache invalidation strategy determines data freshness. Time-based invalidation refreshes data older than a threshold (1 hour, 1 day). Event-based invalidation refreshes on user action or push notification. Hybrid approaches combine both.
Real-time collaboration fallback
Applications with real-time features (collaborative editing, live chat) cannot provide full functionality offline but can degrade gracefully. Offline mode queues contributions for later transmission. Reconnection reconciles divergent states. The user experience shifts from real-time to asynchronous.
This variant requires careful expectation management. Users must understand that “real-time” features become “eventual” features during offline periods. Visual indicators must clearly distinguish real-time and queued modes.
Anti-patterns
Common implementation mistakes produce systems that appear to handle intermittent connectivity but fail under real conditions.
Optimistic assumptions without persistence
Applications that update the interface optimistically but rely on immediate network success lose data when connections fail. The user sees confirmation, the request fails, and no local record exists. This anti-pattern is worse than no offline support because it creates false confidence.
The failure mode appears as “disappearing records” where users complete work that vanishes after reconnection. Users lose trust in the application and may resort to external tracking (paper backup, photographs of screens) that defeats the purpose of digital data collection.
Infinite retry without backoff
Aggressive retry logic that repeats failed requests immediately and indefinitely consumes battery, wastes bandwidth, and may trigger server-side rate limiting. On metered connections, this creates unexpected costs. On congested networks, it worsens congestion.
The visible symptom is rapid battery drain with high network activity indicators despite no apparent progress. Servers may log excessive requests from specific clients and impose blocks.
Blocking operations on network state
Applications that disable all functionality when offline, rather than enabling appropriate offline features, render themselves useless during outages. Users cannot view cached records, cannot prepare new records, cannot accomplish any work.
This anti-pattern often stems from session management that invalidates local state when connectivity drops or authentication tokens expire. The fix involves separating local state validity from server session validity.
Silent conflict resolution
Automatic conflict resolution without user visibility or logging makes debugging impossible and may discard important data. When users report missing information, there is no way to determine whether data was never entered, lost in transmission, or overwritten by conflict resolution.
Resolution logging must record: the conflict detected, both versions involved, the resolution strategy applied, and the resulting merged or selected version. Significant conflicts (entire record replacement, protection case changes) should surface to users for confirmation.
Unbounded queue growth
Sync queues that grow without limit during extended offline periods eventually exhaust device storage. Applications crash or become unresponsive. When connectivity returns, the massive pending queue may take hours to process or overwhelm server capacity.
Bounded queues implement policies for queue management: maximum age after which records are archived locally, maximum count with oldest records archived when exceeded, maximum total size with large attachments deferred. Users receive warnings as limits approach.
Connectivity detection via polling
Applications that check connectivity by periodically requesting a known resource generate constant network traffic, drain battery, and may receive misleading results. A successful ping to a fast server does not guarantee that the application’s server is reachable or that bandwidth suffices for synchronisation.
Modern browsers provide passive connectivity detection via the Network Information API and online/offline events. Active checks should verify actual application functionality (test request to application server) rather than generic internet connectivity.
Testing strategies
Validating offline behaviour requires simulating network conditions that cannot be reproduced by simply disconnecting.
Network condition simulation tools include Chrome DevTools network throttling (preset profiles and custom conditions), Charles Proxy for intercepting and manipulating traffic, and platform-specific network link conditioners. Automated tests use these tools programmatically to exercise specific scenarios.
Test scenarios should cover:
| Scenario | Conditions | Expected behaviour |
|---|---|---|
| Clean offline | No connectivity | Local operations succeed, sync queued |
| Degraded upload | 10 kbps upload, normal download | Large uploads fail gracefully, small sync succeeds |
| High latency | 800ms RTT | Timeouts adjusted, user not blocked |
| Intermittent | Random 50% packet loss | Retry logic handles, eventual success |
| Mid-operation failure | Disconnect during sync | Partial sync recoverable, no data loss |
| Extended offline | 72 hours without connectivity | Queue management, storage limits |
| Reconnection storm | Many devices reconnect simultaneously | Server handles load, clients backoff |
Each scenario requires verification of data integrity, user feedback appropriateness, and resource consumption (battery, bandwidth, storage).
Monitoring connectivity quality
Production monitoring reveals how applications actually behave in the field.
Client-side telemetry records connection state transitions, sync attempt outcomes, queue depth over time, and time-to-sync for individual records. Aggregate analysis identifies patterns: which field locations have poorest connectivity, which times of day see most failures, which operations fail most often.
Server-side logging correlates with client reports. Time gaps between record creation (client timestamp) and server receipt indicate sync delays. Duplicate submission rates indicate retry behaviour. Geographic patterns emerge from IP geolocation or explicit location data.
Dashboards for IT staff surface: current connectivity status by location, sync backlog by location, error rates by operation type, and trend analysis over time. Alerts trigger when metrics exceed thresholds: sync backlog above 1000 records, average sync delay above 24 hours, error rate above 10%.
See also
- Offline Data Architecture provides architectural patterns for offline-capable data systems
- Offline System Configuration covers configuring specific platforms for offline operation
- Data Synchronisation Setup details synchronisation configuration procedures
- Sync Conflict Resolution addresses resolving data conflicts
- Low-Bandwidth Optimisation covers bandwidth reduction techniques
- Mobile Network Utilisation explains mobile connectivity characteristics
- Satellite Connectivity addresses satellite link behaviour