The system outlived every assumption in it
Dinamo ran in production from a first commit in December 2020 to a last one in January 2025. I wasn’t there for all of it: I joined the codebase in August 2023 and my work runs to the final commit, concentrated on the parts that decide whether a driver actually receives a trip.
Two apps, driver and rider. Both are still on Google Play: the rider app (v10.3.7, versionCode 116) at 50K+ downloads and 3.8/5 across 516+ reviews, the driver app (v8.2.2, versionCode 83) at 5K+ downloads and 4.5/5 across 190+ reviews. Real usage that outlasted the team that built the reliability layer.
One source tree, eight builds
Two brands, “go” and “wasalni”, were built from one codebase via product flavours rather than forked. A second flavour dimension, dev and prod, crosses it. Worth being exact about what is shared: the code is shared across brands, the backend is not.
| Flavour | Packages · driver / rider | Backend |
|---|---|---|
| go | com.starbugs.go_drivercom.starbugs.go_user | wessalegy.com |
| wasalni | com.starbugs.wasalni_drivercom.starbugs.wasalni_rider | dashboard.dinamo-app.com |
x dev / prod environment flavours = eight builds, one source tree
That split has a cost nobody sees until it bites: every bug fix ships to both brands, but every bug report comes from one. A crash only wasalni users hit is still a crash in go’s binary, waiting for the conditions that trigger it.
The part that actually mattered
A ride-hailing driver app has one job that cannot fail: notice that a trip exists. On Android that is genuinely hard, because the OS is actively trying to stop you. Battery optimisation, Doze, and OEM process-killers all exist to end exactly the kind of long-running background work a driver app is made of.
The September 2024 rebuild of the driver app’s trip fetching (ba47be4, with 7ddefbb and 19d84ba the same day) answers that with separate pieces that never call each other:
- A foreground service drives the 30-second cadence and returns
START_STICKY, so Android tries to restart it if it is killed. - Each tick’s real work runs as a WorkManager job holding a wake lock and a Wi-Fi lock (ten-minute ceiling), so one fetch completes or retries whether or not the service lived long enough to see it through.
- The location service is its own foreground service, restarted with
START_REDELIVER_INTENTso it comes back with its last instructions. - A boot receiver brings location back after a reboot.
One trip, four ticks
The fetch loop is easier to read running than described. The worker offers the first trip in the response. A trip shown and not accepted goes into an ignored-orders map with a timestamp, and is not shown again for 30 seconds, because offering the same trip on every tick is how a driver learns to ignore notifications.
Fetch near 30.0444, 31.2357
Trip 4127 returned first
Offer shown
Fetch again
Trip 4127 still open
Suppressed: in ignored map, 30 s not up
Fetch again
Trip 4127 still open, cooldown expired, entry cleaned
Offer shown again
Fetch again
Trip 4127 gone, taken by another driver
Nothing to show
The second row is the one users never see, and the one they would complain about most if it were missing.
What this is not
START_STICKY is a request to the operating system, not a guarantee. Aggressive OEM process-killers, common on the devices this shipped to, can still prevent a restart, and there is no battery-optimisation whitelist prompt in this codebase. The architecture is honest about its intent; it isn’t bulletproof, and no amount of foreground-service discipline makes it so.
What the version history actually shows
It is tempting to describe the app’s growth as accumulated battery engineering. It isn’t. Reading the commit history back, the recurring theme is state after death: crashes on relaunch, stale caches producing “not available in your area”, notification handling that dropped work when the process was recycled.
That’s a less glamorous story than battery tuning and a more useful one. The bugs that survive in production are the ones about what your app remembers when the OS decides it no longer exists.
What replaced it
The Kotlin apps ended with January 2025. The platform did not: another team rebuilt it in Flutter on a new Laravel backend, and the rebuild answers this page’s central problem differently.
In the new backend, TripCreated runs CaptainFilterService::findCaptainsForTrip() and broadcasts to one captain.{id}.trips private channel per eligible captain. InviteExpired withdraws the offer on the same channel. That is the ignored-orders map, moved to the server where it belongs.
The same problem, one layer up
Push does not remove the problem, it relocates it. A phone that is not polling still has to keep a socket alive through Doze, and the new driver app ships flutter_background_service to do it. The 2024 question, “is the driver still reachable?”, became a connection-health question instead of a scheduling one. Better question, same operating system.