From a Stranded Evening to a Global Platform
The origin story is well known: in 2008, Travis Kalanick and Garrett Camp couldn’t hail a cab in Paris, and the idea for Uber was born. What started as a simple "push a button and get a ride" concept has grown into the world’s largest mobility and food delivery platform, operating in over 10,500 cities across 70-plus countries. Today, Uber connects millions of driver-partners and merchants with over 130 million customers, handling billions of database transactions and millions of concurrent users across dozens of apps and thousands of backend services.
Reaching that scale required a series of deliberate architectural pivots. The first version of Uber, launched in San Francisco in 2009, was built by contractors on the classic LAMP stack, with code written in Spanish. As demand grew, the earliest problems were concurrency-related: the system would occasionally dispatch two cars to one person or match a single driver to two different riders. The product’s popularity made it clear that the technical foundation needed to be rebuilt from scratch.
Addressing Real-Time Demands
Circa 2011
The push for global scale brought two immediate technical challenges: resolving concurrency issues and processing large volumes of real-time data. Uber needed to track driver locations continuously to match riders efficiently, while the product was still evolving and required rapid iteration.

Uber adopted Node.js for its real-time needs, becoming one of the first major production adopters of the runtime. Node.js was a natural fit for two reasons: it handles requests asynchronously through a non-blocking, single-threaded event loop, enabling fast data processing, and it runs on the V8 JavaScript engine, which offers strong performance and supports rapid development cycles.
A second service, built in Python, handled business logic functions such as authentication, promotions, and fare calculation. This yielded a two-service architecture: a Node.js-based "dispatch" service connected to MongoDB (later Redis), and a Python-based "API" service connected to PostgreSQL. To harden the dispatching flow, Uber added an intermediary layer called "ON" (Object Node) between dispatch and API to ensure continuity if the API service experienced disruptions.

This setup began to resemble a service-oriented architecture, which carries a useful organizational benefit: dedicated services allow engineering teams to be structured around specific domains, enabling faster team growth. But as features multiplied, the API service became monolithic, with conflicting changes slowing productivity and making deployments risky. The logical next step was to break the API apart.
The Microservices Transition
Circa 2013
Uber formally adopted a microservice architecture, with small services dedicated to well-encapsulated domain areas such as rider billing, driver payouts, fraud detection, and city management. Each service could use its preferred language and database—many backend services stayed in Python, while a growing number adopted Tornado for asynchronous response handling. By 2014, Uber ran roughly 100 services.

Microservices solved many problems but introduced significant operational complexity. Without careful tooling, such an architecture risks becoming a distributed monolith. Uber built a set of in-house solutions to keep the platform manageable:
- Standardized frameworks:
Clay, a Python wrapper onFlask, provided consistent monitoring, logging, HTTP request handling, and deployment patterns across services. - Service discovery and resilience:
TChannel, a bi-directional RPC protocol built overHyperbahn, enabled services to find and communicate with one another while offering fault tolerance, rate limiting, and circuit breaking. TChannel was built in-house primarily for better performance and forwarding with Uber’s Node and Python services. - Strong interfaces: Apache
Thriftdefined well-structured RPC contracts between services. - Configuration control:
Fliprfeature flags managed code changes and rollouts, preventing cross-service capability issues. - Observability: The
M3platform exposed service metrics to every engineer via Grafana dashboards, complemented byNagiosfor alerting at scale. - Distributed tracing: Initially
Merckx, which pulled instrumentation data from Kafka, Uber eventually evolved toJaegeras asynchronous patterns became more common across services.
Over time, the organization migrated to gRPC and Protobuf for interfaces, and many services now run on Golang and Java.
Rebuilding the Trip Data Layer
Circa 2014
Even as Uber created numerous backend services, trip data remained in a single PostgreSQL database—a bottleneck that was becoming existential.

The database was struggling on performance, scalability, and availability fronts; there was a hard limit on how much memory and CPU could be added. Engineers faced diminishing productivity when adding rows, tables, or indices for new features. The urgency was acute: by early 2014, Uber was only six months from Halloween, one of the highest-traffic nights of the year.
Analysis of the data mix showed that trip data dominated storage and was growing fastest. Trip records are essential for services like Uber Pool, rider and driver support, fraud prevention, and developing features such as suggested pick-ups.

To solve this, Uber built Schemaless, an append-only, sparse three-dimensional persistent hash map similar to Google’s Bigtable, built on top of MySQL. Its row partitioning across multiple shards enabled natural horizontal scaling while supporting rapid development. The migration of all trip-related services succeeded in time, averting the Halloween traffic threat.

For broader data needs—including the database supporting marketplace matching and dispatching—Uber adopted Cassandra as a scalable replacement.
Decoupling Dispatch and Building a Mobile Gateway
Circa 2014
The original dispatch service juggled matching logic alongside its role as a proxy, routing traffic to internal microservices. Beyond that, it was built on assumptions that no longer held: simple one-rider-to-one-driver trips, and a focus exclusively on moving people rather than goods. Its state, sharded by city, buckled under the growth of key metros. Uber split dispatch into two efforts: extracting the mobile gateway and rewriting the core matching engine.

The Real-Time API Gateway
To handle real-time mobile traffic, Uber created a new gateway layer called RTAPI ("Real-Time API"), still built on Node.js. Although a single repository, it was organized into specialized deployment groups to serve different business lines.

The gateway offered a flexible space for prototyping new code with access to hundreds of company services. The first generation of Uber Eats, for instance, was fully built inside the gateway before its components were migrated into purpose-built services as the product matured.
Rewriting the Matching Engine
The original dispatch logic was rewired into a family of services. Matching evolved into a form of the traveling salesman problem, considering not just currently available drivers but also those who might become free shortly. That required a geospatial index built on Google's S2 library, which segmented cities into cells used as the sharding key. (Uber has since updated to and open-sourced H3.)

Because these stateful services were still running on Node.js, Uber developed Ringpop, a gossip-protocol library, to share geospatial and supply positioning data efficiently across nodes.
More on the history of the dispatch stack and this talk on the Real-time Market Platform.
Scaling Mobile Development with RIBs
Circa 2016 to present

With global expansion came a long feature list: localized payments, distinct vehicle products, airport details, and new bets like Uber Eats. The mobile app repositories started resembling a backend monolith, with too many engineers and features fighting over a single releasable codebase.
That drove the development of the RIB architecture, beginning with the main Uber app rewrite.

RIBs mirror the separation of concerns found in microservices. Each RIB owns a single responsibility, letting teams split dependencies into core and optional code. Stricter review of core code increases confidence in primary flows and simplifies feature flagging. Like microservices, individual RIBs belong to distinct teams, letting mobile codebases grow with hundreds of engineers. Today the Driver app, Uber Eats, and Uber Freight have adopted or are migrating to RIBs.
The Launch and Rise of Uber Eats
Circa 2017
Uber experimented with various "Uber for X" on-demand concepts from 2014, and the data kept pointing to food. Uber Eats launched in Toronto in late 2015, then followed a growth trajectory similar to UberX.
The product reused the Uber stack wherever possible, spinning up new services and APIs only for food-specific needs like carts, menus, search, and browsing.

City operations teams often resorted to hacks that didn't scale while tuning marketplaces, until permanent tech was built.

Early Uber Eats was a simple three-way marketplace: one consumer, one restaurant, one driver. The current platform (130+ million users) handles complex orders, guest checkout, group ordering, multi-restaurant baskets, and fleets owned by the merchants themselves.
For a deep dive into the origins, Uber Eats founder Jason Droege recounts the early days on "Building Uber Eats".
Project Ark: Standardizing a Decentralized Culture
Circa 2018
Uber's decentralization extended from city operations to engineering. A "Let Builders Build" value produced rapid development—and thousands of microservices and repositories, with many overlapping solutions to the same problems.
"You've got five or six systems that do incentives that are 75 percent similar to one another" - Former Uber CTO Thuan Pham
Developer productivity suffered, prompting leadership to form Project Ark. Its targets spanned engineer productivity, alignment across teams, duplication, and knowledge access.
Project Ark made Java and Go the official backend languages for type safety and performance, phasing out Python and JavaScript. It also consolidated code repositories (down from 12,000 to the main languages), defined clear architectural layers for client, presentation, product, and business logic, grouped related services into "domains," and standardized on libraries for tracing, logging, and networking resilience.
A More Modern Edge
Circa 2020
By 2019, Uber ran many business lines—Eats, Freight, ATG, Elevate—with teams needing vertical independence for speed. The aging RTAPI gateway ran on the deprecated Node.js/JavaScript stack and had accumulated messy ad hoc code mixing view generation and business logic.
The new Edge Gateway standardized on four layers:
- Edge Layer: API lifecycle management with no extra logic.
- Presentation Layer: microservices generating views and aggregating from downstream services.
- Product Layer: reusable functional APIs describing each product, composable into new experiences.
- Domain Layer: leaf microservices providing a single refined function for a product team.

This structure let Uber keep product velocity high while aligning its thousands of engineers around consistent architecture.
Rebuilding the Fulfillment Stack
Circa 2021
By 2021, the fulfillment stack—core to matching—strained under new demands: reservation flows with upfront driver confirmation, batched trips, airport virtual queues, the three-sided Eats marketplace, and Uber Direct package delivery.

Uber chose to rewrite the Fulfillment Platform from the ground up, adopting a NewSQL architecture with Google Cloud Spanner for transactional consistency, horizontal scaling, and low operational cost.
As lead Uber engineer Ankit Srivastava describes it: "as we scale and expand our global footprint, Spanner's scalability & low operational cost is invaluable. Prior to integrating Spanner, our data management framework demanded a lot of oversight and operational effort, escalating both complexity and expenditure."

Beyond the Core Stack
Scaling Uber has never been a single-story effort. Across engineering and operations, numerous large initiatives have shaped how the company grows. Critical systems such as the API gateway, fulfillment stack, money stack, real-time data intelligence platform, and geospatial data platform—which produced the open-sourced H3—each have their own histories of evolution. Machine learning at scale has been addressed through Michelangelo.
Several supporting layers have been added over time: multiple tiers of Redis caches, and frameworks like Cadence for writing fault-tolerant, long-running workflows. On the data side, infrastructure investments have enabled long-term growth, including leveraging Presto, scaling Spark, and building Apache Hudi to run business-critical pipelines with low latency.
Server performance remains a focal point, with work spanning optimized hardware, memory tuning, system-level adjustments, and adoption of newer runtimes.
Moving to the Cloud
Uber has historically run on-premises across multiple data centers, but this operational model brought challenges as the company scaled globally. The server fleet grew to over 250,000 machines, making tooling and management a constant struggle. Geographic expansion demanded more data centers and availability zones, while fleet sizing required continuous, manual adjustment—a costly exercise in a purely on-prem environment.
In response, Uber spent recent years making over 4,000 of its stateless microservices portable. This work was paired with Project Crane, an initiative to ensure stack consistency across cloud and on-prem environments. The portability effort positions Uber to shift a significant portion of its online and offline fleet to the cloud in the coming years, as detailed in lead engineer Kurtis Nusbaum’s talk on Crane.



