Functional Scope (In-Scope)
- Social Graph Integration: Pulls candidate posts from users followed by the active observer user.
- Engagement Signal Computation: Extracts explicit engagement indicators (likes, shares, comments) to weight baseline interest.
- Recency Time Decay: Penalizes candidate posts exponentially according to their absolute age:
score * e^(-λ * ageInHours). - Author Diversity Controls: Implements strict deduplication capping candidate results from a single author to ensure user variety.
Explicit Boundaries (Out-of-Scope)
- Deep-Neural Ranking Pipelines: Real-time inference of complex transformer recommendation models is out-of-scope.
- Real-time Graph Sockets: Bypasses live transport socket subscriptions for immediate post delivery.
Clean reference designs demonstrating personalized feed ranking in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Post {
private final String id;
private final String authorId;
private final String content;
private final long timestamp;
private final int likes;
private final int comments;
private final int shares;
public Post(String id, String authorId, String content, long timestamp, int likes, int comments, int shares) {
this.id = id;
this.authorId = authorId;
this.content = content;
this.timestamp = timestamp;
this.likes = likes;
this.comments = comments;
this.shares = shares;
}
public String getId() { return id; }
public String getAuthorId() { return authorId; }
public String getContent() { return content; }
public long getTimestamp() { return timestamp; }
public int getLikes() { return likes; }
public int getComments() { return comments; }
public int getShares() { return shares; }
}
class FeedCandidate implements Comparable<FeedCandidate> {
private final Post post;
private final double score;
public FeedCandidate(Post post, double score) {
this.post = post;
this.score = score;
}
public Post getPost() { return post; }
public double getScore() { return score; }
@Override
public int compareTo(FeedCandidate o) {
return Double.compare(o.score, this.score); // Descending score sorting
}
}
class SignalExtractor {
public static double computeRecencyDecay(Post post, long currentMillis, double decayRate) {
double ageInHours = (currentMillis - post.getTimestamp()) / (1000.0 * 60.0 * 60.0);
return Math.exp(-decayRate * Math.max(0.0, ageInHours));
}
public static double computeEngagementScore(Post post) {
return (post.getLikes() * 1.0) + (post.getComments() * 5.0) + (post.getShares() * 10.0);
}
public static double computeEdgeWeight(String userId, String authorId, Map<String, Double> userAffinities) {
return userAffinities.getOrDefault(authorId, 0.1);
}
}
class FeedService {
private final Map<String, List<Post>> authorPosts = new ConcurrentHashMap<>();
private final Map<String, Set<String>> userFollows = new ConcurrentHashMap<>();
private final Map<String, Map<String, Double>> userAffinities = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void addPost(Post post) {
lock.writeLock().lock();
try {
authorPosts.computeIfAbsent(post.getAuthorId(), k -> new CopyOnWriteArrayList<>()).add(post);
} finally {
lock.writeLock().unlock();
}
}
public void followUser(String followerId, String followeeId) {
lock.writeLock().lock();
try {
userFollows.computeIfAbsent(followerId, k -> ConcurrentHashMap.newKeySet()).add(followeeId);
} finally {
lock.writeLock().unlock();
}
}
public void setAffinity(String userId, String authorId, double score) {
lock.writeLock().lock();
try {
userAffinities.computeIfAbsent(userId, k -> new ConcurrentHashMap<>()).put(authorId, score);
} finally {
lock.writeLock().unlock();
}
}
public List<Post> getFeed(String userId, int page, int pageSize, int diversityCap, double decayRate) {
lock.readLock().lock();
try {
long currentMillis = System.currentTimeMillis();
Set<String> followed = userFollows.getOrDefault(userId, Collections.emptySet());
Map<String, Double> affinities = userAffinities.getOrDefault(userId, Collections.emptyMap());
List<FeedCandidate> candidates = new ArrayList<>();
for (String followee : followed) {
List<Post> posts = authorPosts.getOrDefault(followee, Collections.emptyList());
for (Post post : posts) {
double recency = SignalExtractor.computeRecencyDecay(post, currentMillis, decayRate);
double engagement = SignalExtractor.computeEngagementScore(post);
double edge = SignalExtractor.computeEdgeWeight(userId, post.getAuthorId(), affinities);
// Composite Score formula
double score = (engagement + 1.0) * recency * edge;
candidates.add(new FeedCandidate(post, score));
}
}
Collections.sort(candidates);
// Apply author diversity filtering
List<Post> filteredResult = new ArrayList<>();
Map<String, Integer> authorCounts = new HashMap<>();
for (FeedCandidate candidate : candidates) {
String authorId = candidate.getPost().getAuthorId();
int currentCount = authorCounts.getOrDefault(authorId, 0);
if (currentCount < diversityCap) {
filteredResult.add(candidate.getPost());
authorCounts.put(authorId, currentCount + 1);
}
}
// Slice list for pagination
int startIndex = (page - 1) * pageSize;
if (startIndex >= filteredResult.size()) {
return Collections.emptyList();
}
int endIndex = Math.min(startIndex + pageSize, filteredResult.size());
return filteredResult.subList(startIndex, endIndex);
} finally {
lock.readLock().unlock();
}
}
}
public class Main {
public static void main(String[] args) {
System.out.println("=== INITIALIZING NEWS FEED RANKER ===");
FeedService feedService = new FeedService();
// Setup users & follows
feedService.followUser("Alice", "Bob");
feedService.followUser("Alice", "Charlie");
// Set higher affinity towards Charlie
feedService.setAffinity("Alice", "Charlie", 2.5);
// Add posts with different ages and engagement counts
long now = System.currentTimeMillis();
// Bob's posts (Low affinity, high recency vs low recency)
feedService.addPost(new Post("p1", "Bob", "Hello world from Bob!", now - (30 * 60 * 1000), 10, 2, 0)); // 30m ago
feedService.addPost(new Post("p2", "Bob", "Another Bob post!", now - (3 * 3600 * 1000), 50, 10, 2)); // 3h ago
// Charlie's posts (High affinity)
feedService.addPost(new Post("p3", "Charlie", "Stunning landscapes here!", now - (60 * 60 * 1000), 5, 1, 0)); // 1h ago
feedService.addPost(new Post("p4", "Charlie", "A redundant post from Charlie", now - (10 * 60 * 1000), 0, 0, 0)); // 10m ago
System.out.println("\\n=== 1. FETCHING FEED FOR ALICE (PAGE 1) ===");
List<Post> feedPage1 = feedService.getFeed("Alice", 1, 2, 2, 0.5);
for (int i = 0; i < feedPage1.size(); i++) {
Post post = feedPage1.get(i);
System.out.println((i + 1) + ". [" + post.getAuthorId() + "] " + post.getContent() + " (Likes: " + post.getLikes() + ")");
}
System.out.println("\\n=== 2. FETCHING FEED FOR ALICE (PAGE 2) ===");
List<Post> feedPage2 = feedService.getFeed("Alice", 2, 2, 2, 0.5);
for (int i = 0; i < feedPage2.size(); i++) {
Post post = feedPage2.get(i);
System.out.println((i + 3) + ". [" + post.getAuthorId() + "] " + post.getContent());
}
}
}