Skill-Based Matchmaking Architecture: Ratings, Pools & Relaxation
Design SBMM with ratings, party/region pools, queue-age relaxation, latency constraints, backfill and server assignment. Compare Open Match and FlexMatch.
Skill-based matchmaking is a queue-management problem before it is a rating-algorithm problem. The hard part is deciding which constraints are non-negotiable, which can relax as a ticket ages, how parties remain atomic, where a playable server will come from, and how to measure the quality/wait-time tradeoff. Elo, Glicko, TrueSkill-style models, or another rating estimator only provide one input to that decision.
The Production Matchmaking Pipeline
| Stage | Responsibility | What should stay separate |
|---|---|---|
| 1. Rating / player features | Maintain skill estimate, uncertainty, role/preferences and other trusted attributes | Rating updates do not have to run inside the queue service |
| 2. Ticket ingest | Create one ticket per solo player or party, with region latency and mode constraints | Authentication and party membership should be resolved before matching |
| 3. Candidate pools | Filter tickets by hard compatibility: mode, build, crossplay, region availability, party policy | Do not turn every preference into a permanent partition |
| 4. Match generation | Group compatible tickets into candidate matches and balance teams | The algorithm should be testable independently from server allocation |
| 5. Relaxation | Widen soft constraints as the oldest ticket waits | Never relax rules that are true correctness/safety requirements |
| 6. Assignment | Choose or allocate the session/host and return a connection assignment | Matching players is not the same operation as hosting them |
| 7. Backfill / recovery | Fill eligible vacancies, handle declines/timeouts, return failed assignments to queue | Backfill needs its own policy; it is not just “run matchmaking again” |
Rating Is an Input, Not the Matchmaker
The rating model should answer something like “what do we currently believe about this player's/team's skill?” The matchmaker then combines that estimate with latency, party structure, roles, queue age and session constraints.
| Model family | Useful property | Watch for |
|---|---|---|
| Elo-style | Simple single-number rating; easy to explain and audit | No native uncertainty term; multiplayer/team updates need additional design |
| Glicko-style | Adds rating uncertainty/deviation, useful when activity varies | You still need a team/multi-player outcome model if the game is not 1v1 |
| TrueSkill / Weng-Lin / OpenSkill-style | Models a distribution rather than one number and can support multi-team outcomes | Choose/model/test for your actual match format; there is no universal best default |
Do not pick a rating library because a blog calls it “the 2026 default.” Replay historical matches offline, measure calibration and convergence, and decide how inactivity, smurfs/new accounts, parties and mode-specific skill should work for your game.
The Core Tradeoff: Match Quality vs Queue Age
A practical matcher normally starts strict and expands soft constraints with ticket age. For example:
0-10s same region, skill delta <= 75, exact party policy
10-25s same region, skill delta <= 150
25-45s common acceptable region, skill delta <= 250
45s+ game-specific fallback or ask player to widen search
The numbers above are examples, not recommendations. Derive them from your measured queue distribution. AWS FlexMatch formalizes this concept as expansions: rules can relax after configured wait times. It can base expansion timing on the newest or oldest ticket in a candidate match, which directly changes the quality-versus-wait bias.
Latency: Match on Common Playable Regions
Do not reduce networking to “players must be within X milliseconds of each other.” Each ticket should report latency to candidate regions (or another trusted estimate of region suitability). A match is feasible when the party/tickets share at least one acceptable hosting location. Then choose the location that satisfies your game's policy: cap worst-player latency, minimize average/max latency, prefer a home region, or trade latency against queue age.
{
"ticket_id": "t_123",
"party_id": "p_456",
"skill": { "estimate": 1420, "uncertainty": 85 },
"regions": { "eu-west": 31, "eu-central": 42, "us-east": 118 },
"mode": "ranked_5v5",
"queued_at": "2026-09-12T16:00:00Z"
}
FlexMatch accepts player latency data and can evaluate latency in rules or use latency-aware batching/placement. Open Match leaves this policy in your pools/matchmaking function rather than imposing one universal latency model.
Parties Are Atomic Tickets
A party should enter the queue as one logical unit. Do not split a four-player party into four independent tickets and hope team assembly puts them back together. Your policy then decides what party combinations are legal and how team skill is calculated.
- Hard constraints: party members stay together, total team size fits, incompatible platform/build combinations are rejected.
- Soft constraints: preferred role composition, party-vs-party symmetry, skill balance and region preference can relax if your game permits it.
- Team rating: define how a party's skill distribution contributes to team balance; do not blindly average if one extreme player meaningfully changes expected outcome.
Open Match: Do Not Copy an Old Architecture Diagram Blindly
Open Match 1.x uses the familiar Frontend/Backend/Query services, Match Functions, optional evaluator/synchronizer flow, and an external Director that fetches matches and writes assignments. Its documented deployment model is Kubernetes-oriented.
Open Match 2 is a separate public preview architecture. Its current documentation describes a single horizontally scalable om-core service backed by Redis, with no evaluator and a simplified core API. You still own the surrounding matchmaker: ticket-facing service, custom matchmaking function, assignment/server allocation, and player notification. If you are selecting Open Match today, decide deliberately whether you are adopting the established 1.x line or evaluating the 2.x public preview; do not mix their component diagrams.
FlexMatch: Managed Matching Does Not Require GameLift Hosting
Amazon GameLift Servers FlexMatch is a managed configurable matchmaker. Its rule sets define teams, player attributes, rules and time-based expansions; parties, player acceptance and backfill are supported. It can integrate with GameLift hosting, but AWS also documents standalone FlexMatch for peer-to-peer games or other compute providers. That makes “FlexMatch means GameLift fleet lock-in” an inaccurate decision rule.
Use FlexMatch when its rule language and managed lifecycle fit your game. Use a custom/Open Match path when the match-generation logic itself is a differentiator that needs arbitrary code, experiments or unusual constraints. Either way, keep rating calculation and game-server allocation as explicit boundaries.
Build Your Own: Size by Queue Shape, Not Total CCU
A small custom matcher can be entirely reasonable. The deciding variable is not “under 10K CCU.” It is whether the number of modes/regions/party shapes and the matching policy are simple enough to reason about, test and operate.
eligible_arrival_rate = tickets_per_second after hard partitions
minimum_fill_time ≈ players_needed / eligible_arrival_rate
actual_fill_time = minimum_fill_time + rejection/relaxation/assignment cost
A co-op game with three queues and simple constraints may need very little machinery at large CCU. A competitive game with many ranked modes, strict parties, crossplay policy, role constraints and regional fragmentation can need sophisticated matching at much smaller population.
Backfill Is a Separate Policy
Backfill asks a different question from new-match creation: “should this waiting ticket join an already-running session with an empty slot?” You need rules for round phase, score/state disadvantage, party integrity, expected remaining match duration and whether players can opt out. Prioritize or deprioritize backfill explicitly; AWS FlexMatch, for example, exposes backfill priority in its algorithm configuration.
Observability That Tells You Why Matchmaking Feels Bad
- Queue time P50/P90/P95 by region, mode, party size and skill segment.
- Expansion stage at match time: how often are players reaching each relaxation step?
- Abandonment / timeout rate by queue age.
- Skill quality: predicted win probability or rating spread between teams, not just raw MMR delta.
- Latency quality: chosen region, max/median player latency and rejected-region causes.
- Party composition: solo vs premade distributions and resulting team balance.
- Assignment failures: match found but no usable server/host, plus time to recover.
- Backfill outcomes: acceptance, completion and repeat-queue behavior for backfilled players.
Track these before experimenting with “AI matchmaking.” If you cannot explain which relaxation rule produced a poor match, adding a learned scoring model makes the system harder to debug, not smarter.
A Minimal Match-Generation Skeleton
for oldest_ticket in queue_by_age:
policy = constraints_for_age(oldest_ticket.wait_time)
candidates = pool.compatible_with(oldest_ticket, hard_constraints)
candidates = candidates.where(common_region && policy.soft_rules)
match = balance_parties_and_teams(oldest_ticket, candidates, policy)
if match.complete:
reserve_tickets_atomically(match)
assignment = allocate_or_select_session(match)
publish_assignment_or_release(match, assignment)
The difficult functions are constraints_for_age, party/team balancing, atomic reservation and failure recovery. Keep those testable with recorded queue snapshots so every rules change can be evaluated against the same historical population before it reaches production.
Crux boundary: Crux does not currently provide the SBMM system described above. Its current matcher uses exact game_mode + region buckets and forms groups of two; metadata is stored but does not drive matching, and there is no current MMR, party, latency-optimization or backfill engine. Crux can provide the surrounding player/backend and dedicated-server-registry surfaces while you use a specialized matcher or custom service for ranked matchmaking.
Current References
- Open Match 1 overview
- Open Match 2 public-preview architecture
- AWS FlexMatch overview
- FlexMatch rule expansions