Disclaimer. This article is based entirely on Uber's public communications — their engineering blog, conference talks, open-source repositories, and external case studies. It is not an official Uber architecture document. We model what we know publicly to illustrate how a four-thousand-service stack can be made legible with the C4 model. Where details are inferred rather than stated by Uber, we say so.
Anatomy of a Ride: Modeling Uber in C4 with Archyl
A rider taps Request UberX at 7:23 pm on a rainy Friday in Manhattan. Eight seconds later, a driver 0.4 miles away has accepted the trip, an ETA has been computed and rendered, the fare is locked in, payment is pre-authorized, and a low-latency real-time channel is open between rider and driver. By the time they look up from the phone, the car is already moving toward them.
That single tap, repeated more than 30 million times a day across 600+ cities, traverses dozens of Uber systems and three external networks before frame zero ever lands on the driver's screen.
In 2024, Uber's stack ran roughly 4,000 microservices, served at peaks of well over a million requests per second to its mobile clients, scheduled compute on its own cluster manager, persisted state in its own MySQL-derived storage layer, indexed the planet on its own hexagonal grid, orchestrated millions of trip workflows on its own state-machine engine, and trained ETA models on its own ML platform.
How do you understand a stack with that many moving parts? Like Stripe and Netflix, you don't — not all at once. That's exactly the problem the C4 model was invented to solve.
In this post we follow one user action — a single ride request from tap to driver-accepted — and watch it traverse Uber's architecture across the four C4 levels. We won't cover every product; we'll trace one ride, write the ADRs that explain the choices we hit along the way, and end with a map of the teams who own each box. Along the way, we'll tour every feature Archyl offers to make this kind of model — and the policies around it — actually maintainable.
Level 1 — System Context: three platforms, one Marketplace

At System Context, Uber is not "a ride-hailing app". It's three product platforms sitting on top of one shared Marketplace and a stack of cross-cutting Foundation systems:
- Mobility — UberX, Uber Black, Uber Pool, Uber Reserve, Comfort, SUV, Premier, taxi partnerships
- Delivery — Uber Eats (food), Uber Direct (third-party delivery-as-a-service), Postmates, alcohol, grocery
- Freight — long-haul trucking, Uber Freight Loadbuilder, broker tools
Underneath them, the Marketplace platform is the actual brain — the matching engine, the dispatch optimizer, the dynamic-pricing system, the supply/demand forecasters. Marketplace is what makes a ride request a ride.
Around all of this sit the Foundation platforms: Maps (routing, ETA, distance/duration matrices, traffic), Payments, Identity & Risk, Communications (push, SMS, in-app messaging), Notifications, the ML platform (Michelangelo), the Workflow engine (Cadence/Temporal), the Storage platforms (Schemaless, Docstore, Cassandra, RocksDB-based stores), the Streaming platforms (Kafka, uReplicator, Flink), the Observability stack (M3 metrics, Jaeger tracing, ELK logs), and the Compute platform (historically Mesos + Aurora → Peloton → Kubernetes-based today).
Around them, the external actors: riders, drivers, eaters, couriers, merchants, shippers and carriers, map providers (their own + third-party for fallback), payment networks and acquiring banks, identity verification providers, telecom carriers for SMS and voice, cloud providers (Uber runs hybrid: own data centers + AWS/GCP for specific workloads), and city-level regulators.
Three product platforms. One Marketplace. Ten Foundation platforms. Everything else is detail.
This is the gift of Level 1: at System Context, you don't need to know that Mobility is two hundred microservices. You need to know it exists, that it talks to Marketplace, that Marketplace talks to Maps, and that Cadence orchestrates the long-running trip workflow underneath. The diagram is a conversation starter, not an inventory.
Archyl feature in play. A System Context diagram in Archyl is a single C4 Level 1 view with auto-layout, click-through navigation into containers, and overlays that let you mute/highlight subsets (e.g. "show only Foundation platforms"). External actors are first-class C4 elements with their own type so they render distinctly.
ADR-001 · H3 hexagonal grid for geospatial indexing
Status · Accepted (2018, open-sourced; still active in 2026)
Context · Marketplace's matching engine needs to answer "which drivers are near this rider?" in milliseconds, at city-wide concurrency, while supporting analytics like surge zones, ETAs, and supply forecasting. The classic options were rectangular tilings (Z-order, geohash, S2 cells from Google) but rectangles have a fundamental flaw for this domain: each rectangle has more than one neighbor distance — corners are farther than edges, which produces uneven distance approximations and asymmetric "what's nearby" queries.
Decision · Tile the planet in hexagons instead. Build a hierarchical hexagonal grid (H3) with sixteen resolutions from continent-scale down to ~1 m². Every hexagon has six equidistant neighbors, making nearest-neighbor and ring queries symmetric and fast. Open-source the library so partners and Uber engineers share the same grid.
Consequences · H3 became Uber's spatial primitive across Marketplace, Maps, ETA, surge, and analytics. The same H3Index is used in dispatch hot paths and in offline forecasting jobs. It's also one of Uber's most successful open-source projects — used by Foursquare, DoorDash, AT&T, and countless geo-startups. The few cases where hexagons can't tile cleanly (the icosahedron's 12 pentagonal anchors) are documented and avoided in production code.
In Archyl, that's how an ADR earns its place: it explains why the boundary looks the way it does. Click any container that touches geospatial state and the H3 ADR is one click away.
Archyl feature in play. ADRs in Archyl are first-class records linked to specific C4 elements. They show up as cards on the relevant boxes, they're filterable by status (proposed, accepted, deprecated, superseded), and they ship to git as YAML so they live alongside the code. When someone proposes replacing H3 in 2030, the existing ADR shows up automatically as related context.
Level 2 — Container: zoom into Marketplace's dispatch path

The rider's tap lands at the API edge. Let's open the box.
The dispatch path traverses something like the following containers:
- Edge gateway — historically TChannel + Thrift IDL, today a gRPC + HTTP/2 edge for mobile clients. Does auth, rate limiting, request shaping, and routing.
- Trip orchestrator — runs as a long-lived Cadence/Temporal workflow. The trip is a workflow instance with deterministic state transitions: requested → matched → arriving → on-trip → completed. Idempotent retries and timer-driven escalations are built-in primitives, not bespoke code.
- Matching engine (DISCO) — the actual marketplace optimizer. Given a rider request and the live driver supply within a few H3 ring-distances, it solves a constrained assignment problem on every tick.
- Dynamic pricing service — combines real-time supply/demand signals to compute surge multipliers per H3 cell. Outputs a fare quote that's locked at request time.
- ETA service — feeds Maps + ML models for route, traffic, and arrival prediction. Uber's ETA models moved to deep learning around 2018 and have been refined every year since.
- Maps platform — Uber's in-house routing engine, distance-matrix service, and traffic ingestion pipeline. Falls back to external map providers for select markets.
- Driver state service — tracks every driver's current state (offline, online, on-trip), location, and acceptance behavior. Reads/writes hot-path location data via custom geo-stores.
- Schemaless / Docstore — Uber's MySQL-backed sharded storage. Schemaless is the older one; Docstore is the newer, multi-region, transactional successor. Trip state, payments, user profiles, and most line-of-business data live here.
- Kafka cluster — every state transition emits an event. Marketplace subscribes for analytics; downstream services subscribe for fanout (notifications, fraud, accounting).
- Real-time channel — once matched, a low-latency bidirectional channel between rider and driver apps for location updates and chat. Backed by long-poll/WebSocket gateways and Kafka under the hood.
The technology stack at this level: Go for most new high-throughput services, Java in older marketplace code, Python in ML pipelines and ops scripts, Node.js at some edge layers, gRPC as the modern RPC protocol (TChannel/Thrift was the predecessor), Cassandra and Redis for hot-path latency, MySQL under Schemaless/Docstore, Hadoop/HDFS/Hive/Presto for the data warehouse, Spark/Flink for batch and streaming compute.
A typical ride request touches Edge gateway → Trip orchestrator (Cadence) → Matching engine (DISCO with H3 ring queries against Driver state) → Dynamic pricing → ETA → Notifications fanout (push to driver) → driver-accept callback → real-time channel established. All of that, with retries, instrumented end-to-end via Jaeger and metered via M3.
Archyl feature in play. Container-level diagrams show every container, its type (api / service / database / message_queue / cache / worker / gateway / library / infrastructure), its technologies (drawn from a per-organization technology catalog), and its relationships with labels. API contracts can be attached to any container and rendered inline — Uber would link the gRPC
.protofor Trip orchestration directly to the Trip orchestrator container.
ADR-002 · Cadence (Temporal) — build a workflow engine, don't pile up stateful microservices
Status · Accepted (~2017, open-sourced as Cadence; spun out as Temporal)
Context · By 2017, Uber had hundreds of microservices implementing long-running, stateful business processes — trips, orders, signup flows, driver onboarding, fraud reviews. Each one had grown its own ad-hoc state machine with timers, retries, idempotency, and recovery code. The result: every team paid the distributed-systems tax, and outages frequently came from subtle bugs in retry/timeout logic.
Decision · Don't ask every team to invent a state machine. Build a generic workflow engine with deterministic replay, durable timers, automatic retries, signal handling, and a programming model where workflow code reads like sequential business logic. Open-source it as Cadence. Migrate trips, signup, fraud reviews, and money-movement workflows to it over several years.
Consequences · Cadence (and its Temporal fork, now used inside and outside Uber) is now the substrate for every long-running stateful flow at Uber. The "trip is a workflow" abstraction collapses thousands of lines of bespoke retry code into a handful of well-typed activities. The engine also became one of the most-adopted open-source workflow projects in the industry. The path-dependent lesson: when ten teams are independently re-implementing the same primitive, build the primitive.
Archyl feature in play. Decisions like Cadence ripple across the model. In Archyl, an ADR can link to multiple C4 elements simultaneously — one decision, many affected boxes. Searching "workflow" across the model surfaces every container annotated as a Cadence consumer.
Level 3 — Component: inside the matching engine

Of all the components in Uber's stack, the matching engine — internally DISCO — is the most well-documented in conference talks and engineering posts.
A single ride request, once it reaches the matching engine, traverses these components:
- Request normalizer — turns the rider's coordinates into an H3 index at multiple resolutions (typically res 9 for hot-path, res 6 for ring expansion).
- Supply scanner — queries the live driver-state index for all eligible drivers within a starting H3 ring (~500 m). Filters by vehicle type, driver acceptance rate, and recent decline behavior.
- Ring expander — if no eligible drivers exist in the inner ring, expand outward in concentric H3 rings until a candidate set forms or a max-distance bound is hit. Uber has published several iterations of this expansion strategy, including ML-driven expansion that predicts likely driver paths.
- Candidate ranker — scores each candidate on ETA-to-pickup, marketplace efficiency (do we want to keep this driver in this neighborhood?), and historical acceptance probability for this rider/driver pair.
- Assignment solver — formulates the matching problem as a constrained optimization across the local supply pool. The solver runs continuously, batching nearby requests rather than committing to greedy first-best-match.
- Notification dispatcher — sends the matched candidate a push notification with a short acceptance window. Records acceptance/decline back into driver-state.
- Fallback path — on solver timeout or no eligible candidates, retry with relaxed constraints (broader vehicle types, longer ETA) or escalate to surge.
The pattern is brutally simple once you see it: every spatial query is an H3 ring; every business decision is a scored candidate; every match is the output of a global solver run, not a local greedy decision. The shape eliminates an entire class of "first-driver-grabs-fare" anti-patterns that plague naive dispatch systems.
This is what Component-level C4 looks like: not "here's some code", but "here's the chain of business-meaningful primitives, each owned, each replaceable, each measurable".
Archyl feature in play. Component diagrams show how a container is built. Each component has a type (controller / service / repository / handler / module / job / workflow / activity / entity), a file path, owners, and technologies. Components compose into a User Flow — Archyl's flow feature lets you author the rider's journey as an ordered sequence of component invocations and render it as a step-by-step diagram.
ADR-003 · Schemaless and Docstore — own the storage layer instead of buying
Status · Accepted (Schemaless: ~2014; Docstore: ~2020 onward)
Context · By 2014, Uber's ride volume had outgrown a single PostgreSQL instance, and the off-the-shelf NoSQL options of the time (Cassandra, Couchbase, MongoDB) had operational quirks Uber wasn't willing to accept for trip and payment data. Trip state needs strongly consistent multi-region writes, low p99 latency, and zero-downtime sharding. The industry's response in 2014 was "pick a NoSQL and live with the trade-offs".
Decision · Treat MySQL as the durable bedrock and build above it. Schemaless wraps sharded MySQL with a triggerless append-only log, automatic re-sharding, and a JSON-document API. Years later, Docstore layers a strongly consistent, multi-region transactional document store on the same MySQL substrate — and becomes the default for new product data.
Consequences · Uber has stayed off the boom-bust cycle of "we'll migrate to NewSQL"/"we'll migrate back to Postgres" that hit several similarly-sized companies. The path is incremental: new workloads get Docstore; mature workloads stay on Schemaless until migrated. Both are operated by a small platform team with deep MySQL expertise. The rumor that Uber went all-in on Cassandra? They use Cassandra, but it's never been the system of record for trips.
This ADR is a great example of path-dependent architecture: in 2014, the right answer was to extend MySQL, not migrate off it.
Archyl feature in play. Drift detection matters most here. When a new service starts writing to "Schemaless" but the C4 model still says "PostgreSQL", Archyl computes a drift score against the codebase and flags it weekly. ADRs prevent the next drift: a new team writing to a new datastore would have to file an ADR proposing the change, which the platform team can approve or reject.

Three decisions. Three cards in Archyl, each linked to the C4 elements they shape — H3 to every spatial container, Cadence to every long-running workflow, Schemaless/Docstore to the data tier. The diagram is the present tense; the ADRs are the why.
Ownership: turning a model into accountability

A C4 model is a static artifact until you map teams to it. Uber communicates publicly about its engineering structure: strong Foundation groups (Storage, Compute, Networking, Observability, Security, ML Platform, Maps), product orgs aligned to Mobility, Delivery, and Freight, and a central Marketplace organization that owns the cross-product economic engine.
Drop these onto the C4 model:
- Marketplace owns DISCO, the dynamic-pricing service, surge, the ETA platform, and the demand/supply forecasters
- Mobility Engineering owns the rider and driver apps, the trip orchestrator, the rating and tipping flows, the safety toolkit
- Delivery Engineering owns Eats's order orchestration, courier matching (which reuses DISCO primitives), merchant tools, and the menu/inventory platforms
- Freight Engineering owns the long-haul-specific workflows: load matching, broker tools, settlement
- Maps owns routing, ETA models, traffic ingestion, the H3 library
- ML Platform (Michelangelo) owns model training, feature stores, online serving, and the ML observability stack
- Storage Platform owns Schemaless, Docstore, Cassandra, the backup/restore tooling
- Compute Platform owns the Kubernetes-era cluster manager and the descendants of Aurora/Peloton
- Observability owns M3 (metrics), Jaeger (tracing), the log pipeline
- Security & Identity owns Risk, IAM, the secrets platform, and the abuse/fraud signals shared with Marketplace
- Cadence/Workflow Platform owns the durable workflow runtime used by every product
Once a system, container, or component has a team owner, drift detection becomes accountable. When a new service appears in commits and isn't on the diagram, a specific team gets asked. When a conformance rule is violated — "only Marketplace services may write to the surge cache" — there's a name in an inbox.
In Archyl, the Ownership Map is the moment a documentation tool becomes a governance tool.
Archyl feature in play. Every C4 element supports
owners.teamsandowners.users. The Ownership Map view rolls up coverage so you can see (and fix) the boxes nobody owns. Coverage gaps are unacceptable in any system this size — they're how on-call escalates to a Slack channel where nobody answers.
Drift, conformance, and the four-thousand-service problem
A model with four thousand microservices will drift. Hard. Mobility ships a feature; a new microservice appears; the Marketplace data contract evolves; a 2019 deprecated service finally gets retired. Multiply that by every team, every quarter.
Archyl computes a drift score weekly: the gap between the documented C4 model and what's currently in the codebase. The number is bounded between 0 and 100. A drift score of 12 might mean six new services not yet in the model, three relationships in the diagram pointing at deleted endpoints, and a handful of containers tagged with technologies that no longer match the actual stack.
Conformance rules add the policy layer. Examples a Marketplace org might write:
- Only Marketplace services can read from the surge cache
- Every container that handles PII must carry the
pii:truetag and reference an Identity ADR - All public APIs must have an attached OpenAPI or gRPC contract — and that contract must be the source of truth, not the implementation
- Every container needs an owner team
- Trip-state mutations must go through Cadence; direct database writes are forbidden
- New datastores require an ADR and a Storage Platform sign-off
Archyl evaluates these rules continuously. Violations get surfaced on the diagram, in the team's weekly digest, and as commit-time checks if you wire the GitHub Action.
Archyl features in play.
- Drift score for the gap between model and code, recomputed on every push
- Conformance rules authored as YAML, applied to all C4 elements
- Architecture Change Requests — pull-request-style review for proposed model changes, so the architecture follows the same rigor as the code
- Architecture Insights — AI-surfaced anomalies and recommendations from the drift + conformance signals
For a stack the size of Uber's, this isn't optional. It's the only way the model stays honest without a dedicated architecture-documentation team.
API contracts, events, and the marketplace's nervous system
The Marketplace at Uber is a graph of services exchanging events at high velocity. Trip-state transitions, driver-location updates, surge recomputations, fare quotes, payment authorizations — every change emits a Kafka event consumed by zero-to-many downstream services. Most service-to-service synchronous calls are gRPC.
In Archyl:
- Every container can have an attached API contract (HTTP/OpenAPI, gRPC, GraphQL, or AsyncAPI). The spec is rendered inline; consumers see exactly what they're calling.
- Every async channel can be modeled as an Event Channel with broker (Kafka), topic name, schema format (Avro, Protobuf, JSON Schema), and the schema body. Producers and consumers are linked to the channel.
- Breaking changes show up as a diff on the contract — and if you have conformance rules requiring a version bump, the change is blocked until the rule is satisfied.
For Uber, that's three thousand topics and tens of thousands of contracts becoming inspectable from the same place as the C4 model. No more "who consumes my events?" — the model knows.
DORA at the Marketplace scale
Once you have C4 elements with owners, you can connect them to delivery telemetry. Archyl's DORA module pulls deployment frequency, lead time for changes, change failure rate, and mean time to recovery from your CI/CD and incident systems — and rolls them up by C4 element and by team.
For Mobility's Trip Orchestrator, you'd see that team's deployment cadence and stability separately from Pricing's. For Marketplace overall, you'd see how the whole platform's MTTR is trending. When MTTR spikes, drill into the offending containers; when deploy frequency stalls, you can attribute it to a specific subtree.
Archyl feature in play. The DORA dashboard in Archyl renders the four metrics with team and element breakdowns and trend lines, and it ties incidents back to the architecture elements they affected. It's how "we have observability" becomes "we have engineering health".
And then there's the AI tier
A 4,000-service stack is the natural habitat for AI coding agents — Claude Code, Cursor, Windsurf, and the rest. Every Uber engineer has the same problem: "how does service X talk to service Y, and where does the surge multiplier actually get persisted?"
In Archyl, the model is exposed via an MCP server. Any AI agent on an engineer's laptop can ask:
- "List all services that depend on the H3 library"
- "Show me the API contract for
dispatch.MatchService" - "Which ADRs cover storage decisions?"
- "Generate a migration plan from Cadence to Temporal SDK v2 across owned services"
The agent gets the same architectural context an engineer does. Onboarding shrinks. Cross-team code reviews stop being "what does this even do?". The context that lived in heads now lives in a queryable model.
Archyl features in play. MCP server for AI agents, import/export in archyl YAML, Structurizr DSL, LikeC4, IcePanel JSON, and Backstage catalog format — so existing architecture data flows in without a rewrite. Project documentation, user flows, and architecture insights round out the surface.
The full feature surface for an organization at this scale
If you're an Uber-shaped engineering org evaluating Archyl, here's the surface, mapped to the parts of your day-to-day where it earns its keep:
- C4 model with all four levels — System Context, Container, Component, Code — with auto-layout, overlays, and click-through navigation. The thing every diagramming tool gets right; we get it right and keep going.
- AI architecture discovery — point Archyl at a repository and it discovers C4 elements automatically. Gets you from zero to first model in an hour, not a quarter.
- Architecture-as-Code —
archyl.yamlchecked into git, parsed and validated. CI/CD-ready via GitHub Action. Same rigor as code. - Multi-format import — Backstage catalog (JSON), Structurizr DSL, LikeC4, IcePanel JSON, plus Archyl's native YAML.
- ADRs linked to C4 elements with full lifecycle (proposed / accepted / deprecated / superseded).
- Project Documentation with markdown, attachments, linking to specific elements — your living architecture handbook.
- API contracts for HTTP/gRPC/GraphQL/AsyncAPI, rendered inline against the producing container.
- Event channels with broker, topic, schema, producers, and consumers — the async side of the architecture.
- Releases & environments — versioned deployments tied to the architecture, surfaced on the diagram.
- Ownership Map with team and user assignments at every level.
- Drift score between the model and the actual codebase, recomputed on every push.
- Conformance rules as policy on the model — author, evaluate, and enforce.
- Architecture Change Requests — pull-request-style review for proposed model changes.
- Architecture Insights — AI-surfaced anomalies, risks, and recommendations.
- DORA metrics rolled up by element and by team, with trend lines and incident attribution.
- Architecture Team Digest — a weekly per-team summary scoped to the team's owned perimeter.
- MCP integration — every AI coding agent on the team shares the same architectural context.
- GitHub PR reviews — Archyl's review bot comments on architecture-impacting PRs with drift, conformance, and ADR context.
- Sharing & embedding — public links, team-only links, embeddable iframes for internal wikis.
- Image/PDF export — PNG, SVG, and PDF for presentations, formal docs, and printed slide decks.
- Multi-language — every Archyl surface available in nine languages, including the docs and the agent prompts.
That's the full toolbox. For a stack like Uber's, you'd use roughly all of it. For a stack of fifty services, you'd use the half that matches your maturity — and grow into the rest.
You don't need four thousand services
You're not Uber. Most engineering organizations aren't.
But the lesson scales down. The discipline of separating Context from Container from Component, of writing the ADR that explains a path-dependent decision (we built H3, we built Cadence, we extended MySQL instead of replacing it), of attaching ownership to every box — that discipline is what keeps a stack of fifty services from feeling like four thousand.
C4 + ADRs + Ownership + Drift + Conformance + API Contracts + DORA + MCP — that's what Archyl gives you out of the box. The Uber example is just the largest plausible stress-test of the model in the marketplace-and-logistics domain.
Open up your own architecture. Sketch ten products (or three, if you have three). Pick the one with the most surprising past-tense decisions and zoom into its containers. Write three ADRs explaining what would baffle a new hire. Map a team to every container.
You'll be ahead of where most engineering organizations get to in a year.
Want to model your own architecture in C4? Start with Archyl. Read more on why ADRs and C4 work better together or how Architecture Change Requests bring pull-request rigor to your C4 model. The previous case studies modeled Stripe in C4 — Anatomy of a Charge and Netflix in C4 — Anatomy of a Play.