Pagination breaks first
Pagination breaks first in feed systems, so handle it first.
Understand why page numbers cannot work. Between page one and page two new posts arrive, everything shifts down, and your user sees the same items twice.
Use a cursor instead. Your first response includes an opaque marker encoding the position, in the simplest case the sortable identifier of the last item. The next request asks for 50 items older than that marker, which stays stable no matter how many posts arrived above it.
Notice the behaviour that gives you. New content appears only when somebody deliberately pulls to refresh, exactly how people expect a feed to work.
Ranking is a layer
Treat ranking as a layer rather than a rewrite. A chronological feed serves the sorted set directly. A ranked one inserts a scoring step between fetching candidates and responding.
Pull a few hundred candidates, call a scoring service that runs a model over things like how close you are to the author, what you engaged with before, how recent it is and what kind of post it is, then sort.
The first famous version of this was literally three terms multiplied together, before deep models replaced the formula. The architecture survives every model change: your candidates come from the same cache, and scoring is a stateless service you can scale and test independently.
Name the consistency wrinkle that ranking creates. Scores change between requests, so a freshly ranked second page might re-include something from the first.
Freeze the order for the session, as production systems do, making the cursor a snapshot held server-side for that scroll.
Accept staleness as the last honest trade. Precomputed feeds drift, so you unfollow somebody and their post is still sitting in your cached set.
Filter at hydration time against the current follow list, and let fan-out clean up lazily. Chasing perfect freshness synchronously would bring back every cost precomputation exists to avoid.
Worked example
Sofia's team ships infinite scroll for a news app using page numbers: /feed?page=2. Reviews start mentioning the app shows me the same story three times. The mechanism is textbook: a user reads page one for 90 seconds, 12 new stories arrive, everything shifts 12 positions down, and page two overlaps 12 of the 20 items they just read. Duplicate rate in analytics: 18 percent of loaded items during peak news hours. She switches to cursor pagination, with the cursor carrying the last item's Snowflake-style ID, and the query becomes WHERE id < cursor LIMIT 20 against the feed cache. Duplicates drop to zero and one unexpected metric moves: average session depth rises 22 percent, because users who stopped scrolling at the first repeat, assuming they had reached the end, no longer hit one.