Relevance Scoring: Why Chronological Order Breaks Down
You follow 500 sources. Each posts multiple times a day. You open the app. There are 3,000 new items since your last visit. Sorted by time.
You scroll past 200 items. Maybe 10 were interesting. The rest? Noise.
Chronological order works when the volume is low. Once it’s not, you need scoring.
The Scoring Function#
Every item gets a score. Higher score means higher in the list. The simplest useful model has three signals:
Affinity: How much does this user interact with the author? If you always read posts from Alice but never from Bob, Alice’s content scores higher.
Content weight: What type of content is it? A long-form post might score higher than a quick status update. An item with high engagement from others gets a boost.
Time decay: Newer content scores higher, but the advantage fades. A post from 5 minutes ago beats a post from 5 hours ago, but a post from 5 hours ago and one from 6 hours ago are basically equal.
public double score(FeedItem item, UserProfile viewer) {
double affinity = interactionService.getAffinity(viewer.getId(), item.getAuthorId());
double weight = item.getContentWeight();
double ageHours = Duration.between(item.getCreatedAt(), Instant.now()).toHours();
double decay = 1.0 / (1.0 + 0.1 * ageHours);
return affinity * weight * decay;
}
Fetch-Then-Rank#
You don’t score everything. That’s too expensive. Instead: fetch a candidate set (top N recent items via fan-out), score them, return the top K.
The scoring budget matters. If scoring adds 50ms per request, that eats into your P99 latency. Pre-compute what you can (affinity scores cached in Redis, content weights calculated at write time).
The Pagination Problem#
Cursor-based pagination works great for chronological order. With ranking, the cursor gets tricky. Scores can change between requests. An item on page 1 might shift to page 2 by the time you scroll.
Most systems solve this by snapshotting the ranked list for a session. Your first request generates the ranking. Subsequent pages pull from that snapshot. Refresh to re-rank.
What I’m Learning#
At Salesforce, we had a dashboard showing config changes across 4000+ services. Chronological order was useless. The most relevant changes (your team’s services, high-severity modifications) drowned in noise. A simple scoring model (team affinity + severity weight + recency) cut the time engineers spent scanning the dashboard significantly.
Even a basic linear scoring model beats chronological order once you pass a few hundred items. You don’t need ML to start. Affinity + weight + decay gets you surprisingly far.
What do you prioritize when displaying content to users? Pure chronological or some form of ranking?