Skip to content
Mobile booking application

SAFIRI

Taking over an existing application, and making the core work.

SAFIRI is a ride-booking application designed for the Democratic Republic of Congo, where international products fit local practice poorly. I joined a project already under way: my role was to take it over, rework structural parts of it and get it running properly.

Category
Mobile booking application
Platforms
iOS · Android · Web
Context

Where it started

The market imposes constraints mainstream ride-hailing apps do not handle: payment is cash only, the currency is the Congolese franc, and the interface has to exist in French, English and Swahili. The application serves three different audiences — passengers, drivers, and the administration that runs the fleet and approves drivers.

The hard part is not taking an order, it is allocating a ride. It has to be offered to the right nearby driver, with a short window to accept or decline, passed on when declined, and protected against two drivers accepting the same ride — all with nobody arbitrating behind a phone.

The solution

What was built

Allocation runs server-side: the ride is offered to one driver at a time, nearest first, with a countdown and an automatic hand-off to the next candidate on refusal. The phone decides nothing — it observes and reports. Fares are steered remotely, which allows adjusting them without shipping a new version, an important detail in a high-inflation context.

Features

What the product does

  • Automatic ride allocation

    The ride is offered to the nearest available driver, with a short reply window and automatic hand-off to the next. No human dispatcher needed.

  • Destination by map or search

    The passenger drops a pin or types an address, which is resolved automatically into readable text.

  • Four vehicle categories

    Car, comfort, motorbike and tuktuk, each with an estimated fare. A category with no nearby driver is greyed out, so passengers only see what is actually available.

  • Passenger price offer

    The passenger can offer less than the estimate, matching common practice on this market.

  • Live ride tracking

    Passenger, driver and server follow the same document: the position shown to the passenger is the one the driver actually sends.

  • In-ride messaging

    A real, persistent conversation between passenger and driver.

  • Driver earnings

    Earnings aggregated by day, week, month and year, computed from rides actually completed.

  • Driver approval

    Drivers upload their documents; the administration approves or rejects them one by one, with a stated reason.

Engineering

The decisions that mattered

  1. Deciding on the server, not on the phone

    Ride allocation cannot live in the app: it has to work when the passenger’s phone is asleep, stay independent of which version is installed, and be impossible to bypass. The server is therefore the sole decision-maker, and the apps only observe and report. That is what makes automatic allocation trustworthy.

  2. A twenty-second window on a system that will not go below a minute

    The product needs a driver reply within twenty seconds, while the server-side scheduled trigger cannot run more often than once a minute. Rather than building expensive infrastructure for this one need, the driver app counts down locally and sends the refusal itself on expiry, while a once-a-minute pass catches the cases where the app was closed. A deliberate trade-off, with its documented cost: the wait can stretch when a driver does not respond at all.

  3. The ride carries its own state

    Instead of a separate orchestration service, the entire allocation state — the queue of candidate drivers, who currently holds the offer, when it expires — is stored on the ride itself. Everyone subscribes to that document and reacts to changes. The system becomes readable, and debugging means looking at one record rather than reconstructing an exchange between services.

  4. Paying for the map, not for the routing

    Map rendering uses Google Maps at the client’s request. Address search and route calculation, however, go through free open services. It is a deliberate cost/quality trade-off, made possible by keeping the location service cleanly abstracted: the expected rendering quality, without the associated billing.

  5. Fares adjustable without republishing

    Per-kilometre and per-minute rates, minimum fare and per-vehicle multipliers are stored remotely rather than in the code. In a high-inflation economy, changing a fare must not depend on an app update and a store review: it turns a multi-day operation into an immediate setting.

Architecture

How it fits together

  1. Application Flutter

    Three interfaces — passenger, driver, admin — from one codebase

  2. Cloud Functions

    Ride allocation decided server-side, never by the phone

  3. Cloud Firestore

    The ride carries its own state: everyone follows the same document

  4. Remote Config

    Fares are adjusted remotely, without shipping a new version

Tech stack

The technologies used

Frontend

  • Flutter
  • Dart
  • Provider
  • Google Maps

Backend

  • Cloud Functions
  • Node.js

Infrastructure

  • Firebase
  • Cloud Firestore
  • Firebase Storage

Services

  • Firebase Cloud Messaging
  • Remote Config
  • OpenStreetMap
  • OSRM
Result

What was delivered

Takeover of an existing application covering three journeys — passenger, driver and administration — from a single codebase. The core works end to end: booking, automatic server-side allocation, driver acceptance, live tracking and ride completion. My involvement covered the server-side allocation design, the mapping migration across six screens, the rebrand and the scope arbitration. The application is not published on the stores to date.

  • iOS
  • Android
  • Web

Need a similar product?

Let’s talk about what you want to build. I reply personally, usually within 24 hours.

Related services