Introduction
Building high-throughput, sub-hundred-millisecond ad serving engines inside large-scale consumer applications presents a fundamental architectural challenge: how do you decouple business domain complexity from real-time execution performance? At enterprise scale, an advertising marketplace is not a single monolith or a simple CRUD application. It is a dense, high-concurrency ecosystem spanning inventory supply, campaign demand, matching algorithms, auction dynamics, budget pacing, targeted indexing, and click-through rate (CTR) estimations.
When ad logic is tightly coupled to specific business lines—such as ride-sharing, food delivery, or grocery fulfillment—engineering teams inevitably build brittle, siloed serving stacks. Every new ad format, inventory source, or pricing model requires duplicate codebases, fragmented data pipelines, and high-risk deployments. Conversely, forcing every ad format into a rigid, one-size-fits-all monolith destroys latency budgets and introduces dangerous runtime coupling across domain boundaries.
I evaluated Uber's architectural transition toward a generalized ad marketplace platform to extract the structural lessons relevant to distributed systems practitioners. The core challenge Uber faced was migrating from domain-bound ad serving logic to a unified, multi-tenant execution engine without compromising p99 latency or business agility. In this article, I analyze the mechanisms required to decouple ad microservices into a generalized platform architecture, detailing the domain boundary definitions, the contract-driven state machines, the low-latency runtime execution loops, and the operational trade-offs inherent in abstracting high-concurrency ad auctions.
Core Architecture: Abstracting Demand, Supply, and Matching
To decouple ad serving from specific product lines, you must first construct a domain model that separates the participants in the marketplace from the mechanics of the auction. In a naïve implementation, an ad server directly queries campaign databases using specialized query parameters tailored to a single app interface (for example, searching specifically for nearby restaurants in a food delivery app). A generalized platform, however, abstracts all interactions into three foundational primitives: Supply, Demand, and Matching.
Supply represents any monetization opportunity within a digital surface. It is defined not by visual layout or product features, but by structural constraints, contextual metadata, and real-time environment tokens. A food delivery checkout screen, a rideshare post-trip receipt, and a search results page are all modeled as standardized Supply entities. Each Supply object exposes structural schemas declaring the maximum number of candidates it can accept, visual format constraints, contextual attributes (such as geo-location or time of day), and strict SLA expectations for execution response.
Demand represents the catalog of prospective ad items seeking allocation across available Supply. Demand is structured hierarchically to separate long-term configuration from real-time dynamic state. At the top level, advertisers maintain accounts and campaigns containing budgeting, pacing rules, target audience criteria, and attribution schedules. At the execution level, campaigns resolve down to individual candidate Ad Units containing creative payloads, bidding parameters, and target indexing predicates.
Matching is the stateless compute layer that evaluates Demand candidates against a given Supply context. In a generalized platform, the Matching engine does not contain hardcoded domain rules. Instead, it operates as an orchestration harness that invokes modular, configurable stage processors.
+-----------------------------------------------------------------------------------+
| SUPPLY CONTEXT |
| - Location / Time - Surface Identifier - Slot Constraints |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| MATCHING ENGINE |
| |
| +--------------------+ +---------------------+ +--------------------------+ |
| | 1. Candidate | | 2. Scoring & ML | | 3. Auction & Allocation | |
| | Retrieval | ->| Inference | ->| Mechanisms | |
| | (In-Memory Index) | | (CTR / CVR Models) | | (GSP / VCG / Pacing) | |
| +--------------------+ +---------------------+ +--------------------------+ |
+-----------------------------------------------------------------------------------+
^
|
+-----------------------------------------------------------------------------------+
| DEMAND CATALOG |
| - Budget & Pacing - Bidding Parameters - Targeting Predicates |
+-----------------------------------------------------------------------------------+
By establishing these strict boundaries, the core serving platform remains entirely agnostic to whether an ad context represents a promoted vehicle, a sponsored restaurant, or a retail product placement. The platform merely executes a deterministic state machine over standardized protocols.
The Low-Latency Execution Pipeline
In a enterprise ad marketplace, the runtime execution loop must complete within strict performance envelopes—typically under 50 to 80 milliseconds at p99—while processing tens of thousands of requests per second. Achieving this requires decomposing the matching state machine into a pipeline of specialized, non-blocking asynchronous stages. I structure this processing pipeline into four core sequential phases: Retrieval, Filtering, Scoring, and Auctioning.
1. Retrieval Phase
When a Supply request enters the platform, the candidate space must instantly shrink from millions of prospective ads to a workable subset of hundreds. The retrieval phase relies on in-memory inverted indexes and vector spaces. Domain-specific context is translated into key-value queries against partitioned memory tiers. For instance, location metadata, category tags, and user affinity vectors are evaluated to extract eligible candidate IDs. To prevent tail-latency spikes, candidate retrieval enforces strict per-node timeouts; if a retrieval partition fails to respond within its allocated sub-window (e.g., 5ms), the engine proceeds with partial candidate sets rather than stalling the pipeline.
2. Filtering Phase
Once candidates are retrieved, they pass through a series of deterministic filter chains to enforce hard compliance and business constraints. These filters run in parallel and evaluate:
- Targeting Predicates: Matching user demographics, device types, and real-time contextual signals against campaign target specifications.
- Budget and Pacing Constraints: Checking whether a candidate campaign has exhausted its daily or lifetime budget allocation, or if real-time pacing algorithms require throttling display frequency.
- Frequency Capping: Reading distributed caches (such as Redis or key-value memory grids) to ensure a user has not exceeded the maximum allowed impressions for a specific creative within a rolling time window.
- Exclusion Rules: Enforcing negative targeting, brand safety constraints, and competitive separation (e.g., preventing two competing brand ads from appearing on the same screen).
3. Scoring Phase
Filtered candidates proceed to the compute-intensive machine learning scoring phase. Here, the system estimates probabilities of user engagement, primarily Click-Through Rate (pCTR) and Conversion Rate (pCVR). In a decoupled architecture, scoring services are detached from the core pipeline through high-performance gRPC interfaces or inline, optimized ONNX/TensorFlow C++ bindings. The platform maps contextual features and candidate metadata into model inputs, executing batch inference calls to maximize hardware utilization (CPU vector extensions or GPU accelerators). To maintain latency targets, scoring pipelines use model truncation and feature pruning: complex deep-learning models are evaluated for top-tier candidates, while lightweight linear or tree-based models pre-score larger pools.
4. Auctioning and Pricing Phase
With estimated probabilities calculated, the system computes Expected Cost Per Mille (eCPM) for each candidate. The general formulation follows:
$$\text{eCPM} = \text{Bid}_{CPC} \times \text{pCTR} \times 1000$$
Candidates are ranked based on their eCPM values combined with dynamic quality modifiers. The auction stage then applies auction mechanics—such as Generalized Second Price (GSP) or Vickrey-Clarke-Groves (VCG)—to allocate winning slots and compute cleared pricing models. Finally, the response generator transforms the winning candidate structures into standardized JSON or Protobuf responses tailored to the requesting client.
Decoupling Strategy: Platform vs. Tenant Domain Isolation
Decoupling a monolithic or domain-bound ad system into a multi-tenant platform introduces severe engineering friction if responsibility boundaries are poorly defined. I recommend establishing a strict contract model that separates Platform Responsibilities (the core execution engine) from Tenant Domain Responsibilities (business-specific rules and configurations).
Platform teams must own the structural runtime infrastructure. Their sole responsibility is to guarantee high availability, strict latency SLAs, core auction primitives, feature store access patterns, and scalable deployment pipelines. Tenant teams (such as Eats, Delivery, Freight, or Mobility) must own their domain semantics. They define their target parameters, impression payloads, custom scoring features, and domain-specific filtering rules through declarative configurations and plugin interfaces.
To prevent tenant code from destabilizing the core platform, you must implement isolated extension points. This can be achieved through three primary patterns:
- Declarative Rule DSLs: Tenants specify targeting, eligibility, and filtering logic using structured YAML or JSON schemas evaluated by an isolated rule engine inside the platform. Tenants cannot write arbitrary execution code that loops infinitely or allocates unmanaged memory.
- Out-of-Process Micro-Services (Sidecars / RPCs): When tenants require custom scoring logic or proprietary business filters, the core platform invokes external tenant-managed microservices over gRPC using tight circuit breakers, strict hedge requests, and fallbacks. If a domain team's service slows down, the platform trips the breaker and continues the auction without that tenant's candidate pool.
- WebAssembly (Wasm) Plugin Runtimes: For zero-network-overhead custom logic, the platform can host sandboxed Wasm runtimes. Tenant teams compile custom filtering or feature-transformation logic into Wasm binaries. The platform executes these binaries in memory with strict fuel limits (instruction counters) and memory caps, ensuring a tenant script can never crash or hang the underlying host process.

The following code demonstrates how to structure a decoupled, asynchronous ad serving pipeline stage execution harness in Go. It enforces strict context timeouts, non-blocking parallel filter evaluations, and circuit-breaker patterns across multi-tenant execution modules.
package main
import (
