Skip to content

Matchmaking without starvation

Interest filters make conversations better and queues worse. How Kahoo relaxes its preferences over time so a niche choice never leaves you waiting alone.

Last updated

Every filter you add to a matchmaking queue makes the resulting conversation better and the queue worse. Ask for someone in Iceland who likes photography and you have described a lovely conversation and an empty room.

Hard constraints and soft ones

Kahoo splits the rules into two kinds, and the split is the whole design.

Hard constraints can never be violated, no matter how long anyone waits:

  • never yourself, and never a second tab of your own session;
  • never someone either of you has blocked, in either direction;
  • never a country other than the one you asked for, unless you explicitly widened the search.

Soft preferences influence the score and decay with time:

  • shared interests, worth 12 points each up to a cap of 48;
  • waiting time, worth 1.5 points per second up to a cap of 60;
  • a large penalty for someone you just spoke to.

Why the caps matter

In the first seconds of a search, interests dominate: 48 possible points against a handful from waiting. After about forty seconds the waiting bonus has caught up, and after that it decides. The system moves smoothly from "find me someone who shares my interests" to "find me anybody" without a mode switch, a timeout, or a fallback path to get wrong.

The recent-partner rule works the same way. For the first 25 seconds, the person you just left is excluded outright. After that they become a heavily penalised candidate rather than an impossible one — so in a two-person lobby you eventually get matched again instead of both waiting forever.

The country choice is not a hint

A lot of platforms treat a country filter as a preference and quietly match you elsewhere when the queue is thin. We think that is worse than useless: you cannot trust any filter after you notice it once.

On Kahoo the country filter is hard. If nobody is there after twenty seconds, you get told — "No one is available in Japan right now" — and offered two buttons: keep waiting, or search worldwide. Widening is a thing you do, not a thing that happens to you.

Pairing atomically

The classic bug in this kind of system is handing one person to two matches at once. Kahoo avoids it structurally rather than with locking: all matchmaking happens inside a single Durable Object, which is single-threaded by definition. The sweep removes both sockets from the queue before it announces anything, and nothing else can interleave between those two statements.

The greedy sweep itself is ordinary: order the queue by how long each person has waited, and for each unconsumed person take their best compatible partner. Longest-waiting first means the person who has been patient gets first pick of the room.

Testing it

All of this is a pure function: given a queue, a clock reading and a policy, return the pairs. No database, no sockets, no real time. That makes the awkward cases — blocked users, the self-match, the country mismatch, the starvation scenario — cheap to write as unit tests, and it means the scoring weights can be tuned without touching the transport code at all.