Building Offline-First Applications for the African Market
How we engineer resilient mobile applications that work seamlessly in low-connectivity environments across Lagos and beyond.
title: "Building Offline-First Applications for the African Market" date: "2026-01-15" description: "How we engineer resilient mobile applications that work seamlessly in low-connectivity environments across Lagos and beyond." author: "Astexlabs Engineering" tags: ["Engineering", "Mobile", "Offline-First", "Africa"] published: true
In Lagos, internet connectivity is not a guarantee—it's a luxury that comes and goes. At Astexlabs, we've learned that building for Africa means building for offline-first.
The Problem: Connectivity is Unpredictable
The average Nigerian smartphone user faces:
- Intermittent 4G/3G coverage that drops without warning
- Expensive data plans that force users to be selective about bandwidth usage
- Network congestion during peak hours (morning commute, lunch breaks)
- Power outages affecting cellular towers
Standard web and mobile apps fail catastrophically in this environment. Users lose their work, transactions hang indefinitely, and frustration mounts.
The Lagos Spec: Offline-First Architecture
At Astexlabs, we follow what we call "The Lagos Spec"—a set of engineering principles designed for hostile environments:
1. Local-First Data Storage
Never trust that the network will be available. All critical data must be stored locally first:
// Bad: Direct API call
async function saveTransaction(data: Transaction) {
return await api.post('/transactions', data);
}
// Good: Local-first with background sync
async function saveTransaction(data: Transaction) {
// Save locally first
await localDB.transactions.create(data);
// Queue for background sync
await syncQueue.add({
type: 'CREATE_TRANSACTION',
payload: data,
});
return data;
}2. Aggressive Background Sync
Use service workers (web) or background tasks (mobile) to sync when connectivity returns:
// React Native background sync example
BackgroundFetch.configure({
minimumFetchInterval: 15, // minutes
}, async (taskId) => {
const pendingItems = await syncQueue.getPending();
for (const item of pendingItems) {
try {
await syncToServer(item);
await syncQueue.markSynced(item.id);
} catch (error) {
// Retry with exponential backoff
await syncQueue.incrementRetry(item.id);
}
}
BackgroundFetch.finish(taskId);
});3. Circuit Breakers for External APIs
Nigerian banking APIs are notoriously unreliable. Implement circuit breakers to fail fast:
class CircuitBreaker {
private failures = 0;
private lastFailure: number | null = null;
private readonly threshold = 5;
private readonly timeout = 60000; // 1 minute
async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.isOpen()) {
throw new Error('Circuit breaker is open');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private isOpen(): boolean {
if (this.failures >= this.threshold) {
const now = Date.now();
if (this.lastFailure && now - this.lastFailure < this.timeout) {
return true;
}
// Reset after timeout
this.failures = 0;
}
return false;
}
}Real-World Results
Since adopting offline-first principles:
- 99.7% operation success rate even during network outages
- 80% reduction in user-reported "app freezing" issues
- 3x increase in user retention in low-connectivity areas
Tools We Use
- WatermelonDB (React Native) - Fast, reactive local database
- Workbox (Web) - Service worker toolkit for offline caching
- RxDB (Web) - Reactive database with conflict resolution
- React Query with persistent storage for API caching
The Bigger Picture
Offline-first isn't just about handling poor connectivity—it's about respecting the user's context. When you build for Lagos, you build for resilience. And resilient systems work everywhere.
This is the Astexlabs way: Engineer for the worst, delight everywhere.
Want to learn more about our engineering practices? Check out our GitHub or reach out to our team.
