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
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.
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.
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.
The decisions that mattered
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.
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.
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.
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.
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.
How it fits together
Application Flutter
Three interfaces — passenger, driver, admin — from one codebase
Cloud Functions
Ride allocation decided server-side, never by the phone
Cloud Firestore
The ride carries its own state: everyone follows the same document
Remote Config
Fares are adjusted remotely, without shipping a new version
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
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.