module-30/feed-algorithm.mdPRODUCTION MODULE

The Feed Algorithm

A feed that earns the scroll — ranking as configuration, not a data-science moonshot.

The chronological feed drowned the good content in noise — users churned because the tenth post was never worth the scroll.
A configurable ranking engine: affinity × recency-decay × content-weight scoring per user, with deterministic slots for pinned and sponsored items.

The incident

The feed was chronological because chronological was easy. As content volume grew, the good posts drowned in the noise. Users learned that the tenth item was never worth reaching, stopped scrolling, then stopped opening the app. The content was fine. The ordering was the churn.

Why the obvious approach fails

Chronological ordering treats all content as equally valuable, which is false the moment volume grows. But the usual overcorrection — 'we need machine learning' — turns a ranking problem into a twelve-month research project with a cold-start problem and no explainability. The middle path has existed since the early social feeds: a weighted scoring function over signals you already have. Recency, affinity between viewer and author, and content-type weight get you most of the value of a learned model, with behavior you can read, tune, and explain — which matters when the business asks why a post ranked where it did.

The standard

  • Feed ordering is a scoring function, not a timestamp sort: score = affinity × content-weight × recency-decay, computed per viewer.
  • Decay is explicit and tunable — old content sinks on a curve you chose, not by accident.
  • Fixed slots are deterministic: pinned posts, announcements, and sponsored items occupy declared positions regardless of score.
  • Scoring weights are configuration, changeable without deployment, and every change is logged.
  • Feed quality is measured: scroll depth and engagement-per-position are tracked so ranking changes are judged by data, not vibes.

How Cortex validates it

At plan time

feed features declare their signals, weights, decay curve, and slot rules before build.

At validation

assert: feed_order == f(affinity, weight, decay) — not created_atassert: pinned_slots.position == declaredassert: weight_changes.logged == true

Assertion syntax is illustrative.

At review

the approver sees a seeded-corpus ranking run: the same content set ordered for three different user profiles, each ordering traced to its scores.

Composes with

See where your codebase stands against this standard.