T
MobilePlatformReal-timeMicroservicesAWS

Transportation

Ride-Hailing App Development Case Study — Competing with Uber in Central Asia

A mid-sized mobility company in Central Asia needed more than a booking app. They needed a ride-hailing platform that could stand in the same competitive arena as Yandex Go, inDrive, and Uber, while modernizing dispatch, keeping long-time drivers productive, and supporting aggressive multi-city growth.

Taxi platform preview showing a city route map with pickup point, route status colors, and nearby driver distance.
Client Voice

In our market, riders compare every tap against Yandex Go and Uber, drivers compare earnings and flexibility against inDrive, and no one gives you credit for being local. We needed a platform that could compete at that standard without tearing apart the operation we had already built.

Client-side COO

Mobility Platform, Central Asia

Expert View

The hard part was not building a taxi app. The hard part was designing a system where dispatchers, drivers, passengers, and partners could all operate in one consistent real-time model without introducing race conditions.

Vadym Kozak

CTO

Real-time platforms

Request

Build a ride-hailing platform from zero — 4 mobile apps, 2 web dashboards, a distributed backend — that could handle real-time GPS telemetry and peak ride loads while competing with Yandex Go, inDrive, and Uber in Central Asia. Most agencies would have promised it and shipped a monolith.

What We Built

A fully event-driven microservices architecture on AWS: polyglot persistence, MQTT-based real-time telemetry, and 6 client applications across iOS, Android, and web. Each component built to scale independently.

Outcome

Expanded to multiple cities within 12 months. Driver count tripled. The platform handles high-frequency GPS and peak loads without downtime — exactly what was promised.

Key Numbers

6

client products launched

Passenger and driver apps for iOS and Android, plus partner and admin web dashboards.

< 300 ms

target booking confirmation latency

For the initial booking response, including fare estimation and order creation.

10,000+

simultaneous live driver location streams

Designed to absorb continuous telemetry during peak demand windows.

3x

driver fleet growth after launch

Digital onboarding and smoother order flow removed manual expansion bottlenecks.

< 2 sec

driver acceptance to passenger notification

From the moment a driver accepted a ride to the moment the passenger saw confirmation.

12 months

to multi-city expansion

The platform architecture supported expansion across major Central Asian cities without rewriting the core system.

Project Facts

Business model

Ride-hailing marketplace with dispatch support, partner fleets, direct customer bookings, and multi-tariff pricing logic.

Geography

Central Asian transportation market with airport traffic, commuter corridors, dense city centers, and price-sensitive competition.

Legacy constraint

Phone dispatch had to keep running during rollout, alongside new app-based bookings.

Core users

Passengers, drivers, dispatchers, partner fleet managers, operations staff, and finance teams.

Delivery scope

Architecture, backend services, mobile apps, web dashboards, real-time messaging, and payments.

Infrastructure

AWS-based distributed backend with PostgreSQL, Redis, EMQX, and event-driven service boundaries.

Request

The client came to us with a business problem disguised as a product brief. On paper, the ask was straightforward: launch passenger and driver apps, give dispatchers a better control panel, and build a backend that could support serious multi-city growth across Central Asia.

In practice, the mandate was much harder. This was not a company trying to look modern for investors. It was a company trying to compete head-on with the products riders already benchmarked against every day: Yandex Go, inDrive, and Uber. Replacing the operating model in one shot would have created chaos. The platform had to digitize the business while preserving the dispatch workflows, driver habits, and local market knowledge that still made the company viable.

That changed how we approached the project. We were not building a clean-sheet startup app with no legacy burden. We were building a new operational core for a live transportation business.

Context

Competing in Central Asia creates pressure on three fronts at once.

  • Passengers expect instant booking, live ETAs, and transparent pricing.
  • Drivers expect a steady order flow, simple acceptance mechanics, and earnings visibility.
  • The business expects dispatch continuity, partner fleet control, and enough reliability to survive weather spikes, event traffic, and rush-hour concurrency.

The benchmark was not abstract. Riders were already used to the polish and speed of Yandex Go, the price sensitivity of inDrive, and the UX expectations set by Uber. That means a mid-sized platform cannot get away with being merely “good for a local product.” It has to feel credible next to category leaders while still outperforming them in local execution.

The existing business already had street knowledge, driver relationships, and dispatcher experience. What it lacked was a unified digital layer. Orders came from too many channels, status updates were fragmented, and scale depended too heavily on manual coordination.

That is usually the moment when companies overcorrect and try to automate everything at once. We deliberately did the opposite. We designed the system to let old and new workflows coexist, then used architecture to make that coexistence stable.

Strategy

We organized the solution around three principles.

First, the ride lifecycle had to become explicit. Booking, matching, acceptance, pickup, trip execution, and completion could not live as vague status updates spread across clients. They needed to become a strict shared state model understood by every app and service.

Second, real-time infrastructure had to be treated as a core product requirement, not a technical add-on. In transportation, latency is not a cosmetic issue. A late location update means a wrong ETA. A delayed offer means a missed driver. A weak matching flow becomes revenue loss almost immediately.

Third, rollout risk had to stay low. Dispatchers needed to keep taking calls. Drivers needed to stay on the road. Partner fleets needed a controlled onboarding path. That pushed us toward stateless services, modular clients, and clear boundaries between transactional data, volatile real-time state, and background processing.

Operational Insights

Before finalizing the architecture, we mapped the real failure points across the operating model, not just the product surface.

For dispatchers, the problem was fragmented visibility. They needed one live operational picture across app orders, phone bookings, active drivers, and in-progress rides.

For drivers, the biggest friction was response speed. If order offers arrived late, duplicated, or without enough trip context, driver trust in the system would collapse.

For passengers, the critical moment was not registration or profile setup. It was the first booking. The product had to feel instant at the exact moment a user decided to request a ride.

For the business, the hidden issue was scaling behavior. Manual coordination can work in one city and fail completely when a second city or a large partner fleet is added. In Central Asia, that challenge becomes more visible because every new city introduces a slightly different mix of airport demand, commuter traffic, local fleet dynamics, and price elasticity. The backend had to be designed for geographic expansion from day one, even if launch demand was smaller.

Solution

The final solution was a distributed transportation platform with six connected products and a backend designed around event-driven coordination.

On the client side, we delivered passenger and driver apps for iOS and Android, a partner dashboard for fleet operators, and an admin environment where dispatchers and operations teams could manage the live system.

On the backend, we split responsibilities across dedicated services for authentication, orders, notifications, payments, and operational support. Each service owned a clear part of the system instead of competing over the same state.

That separation mattered because almost every “simple” transportation action is actually a multi-step workflow. A booking request touches identity, maps, pricing, order creation, driver search, notification delivery, and live tracking. If those concerns are not separated cleanly, the platform becomes unpredictable under load.

Architecture

One of the first technical decisions was to treat driver telemetry as a first-class stream. A ride-hailing platform does not have one map. It has many maps updating at once: the passenger view, the dispatcher view, the matching engine, and the partner operations view. Standard request-response patterns are too expensive for that kind of constant movement.

We used EMQX and MQTT as the real-time backbone. Each driver device held a persistent connection and published location updates into dedicated topics. That let the platform distribute updates to the right subscribers without forcing clients to poll continuously.

Booking Logic

What happens before a passenger sees the ride confirmed

This is the real complexity of the product. The platform is not judged by how a ride ends, but by how quickly and reliably it can validate, route, lock, notify, and confirm the booking at the exact moment demand spikes.

Target Response

< 300 ms

from booking tap to initial confirmation

Step 1

Auth and trip validation

The request is authenticated, the pickup is normalized, and the trip data is validated before any expensive work begins.

Step 2

Route and fare estimation

Maps and routing services are queried to estimate price and ETA quickly enough for the booking screen to still feel immediate.

Step 3

Order creation and candidate search

The ride is persisted with a controlled initial state, then the system queries the nearest eligible drivers based on live availability and radius rules.

Step 4

Driver offer and passenger confirmation

The best candidate is reserved, the offer is dispatched through the real-time layer, and the passenger receives confirmation without waiting for the whole fleet search to complete.

Rush-Hour Safeguards

What keeps the flow from breaking under load

Driver locking

Redis locks prevent the same nearby driver from being targeted by overlapping booking waves during rush hour.

Timeout and re-match

If an offer is declined or ignored, the order re-enters matching automatically instead of falling into manual recovery.

Telemetry fan-out

MQTT distributes the same live location stream to matching, dispatch, and passenger tracking without turning the system into an HTTP polling storm.

The ride lifecycle itself was still enforced by the Orders-service as a strict state model, but the real value came from the safeguards around it. Redis locks prevented duplicate assignment attempts, timeout rules pushed unclaimed orders back into matching automatically, and the messaging layer kept status propagation fast enough for drivers, passengers, and dispatchers to trust what they were seeing.

Demand Intelligence Engine

One of the most valuable subsystems sat slightly below the booking flow: a dedicated demand worker that turned local ride activity into a live geographic pressure map of the city.

Most transportation platforms talk about dynamic pricing as if it were a single coefficient. In reality, that approach is too blunt. Demand is not flat, and it does not move evenly. It concentrates around stations, event venues, nightlife districts, rain pockets, airport waves, and commuter corridors. We needed the platform to understand that geography in real time rather than treating the whole city as one noisy average.

This worker used H3 hexagons as the unit of spatial intelligence. Every demand action landed in a specific cell, updated local fill state, recalculated multipliers for the affected tariff, and, if the cell was already saturated, pushed the remainder outward into neighboring rings. That gave the platform something much more useful than a surge toggle. It gave it a controlled model of how pressure spreads.

Demand Engine

A real-time geographic pressure layer for pricing and supply balance

We did not model demand as a number. We modeled it as pressure moving through the city.

Spatial memory

Demand was modeled as a geographic field, not as a city-wide counter, so the system could react to pressure exactly where it formed.

Recursive spillover

When a core hexagon hit capacity, the worker propagated the remainder into outer rings instead of dropping information or overstating one point on the map.

Controlled decay

TTL cleanup and multiplier recalculation let hotspots cool down predictably, which kept pricing responsive without turning the map into flickering noise.

The implementation detail that mattered most was stability. When pressure spilled into outer rings, inner cells could be temporarily frozen so the field did not oscillate every few seconds. State lived in Redis, writes were grouped through Lua scripts, and a cleanup worker continuously removed expired actions and recomputed fill values. In effect, this was not just caching. It was a real-time geographic pricing layer sitting underneath the product.

Loading diagram…

For riders, that meant fares and ETAs felt grounded in local conditions. For drivers, it meant supply signals were clearer. For the business, it meant demand hotspots could be represented, persisted, and decayed in a way that was operationally credible rather than mathematically naive.

The platform architecture below captures how these pieces fit together across mobile apps, dashboards, messaging, data, and infrastructure.

At the data layer, PostgreSQL remained the source of truth for transactional records, while Redis handled volatile state such as availability, session context, and matching locks. This split gave us transactional safety where the business needed integrity and memory-speed operations where the product needed responsiveness.

Delivery Decisions

Several delivery choices had an outsized impact on business stability.

We kept services stateless wherever possible so traffic could be redistributed through AWS load balancing during spikes. That protected the platform during rain events, evening peaks, and local demand bursts without forcing the team to provision every service for worst-case load all year round.

We also kept phone dispatch inside the same operating environment as app bookings. That may sound small, but it was critical. Many transportation businesses fail digital transformation because they create one system for “legacy staff” and another for “new users.” We intentionally built a unified operational view, so dispatchers could work across both channels instead of being bypassed by the product.

On the client side, the driver experience was designed for speed and low cognitive load. Offers had to be clear, answerable in one tap, and backed by reliable navigation and earnings history. In this market, driver retention is partly a UX problem, not only an incentive problem.

Tech Stack

Cloud & Infrastructure

AWSAWS ELB / ALBAWS S3

Mobile

SwiftKotlin

Web

Angular

Real-time & Messaging

EMQXMQTTWhatsAppSMS

Databases

PostgreSQL

Maps & Routing

Google Maps APIOSRM

Notifications

APNSFirebase FCM

Auth & Security

OAuth2JWTTLS/SSL

Results

  • Multi-city expansion within 12 months without replatforming, which confirmed that the original service boundaries were strong enough for Central Asian market rollout.
  • Driver fleet growth reached 3x after launch, supported by smoother onboarding and a more reliable order distribution flow.
  • Passenger booking felt materially faster, with booking confirmation designed to stay under the sub-300-millisecond target for the critical first response.
  • Dispatcher operations remained intact during digitization, which protected the business from the rollout shock that usually comes with platform replacement.
  • Race conditions in driver matching were removed from the normal operating path, reducing the risk of duplicate assignments and lost orders during peak periods.
  • The platform moved the client from reactive operations to scalable systems thinking, giving the business room to add pricing logic, analytics, and new service tiers rather than just keeping up with competitors.

Next Phase

Where the platform goes next

After the first expansion wave, the roadmap shifted from basic operational parity to market advantage: dynamic pricing by zone and demand band, deeper partner fleet analytics, loyalty mechanics for repeat riders, and more automation inside dispatcher workflows. The important point is that none of those features required a new platform. They were layered onto a backend that had already been designed for operational growth.

Your Turn

Need a product that has to perform under real market pressure?

Ride-Hailing App Development Case Study — Competing with Uber in Central Asia is one example of how we design and build serious transportation systems: real-time logic, operational tooling, and product decisions that hold up outside the pitch deck. If you are building something equally demanding, let's talk through the architecture before it becomes expensive to change.

Best fit

Platforms with real-time flows, multi-sided operations, pricing logic, dispatch, mapping, or heavy delivery constraints.

See more cases