Skip to main content

Overview

The Follow Recommendations Service (FRS) is a robust recommendation engine designed to provide users with personalized suggestions for accounts to follow. FRS powers:
  • Who-To-Follow (WTF) modules across Twitter product surfaces
  • FutureGraph tweet recommendations - tweets from accounts users may be interested in following
  • Post-NUX (New User Experience) recommendations
  • Ad targeting for account promotion

Architecture

FRS Architecture FRS is designed to accommodate diverse use cases through a flexible pipeline architecture. Each use case features a unique display location identifier.
View all display locations at: follow-recommendations-service/common/src/main/scala/com/twitter/follow_recommendations/common/models/DisplayLocation.scala

Recommendation Flow

FRS implements a comprehensive RecommendationFlow that encapsulates all steps from candidate generation to final recommendations:
1

Target Eligibility

Determine if the requesting user is eligible for recommendations based on:
  • Account age and activity level
  • Current follow count
  • Engagement history
  • Safety and quality signals
2

Candidate Generation

Fetch account candidates from multiple sources using various algorithms and user signals
3

Pre-Ranking Filtering

Apply lightweight filters to reduce candidate pool before expensive ranking
4

Feature Hydration & Ranking

Fetch ML features and rank candidates using machine learning models
5

Post-Ranking Transform

Apply transformations like deduplication, social proof attachment, and tracking tokens
6

Heavy Filtering

Apply computationally expensive filters to top-ranked candidates
7

Truncation

Trim to the final number of recommendations for optimal user experience

Flow Implementation

trait BaseRecommendationFlow[Target, Candidate <: UniversalNoun[Long]] {
  val identifier = RecommendationPipelineIdentifier("RecommendationFlow")
  
  def process(
    pipelineRequest: Target
  ): Stitch[RecommendationPipelineResult[Candidate, Seq[Candidate]]]
}
Source: follow-recommendations-service/common/src/main/scala/com/twitter/follow_recommendations/common/base/RecommendationFlow.scala:19

Candidate Generation

FRS utilizes various user signals and algorithms to identify candidates from all Twitter accounts.

Candidate Sources

Social Graph

Friends-of-friends and mutual connections

Topic Interests

Accounts matching user’s followed topics

Engagement Graph

Accounts from tweets user engaged with

Geo-based

Popular accounts in user’s location

Similar Users

Collaborative filtering recommendations

New & Notable

Trending and emerging accounts
Candidate sources are located at: follow-recommendations-service/common/src/main/scala/com/twitter/follow_recommendations/common/candidate_sources/

Filtering

FRS applies different filtering logic after generating candidates to improve quality and health.

Pre-Ranking Filters (Lightweight)

Applied before ranking to reduce candidate set:
  • Basic safety filters (blocked, muted accounts)
  • Already following check
  • Account status validation (suspended, deactivated)
  • Minimum follower threshold

Post-Ranking Filters (Heavy)

Applied after ranking to top candidates:
  • Advanced safety and quality checks
  • Engagement prediction thresholds
  • Content diversity validation
  • Fatigue management (recently dismissed accounts)
Heavier filtering logic with higher latency is typically applied after the ranking step to minimize computational cost.

Ranking

FRS employs both Machine Learning and heuristic rule-based ranking:

Feature Hydration

Before ML ranking, features are fetched for each <user, candidate> pair:
  • User Features: Demographics, engagement patterns, interests
  • Candidate Features: Account age, follower count, engagement rate
  • Edge Features: Mutual connections, topic overlap, geographic proximity

ML Ranking Model

// DataRecord construction for ML model
val dataRecord = buildDataRecord(
  user = targetUser,
  candidate = accountCandidate,
  features = hydratedFeatures
)

// Get prediction from ML service
val score = mlPredictionService.predict(dataRecord)
The ML model predicts a weighted combination: score=w1P(followrecommendation)+w2P(engagementfollow)\text{score} = w_1 \cdot P(\text{follow} | \text{recommendation}) + w_2 \cdot P(\text{engagement} | \text{follow}) Where:
  • P(follow|recommendation) - Probability user will follow if recommended
  • P(engagement|follow) - Probability of positive engagement after following
Rankers are located at: follow-recommendations-service/common/src/main/scala/com/twitter/follow_recommendations/common/rankers

Transform

After ranking, candidates undergo necessary transformations:

Deduplication

Remove duplicate accounts that may have come from multiple sources.

Social Proof

Attach context like “Followed by [mutual connection]” to increase relevance.

Tracking Tokens

Add tokens for attribution and analytics.

Formatting

Format candidates according to display location requirements.
Transformers are located at: follow-recommendations-service/common/src/main/scala/com/twitter/follow_recommendations/common/transforms

Display Locations

FRS serves recommendations across multiple Twitter surfaces:

Home Timeline

  • Who-to-follow module in timeline
  • FutureGraph tweet authors

Profile Pages

  • Similar accounts module
  • “You might also like” suggestions

Onboarding

  • Post-NUX account recommendations
  • Interest-based account selection
  • Account suggestions in search results
  • Related accounts for searched profiles

Products and Flows

Each product (corresponding to a display location) can select one or multiple flows:
// Example product configuration
case class HomeTimelineTweetRecsProduct(
  displayLocation: DisplayLocation,
  flows: Seq[RecommendationFlow]
) extends Product
View all products at: follow-recommendations-service/server/src/main/scala/com/twitter/follow_recommendations/products/

Performance Optimization

Caching Strategy

  • Cache candidate generation results
  • Cache feature hydration for popular accounts
  • Cache ML model predictions for common user-candidate pairs

Batching

  • Batch feature hydration calls
  • Batch ML prediction requests
  • Batch filtering operations

Parallel Processing

  • Fetch from multiple candidate sources in parallel
  • Parallel feature hydration for candidates
  • Concurrent filtering when possible

Monitoring and Quality

Key Metrics

  • Follow Rate: % of recommendations that result in follows
  • Engagement Rate: Engagement with followed accounts
  • Diversity: Variety in recommended account types
  • Latency: Time to generate recommendations

A/B Testing

FRS supports extensive A/B testing for:
  • New candidate sources
  • Ranking model changes
  • Filter adjustments
  • UI/UX variations
  • Home Mixer - Uses FRS for Who-to-Follow modules and FutureGraph tweets
  • CR Mixer - Complementary tweet candidate generation
  • Pushservice - Uses FRS for account recommendation notifications

Build docs developers (and LLMs) love