{
"title": "Building ReserveFlow: AI-Powered Booking Infrastructure for Africa's Mobile-First Reality",
"content": "# Building ReserveFlow: AI-Powered Booking Infrastructure for Africa's Mobile-First Reality\n\nWhen we started building ReserveFlow at SharkFlow, we had one brutal constraint: **most of our users would be on 3G, 2G fallback networks, accessing via phones with 2GB RAM max**. This isn't a edge case in Kenya—it's the norm. M-Pesa processes $320B annually through USSD and SMS-based systems. If they could build payments for 60M+ users on fragile networks, we could build bookings.\n\nBut real estate and hotel booking? That's different. You're moving complex data: property listings, pricing calendars, availability states, user preferences, booking confirmations, and payment reconciliation—all needing instant updates across terrible networks.\n\nHere's how we engineered ReserveFlow to actually work in Nairobi, Kampala, and Lagos.\n\n## 1. API Design: Embrace Unreliability\n\nMost booking APIs assume good connectivity. Ours doesn't.\n\nWe built ReserveFlow on a **graph-based REST API with explicit offline-first principles**. Every endpoint returns a `_metadata` object:\n\n```
json\n{\n \"data\": {\n \"property_id\": \"prop_8472kd\",\n \"name\": \"Westlands Studio\",\n \"available_dates\": [\"2024-02-15\", \"2024-02-16\"],\n \"price_per_night\": 4500\n },\n \"_metadata\": {\n \"timestamp\": 1707900000,\n \"version\": 3,\n \"cache_ttl\": 3600,\n \"checksum\": \"a3f9d2e1\"\n }\n}\n
```\n\nWhy the `_metadata`? Because:\n- **Timestamps** let clients detect stale data (critical when re-syncing after network drops)\n- **Version numbers** prevent conflicts when offline changes sync back online\n- **Cache TTL** tells mobile apps how long to trust cached responses\n- **Checksums** let us verify data integrity after spotty downloads\n\nWe keep endpoints **intentionally chunky**—one request returns 50 properties with their full calendar availability for a month, not multiple requests that fail halfway. Bandwidth is expensive in Kenya; latency is brutal.\n\n## 2. Database: PostgreSQL with Smart Partitioning\n\nWe chose **PostgreSQL with time-based partitioning** over NoSQL, because booking state is transactional and consistency matters more than horizontal scaling at our scale.\n\nBooking availability is the hottest table. Here's the schema:\n\n```
sql\nCREATE TABLE property_availability (\n id BIGSERIAL,\n property_id UUID NOT NULL,\n date DATE NOT NULL,\n available_units INT NOT NULL,\n reserved_units INT NOT NULL,\n price_base INT NOT NULL, -- in KES cents\n surge_multiplier DECIMAL(3,2) DEFAULT 1.0,\n updated_at TIMESTAMP DEFAULT NOW(),\n PRIMARY KEY (property_id, date)\n) PARTITION BY RANGE (date);\n\nCREATE INDEX idx_availability_property_date \n ON property_availability(property_id, date DESC);\n
```\n\nWhy partitioning? Two reasons:\n- **Query performance**: Users always ask \"what's available in the next 90 days?\" Partitioning by date means we scan only relevant partitions.\n- **Archive pattern**: Old data (>1 year) gets archived to cold storage monthly without locking active tables.\n\nFor booking reservations themselves, we use **SERIALIZABLE isolation level** on critical transactions:\n\n```
sql\nBEGIN ISOLATION LEVEL SERIALIZABLE;\n\n-- Check availability\nSELECT available_units - reserved_units as open_slots\nFROM property_availability\nWHERE property_id = $1 AND date >= $2 AND date < $3\nFOR UPDATE; -- Lock these rows\n\n-- Create reservation\nINSERT INTO reservations (property_id, user_id, check_in, check_out, status)\nVALUES ($1, $2, $3, $4, 'pending_payment');\n\nUPDATE property_availability\nSET reserved_units = reserved_units + 1\nWHERE property_id = $1 AND date = $3;\n\nCOMMIT;\n
```\n\nThis prevents double-bookings even under concurrent requests. Essential when 10,000 users are browsing the same popular apartment.\n\n## 3. Scaling for African Mobile Networks\n\n**Here's the honest part**: We don't try to scale like Instagram. We scale *efficiently*.\n\n### 3.1 Request Coalescing\n\nWhen the network is slow, users click \"check availability\" multiple times hoping something happens. We detect this:\n\n```
python\nfrom functools import wraps\nimport hashlib\n\nrequest_cache = {} # Simple in-memory cache; Redis in production\n\ndef coalesce_requests(timeout=2): # 2 seconds\n def decorator(f):\n @wraps(f)\n def wrapper(*args, **kwargs):\n # Create fingerprint of request\n cache_key = hashlib.md5(\n str((f.__name__, args, tuple(sorted(kwargs.items()))))\n .encode()\n ).hexdigest()\n \n # Return pending promise if same request in flight\n if cache_key in request_cache:\n return request_cache[cache_key]\n \n # Otherwise, execute and cache\n promise = f(*args, **kwargs)\n request_cache[cache_key] = promise\n \n # Clear after timeout\n async def
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)