This guide covers the architecture, internals, configuration, deployment patterns, and code structure of Twitter's open-sourced recommendation algorithm. It is written for platform engineers, ML engineers, and architects who need to understand how these systems were designed and operated, either for study, adaptation, or reimplementation.
1. Architecture Overview
What the System Does
Twitter's recommendation algorithm is a distributed system of ~20 interconnected microservices that collectively decide what content users see across Home Timeline, Search, Notifications, and Who to Follow. The system processes billions of tweets and hundreds of millions of user interactions to produce personalized feeds in real time.
The architecture follows a multi-stage candidate generation → ranking → filtering pattern, where each stage progressively narrows and scores content. Services communicate primarily via Thrift RPC (using Twitter's Finatra/Finagle stack), with Kafka for event streaming and HDFS/Manhattan for persistent storage.
System Architecture
┌──────────────────────────────────────────────────────────────────┐
│ PRODUCT LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ ┌─────────┐ │
│ │ Home Mixer │ │ Tweet Mixer │ │ Push Svc │ │ Search │ │
│ │ (For You/ │ │ (content │ │ (notif │ │ Root │ │
│ │ Following) │ │ blending) │ │ ranking) │ │ │ │
│ └──────┬───────┘ └──────┬──────┘ └─────┬──────┘ └────┬────┘ │
│ │ │ │ │ │
│ ┌──────┴─────────────────┴───────────────┴───────────────┘ │
│ │ Product Mixer Framework │
│ │ (pipeline orchestration, feature hydration, scoring) │
│ └──────┬────────────────────────────────────────────────────┘ │
└─────────┼────────────────────────────────────────────────────────┘
│
┌─────────┼────────────────────────────────────────────────────────┐
│ ▼ RECOMMENDATION LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Timeline │ │ CR-Mixer │ │ Follow Recs │ │
│ │ Ranker │ │ (content │ │ Service │ │
│ │ (scoring) │ │ candidates) │ │ (who to │ │
│ │ │ │ │ │ follow) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SimClusters │ │ Topic Social │ │ Recos │ │
│ │ ANN │ │ Proof │ │ Injector │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌─────────┼────────────────────────────────────────────────────────┐
│ ▼ FEATURE & SIGNAL LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Graph Feature│ │ User Signal │ │ Representation│ │
│ │ Service │ │ Service │ │ Manager/Scorer│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Unified User │ │ VisibilityLib│ │
│ │ Actions │ │ (moderation) │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌─────────┼────────────────────────────────────────────────────────┐
│ ▼ DATA & ML LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Earlybird │ │ TweetyPie │ │ ANN Service │ │
│ │ (search │ │ (tweet │ │ (nearest │ │
│ │ index) │ │ storage) │ │ neighbor) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Navi (model │ │ twml (ML │ │ Trust & │ │
│ │ serving) │ │ training) │ │ Safety Models│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌─────────┼────────────────────────────────────────────────────────┐
│ ▼ INFRASTRUCTURE │
│ Aurora (scheduling) │ Kafka (streaming) │ HDFS (storage) │
│ Manhattan (KV) │ Strato (data) │ ZooKeeper (coord) │
│ Thrift (RPC) │ Finagle (network) │ Finatra (framework) │
└──────────────────────────────────────────────────────────────────┘
Note
All Scala/Java services in this repository use Twitter's Finatra framework for HTTP/Thrift server lifecycle, dependency injection (via Google Guice), and request filtering. The Finagle library provides the underlying RPC, load balancing, and connection pooling.
Component Inventory
| Component | Path | Language | Role |
|---|---|---|---|
| Home Mixer | home-mixer/ |
Scala | Home timeline assembly (For You, Following) |
| Timeline Ranker | timelineranker/ |
Scala | Tweet scoring and ranking |
| CR-Mixer | cr-mixer/ |
Scala | Content-based recommendation candidates |
| Follow Recs Service | follow-recommendations-service/ |
Scala | Who to Follow candidate gen + ranking |
| Product Mixer | product-mixer/ |
Scala | Generic pipeline framework |
| TweetyPie | tweetypie/ |
Scala | Tweet storage, hydration, CRUD |
| Push Service | pushservice/ |
Scala + Python | Notification ranking (heavy/light) |
| Earlybird | src/java/com/twitter/search/ |
Java | Real-time tweet search (Lucene-based) |
| VisibilityLib | visibilitylib/ |
Scala | Content moderation rules engine |
| Graph Feature Service | graph-feature-service/ |
Scala | User-user graph feature computation |
| User Signal Service | user-signal-service/ |
Scala | User behavioral signal aggregation |
| Unified User Actions | unified_user_actions/ |
Scala | Cross-product event tracking |
| SimClusters | src/scala/com/twitter/simclusters_v2/ |
Scala | Topic embedding generation (batch) |
| SimClusters ANN | simclusters-ann/ |
Scala | Topic-based nearest neighbor |
| ANN Service | ann/ |
Scala + Java | Approximate nearest neighbor (HNSW/Annoy/FAISS) |
| Representation Manager | representation-manager/ |
Scala | Embedding storage and versioning |
| Representation Scorer | representation-scorer/ |
Scala | Embedding similarity computation |
| Navi | navi/ |
Rust | ML model serving (TF/PyTorch/ONNX) |
| twml | twml/ |
Python + C++ | ML training framework (on TF) |
| Trust & Safety Models | trust_and_safety_models/ |
Python | NSFW, toxicity, abusive content models |
| Recos Injector | recos-injector/ |
Scala | Recommendation event injection |
| Topic Social Proof | topic-social-proof/ |
Scala | Topic relevance scoring |
| Tweet Mixer | tweet-mixer/ |
Scala | Tweet blending for mixed feeds |
| Interaction Graph | src/scala/com/twitter/interaction_graph/ |
Scala | User interaction graph processing |
| Timelines | timelines/ |
Python + Scala | Timeline data processing and model training |
Technology Stack
| Layer | Technology |
|---|---|
| Primary Languages | Scala 2.12, Java 11, Python 3.7, Rust |
| Build System | Bazel (Scala/Java), Cargo (Rust), pip/setuptools (Python) |
| RPC Framework | Apache Thrift via Finagle |
| Service Framework | Finatra (HTTP + Thrift), mTLS |
| ML Training | TensorFlow (via twml), Keras |
| ML Serving | Navi (TF, PyTorch, ONNX via gRPC) |
| Search Engine | Apache Lucene (custom Earlybird) |
| Stream Processing | Apache Kafka |
| Batch Processing | Apache Beam, Scalding, Scio (on Hadoop/HDFS) |
| Coordination | Apache ZooKeeper |
| Storage | Manhattan (KV), HDFS, Feature Store |
| Deployment | Aurora (Mesos scheduler) |
| Graph Processing | GraphJet (real-time), Scalding (batch) |
| Embeddings | SimClusters, Word2Vec (Follow2Vec), WHIMP (cosine similarity) |
| ANN Algorithms | HNSW, Annoy, FAISS, brute-force |
| Monitoring | Prometheus (Navi), Stats (Finagle), Decider (feature flags) |
Data Flow: Home Timeline Request
1. User opens For You tab
2. Home Mixer receives request
3. Product Mixer orchestrates pipeline:
a. Earlybird → in-network tweet candidates (recent tweets from followed accounts)
b. CR-Mixer → out-of-network candidates (content recs from multiple signals)
c. SimClusters ANN → topic-relevant candidates
4. Feature hydration:
a. Graph Feature Service → user-user graph features
b. User Signal Service → engagement history, behavioral signals
c. Representation Manager → user/tweet embeddings
5. Timeline Ranker scores all candidates with heavy ranking model
6. VisibilityLib filters by safety rules, content policy
7. Home Mixer assembles final timeline with mixing rules
8. Response returned to client
2. System Requirements
Runtime Infrastructure Dependencies
| Dependency | Purpose | Required For |
|---|---|---|
| Apache Aurora | Job scheduling and deployment | All Scala/Java services |
| Apache Kafka | Event streaming | UUA, Earlybird indexing, feature updates |
| HDFS | Distributed file storage | Batch jobs, model storage, embeddings |
| Manhattan | Key-value store | Feature serving, embedding lookup |
| Strato | Data access layer | Feature Store, various data sources |
| Apache ZooKeeper | Service coordination | Dataset pointers, config distribution |
| Finagle/Finatra | RPC and service framework | All Scala/Java services |
| Feature Store | ML feature serving | Model feature hydration |
Hardware Requirements (Per Service)
Based on Aurora job configurations in the ground truth:
| Service | CPU | RAM | Disk | Notes |
|---|---|---|---|---|
| ANN Index Builder | 32 cores | 150 GB | 60 GB | GPU optional, CUDA 11.2 for ML |
| Navi Model Serving | 8+ cores | 16+ GB | Varies | GPU recommended for TF/PyTorch |
| twml Training | 2+ cores | 8+ GB | Varies | GPU required for deep models |
| Earlybird | High | High | SSD | Lucene index in memory/SSD |
| Graph Feature Service | Moderate | High | Moderate | Graph data in memory |
Important
The ANN Dataflow worker Dockerfile specifies CUDA 11.2.2 with cuDNN 8 on Ubuntu 20.04 as the reference GPU environment. Python 3.7 is pinned explicitly.
3. Configuration Reference
Navi Model Serving Configuration
Navi reads a JSON configuration file specifying model locations and runtime parameters:
{
"model_dir": ["/path/to/model1", "/path/to/model2"],
"inter_op_parallelism": 1,
"intra_op_parallelism": 4
}
Key configuration functions (from navi/navi/src/lib.rs):
| Function | Purpose |
|---|---|
read_config(meta_file) |
Parse JSON config from file |
get_config_or_else(config, key, default_fn) |
Read config key with computed default |
get_config_or(config, key, default) |
Read config key with string default |
get_meta_dir() |
Get model metadata directory path |
Navi CLI arguments (from navi/navi/src/cli_args.rs):
| Argument | Type | Description |
|---|---|---|
--model-dir |
Vec<String> |
Model directory paths |
--config |
String |
Path to JSON configuration file |
twml Training Configuration (Complete CLI Reference)
All 70 CLI parameters extracted from twml/twml/argument_parser.py:
Core Training Parameters
| Flag | Type | Default | Description |
|---|---|---|---|
--save_dir |
str | (required) | Training result directory (local or hdfs://) |
--export_dir |
str | None | SavedModel export directory for prediction serving |
--train.batch_size |
int | 32 | Samples per training batch |
--eval.batch_size |
int | 32 | Samples per evaluation batch |
--train.learning_rate |
float | 0.002 | Learning rate (scales gradient update) |
--train.steps |
int | -1 | Training batches before evaluation (-1 = full dataset) |
--eval.steps |
int | -1 | Steps per evaluation (-1 = full dataset) |
--train.max_steps |
int | None | Global step limit (None = one train/eval cycle) |
--optimizer |
str | SGD | Optimizer: SGD, Adagrad, Adam, Ftrl, Momentum, RMSProp, LazyAdam, DGC |
--data_threads |
int | 2 | Dataset loading threads |
--num_workers |
int | None | Hogwild workers for single-node training |
Evaluation & Early Stopping
| Flag | Type | Default | Description |
|---|---|---|---|
--eval.period |
int | 600 | Seconds between evaluations in train_and_evaluate |
--eval.delay |
int | 120 | Seconds before first evaluation |
--train.early_stop_patience |
int | -1 | Max evaluations without improvement (-1 = disabled) |
--train.early_stop_tolerance |
float | 0 | Minimum improvement threshold |
--train.log_metrics |
flag | False | Log metrics during training (10-20% perf hit) |
Warning
Early stopping (
--train.early_stop_patience) cannot be used with--distributedmode. Attempting to combine them will produce undefined behavior.
Distributed Training
| Flag | Type | Default | Description |
|---|---|---|---|
--distributed |
flag | False | Enable distributed train_and_evaluate |
--distributed_training_cleanup |
flag | False | Stop TwitterSetDeployment on restart (GKE) |
--disable_auto_ps_shutdown |
flag | False | Disable auto parameter server shutdown |
--train.dataset_shards |
int | None | Number of data partitions for codistillation |
--train.dataset_shard_index |
int | None | Which data partition to use (0-indexed) |
Checkpointing & Monitoring
| Flag | Type | Default | Description |
|---|---|---|---|
--config.save_checkpoints_secs |
int | 600 | Checkpoint save interval (seconds) |
--config.keep_checkpoint_max |
int | 20 | Maximum checkpoints to retain |
--config.tf_random_seed |
int | None | TensorFlow random seed |
--overwrite_save_dir |
flag | False | Delete existing save_dir contents |
--disable_tensorboard |
flag | False | Don't start TensorBoard server |
--tensorboard_port |
int | None | TensorBoard port (ignored if disabled) |
--health_port |
int | None | Health endpoint port (set by twml_cli) |
--stats_port |
int | None | Stats endpoint port |
--experiment_tracking_path |
str | None | ML Dashboard tracking path (user:project:experiment:run) |
--disable_experiment_tracking |
flag | False | Disable experiment tracking |
Gradient & Optimization
| Flag | Type | Default | Description |
|---|---|---|---|
--gradient_noise_scale |
float | None | 0-mean normal noise scale |
--clip_gradients |
float | None | Global gradient norm clipping |
--dgc.density |
float | 0.1 | Deep gradient compression density (top 10%) |
--dgc.density_decay |
bool | True | Enable exponential density decay |
--dgc.density_decay_steps |
int | 10000 | Decay step interval |
--dgc.density_decay_rate |
float | 0.5 | Decay rate |
--dgc.min_density |
float | 0.1 | Minimum density after decay |
--dgc.accumulation |
bool | False | Accumulate small gradients |
--show_optimizer_summaries |
flag | False | Display gradients/LR in TensorBoard |
--num_mkl_threads |
int | 1 | MKL parallelism threads |
Learning Rate Decay
| Flag | Type | Default | Description |
|---|---|---|---|
--decay_steps |
float | None | Steps for exponential/polynomial/inverse/cosine decay |
--exponential_decay_rate |
float | None | Rate for exponential decay |
--end_learning_rate |
float | 0.0001 | End rate for polynomial decay |
Supported decay schedules: exponential, polynomial, piecewise_constant, inverse, cosine_decay, cosine_restarts_decay.
Feature Importance
| Flag | Type | Default | Description |
|---|---|---|---|
--feature_importance.algorithm |
str | (none) | serial or tree |
--feature_importance.sensitivity |
float | 0.03 | Max metric drop before tree expansion stops |
--feature_importance.example_count |
int | 10000 | Examples for importance computation |
--feature_importance.data_dir |
str | None | Dataset path (defaults to eval dir) |
--feature_importance.metric |
str | roc_auc | Metric for tree algorithm stop criterion |
--feature_importance.is_metric_larger_the_better |
flag | False | Metric direction (larger = better) |
--feature_importance.is_metric_smaller_the_better |
flag | False | Metric direction (smaller = better) |
--feature_importance.dont_build_tree |
flag | False | Skip trie building for tree algorithm |
--feature_importance.split_feature_group_on_period |
flag | False | Split groups on period vs. optimal prefix |
ANN Index Builder Configuration
The ANN index builder accepts these Aurora profile parameters:
| Parameter | Type | Description |
|---|---|---|
profile.name |
string | Aurora job name |
profile.role |
string | Aurora role |
profile.hadoop_cluster |
string | Hadoop cluster (e.g., dw2-smf1) |
profile.entity_kind |
enum | Entity type: user, tweet, word, url, tfwId |
profile.num_dimensions |
int | Embedding dimensionality |
profile.metric |
enum | Distance metric: InnerProduct, Cosine, L2 |
profile.concurrency_level |
int | Parallel index inserts (set to CPU count) |
profile.algo |
enum | Algorithm: hnsw, annoy, brute_force |
profile.output_dir |
string | HDFS output path for serialized index |
HNSW-specific:
| Parameter | Default | Description |
|---|---|---|
profile.ef_construction |
— | Build-time recall parameter (higher = better recall, slower build) |
profile.max_m |
— | Max connections per node (6-48 optimal, 16 recommended) |
profile.expected_elements |
— | Approximate index size |
Annoy-specific:
| Parameter | Default | Description |
|---|---|---|
profile.annoy_num_trees |
— | Number of trees (more = better accuracy, larger index) |
FAISS-specific (load testing only):
| Parameter | Default | Description |
|---|---|---|
profile.faiss_nprobe |
1 |
Number of clusters to probe |
profile.faiss_quantizerEf |
0 |
Quantizer ef parameter |
profile.faiss_quantizerKfactorRF |
0 |
Quantizer k-factor |
profile.faiss_quantizerNprobe |
0 |
Quantizer nprobe |
profile.faiss_ht |
0 |
Hamming threshold |
Note
FAISS parameters accept comma-separated lists for grid search during load testing (e.g.,
--bind=profile.faiss_nprobe=1,3,9,27,81,128).
Environment Variables
| Variable | Source | Description |
|---|---|---|
TWML_JOB_CLUSTER |
twml/twml/util.py:917 |
Cluster for distributed training |
TWML_JOB_ROLE |
twml/twml/util.py:918 |
Job role identifier |
TWML_JOB_ENV |
twml/twml/util.py:919 |
Environment (staging/production) |
TWML_DISTRIBUTED_BASE_JOBNAME |
twml/twml/util.py:920 |
Base name for distributed jobs |
HADOOP_CONF_DIR |
— | HDFS configuration (required for hdfs:// paths) |
4. Deployment & Scaling
Deployment Model
All production services deploy on Apache Aurora (a Mesos scheduler). Aurora job configurations are defined in .aurora files using Thermos templates.
Deployment pattern:
- Build service bundle with Bazel:
./bazel bundle <target>:deploy --bundle-jvm-archive=zip - Upload bundle to Packer (Twitter's artifact storage)
- Create Aurora job:
aurora job create <cluster>/<role>/<env>/<name> <config>.aurora
Service Architecture Patterns
All Scala services follow a consistent pattern using Finatra:
object Main extends ServerMain
class ServerMain extends ThriftServer with Mtls {
override val name = "service-name"
override val modules: Seq[Module] = Seq(/* Guice modules */)
override def configureThrift(router: ThriftRouter): Unit = {
router
.filter[LoggingMDCFilter]
.filter[TraceIdMDCFilter]
.filter[ThriftMDCFilter]
.filter[StatsFilter]
.add[ServiceController]
}
override protected def warmup(): Unit = {
handle[WarmupHandler]()
}
}
Standard filter chain: LoggingMDCFilter → TraceIdMDCFilter → ThriftMDCFilter → AccessLoggingFilter → StatsFilter → controller.
Earlybird Cluster Topology
Earlybird splits the tweet index across three cluster types:
| Cluster | Content | Freshness | Shard Strategy |
|---|---|---|---|
| Realtime | Public tweets | Last ~7 days | Time-based partitioning |
| Protected | Protected tweets | Last ~7 days | Time-based partitioning |
| Archive | All tweets | All time (up to ~2 days ago) | Time-based partitioning |
Each cluster uses multiple partitions with replication. Earlybird roots fan out queries across partitions and merge responses.
Indexing pipeline:
- Ingesters consume from Kafka, extract fields/features, write to intermediate Kafka topics
- Earlybird instances consume from intermediate topics and update in-memory Lucene indexes
- Feature Update Service pushes real-time engagement counts (likes, retweets, replies)
Navi Deployment
Navi compiles to three separate binaries depending on ML backend:
| Binary | Feature Flag | Backend |
|---|---|---|
navi |
tf |
TensorFlow SavedModel |
navi_torch |
torch |
PyTorch TorchScript |
navi_onnx |
onnx |
ONNX Runtime |
Each binary serves predictions via the TensorFlow Serving gRPC protocol (prediction_service.proto), with support for the KFServing v2 inference protocol.
Tip
Navi supports hot model reloading. When a new model version directory appears under the configured
model_dir, Navi automatically detects the version timestamp and loads the new model without restart.
5. Operations
Monitoring
Navi exposes Prometheus metrics via an HTTP endpoint (Warp-based). Key metrics:
| Metric | Type | Description |
|---|---|---|
| Prediction latency | Histogram | Per-model inference time distribution |
| Batch size | Histogram | Dynamic batching size distribution |
| Model load time | Gauge | Time to load model version |
| Request count | Counter | Total prediction requests |
Scala services use Finagle's built-in StatsReceiver for metrics, exposed via the standard Finagle admin interface.
Note
All Scala services include
StatsFilterin their Thrift filter chain, which automatically tracks request rate, latency percentiles, and error rates per endpoint.
Feature Flags (Deciders)
Services use Twitter's Decider system for feature flags and gradual rollouts. Decider modules are injected via Guice:
override val modules: Seq[Module] = Seq(
DeciderModule,
// ...
)
Deciders control traffic routing, experiment allocation, and feature availability without redeployment.
Health Checks
Finatra services expose health endpoints via the admin HTTP interface. twml training jobs expose health on a configurable --health_port for graceful shutdown coordination.
Tip
For twml distributed training, the
--disable_auto_ps_shutdownflag prevents the parameter server from shutting down prematurely when individual workers fail. Use this for fault-tolerant long-running jobs.
Batch Processing Operations
SimClusters, interaction graphs, and embedding generation run as batch Scalding/Scio jobs on Hadoop:
# Example: Run embedding sampling job
oscar hdfs \
--screen --tee log.txt \
--hadoop-client-memory 6000 \
--bundle embeddingformat-deploy \
--host hadoopnest1.smf1.twitter.com \
--tool com.twitter.scalding.entityembeddings.util.EmbeddingFormat.EmbeddingSamplingJob -- \
--entity_kind user \
--input.embedding_path $INPUT_PATH \
--input.embedding_format usertensor \
--output.embedding_path $OUTPUT_PATH \
--output.embedding_format tab \
--sample_percent 0.1
Supported embedding formats: usertensor, usercontinuous, comma, tab.
6. Performance
Earlybird Optimizations
- Incremental indexing: New tweets are indexed in real-time as they arrive from Kafka, maintaining index freshness
- Single-writer, multiple-reader: Eliminates write contention while supporting high query concurrency
- Partitioned index: Horizontal scaling across shards, with roots handling fan-out and merge
- Column-stride field indexes: Optimized for per-document feature lookups (engagement counts, timestamps)
Navi Batching
Navi implements dynamic batching to maximize GPU/CPU utilization:
- Incoming requests are buffered in a
BatchPredictor - Batches are flushed when either the size limit or timeout is reached (
duration_pastcheck) - Responses are demultiplexed back to individual request channels
Product Mixer Quality Factors
Product Mixer pipelines support quality factors — runtime parameters that trade off result quality for latency:
// From pipeline framework
class QualityFactorStatus {
// Dynamically adjust pipeline behavior based on latency targets
}
When latency increases, quality factors can reduce candidate counts, simplify feature hydration, or skip expensive scoring stages.
7. Security
Authentication & Authorization
- All Scala services use mTLS (mutual TLS) via Finatra's
Mtlstrait - Navi supports mTLS with x509 certificate verification (
x509-parsercrate) - Inter-service communication is authenticated via Thrift client IDs (
ThriftClientIdModule)
Warning
The open-source release does not include the internal certificate infrastructure. Services expect valid mTLS certificates for production operation. For local development, you would need to set up a local CA or disable mTLS.
Content Safety
VisibilityLib is the centralized content moderation engine. It evaluates rules against:
- Content labels — safety classifications applied to tweets and users
- Visibility actions — outcomes like hide, filter, tombstone, interstitial
- Rule conditions — composable predicates combining labels, user state, and context
The visibilitylib/src/main/scala/com/twitter/visibility/models/ directory (24 files, 126K chars) defines the complete vocabulary of content labels and actions.
Trust & Safety ML Models
| Model | Path | Purpose |
|---|---|---|
| NSFW Text Classifier | trust_and_safety_models/nsfw/ |
Text-based NSFW detection |
| Toxicity Classifier | trust_and_safety_models/toxicity/ |
Toxic content detection |
| Abusive Content Model | trust_and_safety_models/abusive/ |
Abusive behavior classification |
The toxicity trainer supports multiple loss functions: BCE, masked BCE, CCE, sCCE, Focal_BCE, inv_KL_loss, with configurable optimizers and multi-fold training.
Caution
The trust and safety models are trained on sensitive data. The model architectures and training code are included, but training data is not part of the open-source release.
8. Developer Internals
Repository Structure
the-algorithm/ # Root (7,651 files, 12.3M source chars)
├── home-mixer/ # Home timeline product pipeline
│ └── server/src/main/scala/ # Scala service implementation
├── timelineranker/ # Tweet ranking service
│ ├── server/ # Server with config and Scala source
│ ├── client/ # Client library
│ └── common/ # Shared models
├── follow-recommendations-service/ # Who to Follow
│ ├── server/ # Server implementation
│ ├── common/ # 54+ model files, candidate sources
│ └── thrift/ # Thrift service definitions
├── cr-mixer/ # Content recommendation mixer
│ ├── server/ # Server with 9 core modules
│ └── thrift/ # Thrift definitions
├── product-mixer/ # Generic pipeline framework
│ ├── core/ # Pipeline, model, feature, controller
│ ├── component-library/ # Reusable components (scorers, features)
│ └── shared-library/ # Shared utilities (observers)
├── tweetypie/ # Tweet storage service
│ ├── server/ # 49 handler files, 22 config files
│ ├── common/ # Shared types
│ └── servo/ # Request/repo/json/decider subsystems
├── pushservice/ # Push notification service
│ └── src/main/python/models/ # Heavy/light ranking models
├── visibilitylib/ # Content moderation
│ └── src/main/scala/ # 24 model files, features, rules, builders
├── src/ # Core shared source
│ ├── java/com/twitter/search/ # Earlybird search (Java)
│ ├── scala/com/twitter/ # SimClusters, interaction graph, recos
│ ├── python/ # Deepbird timeline models
│ └── thrift/ # Shared Thrift definitions
├── twml/ # Twitter ML library
│ ├── twml/ # Python package (17 files, 180K chars)
│ ├── twml_common/ # Common utilities
│ └── libtwml/ # C++ native library (21 files, 112K chars)
├── navi/ # ML model serving (Rust)
│ ├── navi/ # Main server (9 src files, 100K chars)
│ ├── dr_transform/ # Data record transformer
│ ├── segdense/ # Feature densification
│ └── thrift_bpr_adapter/ # Thrift BPR parsing (127K chars)
├── ann/ # Approximate Nearest Neighbor
│ └── src/main/scala/com/twitter/ann/ # Common, query server, scalding
├── graph-feature-service/ # Graph features
│ └── src/main/scala/ # Server + worker architecture
├── user-signal-service/ # User signals
│ ├── server/ # 18 signal types
│ └── thrift/ # Service definitions
├── unified_user_actions/ # Event tracking
│ ├── adapter/ # Event adapters
│ ├── kafka/ # Kafka serialization
│ ├── enricher/ # Event enrichment
│ ├── service/ # UUA service
│ └── client/ # Client library
├── representation-manager/ # Embedding management
│ ├── server/ # Migration, config, common
│ └── client/ # Client with config
├── representation-scorer/ # Embedding scoring
├── recos-injector/ # Recommendation injection
├── trust_and_safety_models/ # Safety ML models
│ ├── nsfw/ # NSFW classifier
│ ├── toxicity/ # Toxicity classifier
│ └── abusive/ # Abusive content model
├── topic-social-proof/ # Topic relevance
├── simclusters-ann/ # SimCluster nearest neighbor
├── tweet-mixer/ # Tweet blending (20 feature files)
├── timelines/ # Timeline data processing
├── science/ # Science/search tools
└── ci/ # CI (minimal: exit 0)
Architecture Deep Dive: Product Mixer Framework
Product Mixer is the foundational framework for all product pipelines. Key abstractions:
Pipeline types:
MixerPipeline— top-level pipeline combining multiple candidate sourcesCandidatePipeline— produces candidates from a single sourceRecommendationPipeline— end-to-end recommendation pipelineScoringPipeline— scoring-only pipeline
Pipeline steps (from product-mixer/core/src/main/scala/com/twitter/product_mixer/core/pipeline/):
Steptrait — base for all pipeline stepsFailOpenPolicy— determines behavior on candidate pipeline failure (Always, Never, or custom category set)InvalidStepStateException— thrown when a step's required data is missingNewPipelineArrowBuilder— constructs Stitch arrows for pipeline execution
Component types (from product-mixer/component-library/):
- Candidate sources
- Feature hydrators (query-level and candidate-level)
- Scorers (with common scorer abstractions)
- Filters
- Decorators (URT builders for promoted content, articles, etc.)
- Selectors and sorters
- Marshallers (URT, RTF, slice response formats)
Observation: The product-mixer/shared-library/src/main/scala/com/twitter/product_mixer/shared_library/observer/ (3 files, 10.5K chars) provides standardized observability wrappers for pipeline components.
Architecture Deep Dive: ANN Service
The ANN service (ann/src/main/scala/com/twitter/ann/common/) provides a generic interface for approximate nearest neighbor operations:
Core abstractions (from Api.scala):
EntityEmbedding[T]— entity with associated embedding vectorQueryable[T, P, D]— query interface withquery()andqueryWithDistance()Appendable[T, P, D]— mutable index withappend()andtoQueryable()RuntimeParams— algorithm-specific runtime parameters (e.g., HNSW ef, Annoy num_trees_to_search)Distance[D]— distance metric abstraction
ID serialization (from AnnInjections.scala):
LongInjection,StringInjection,IntInjection— and their Java boxed variants- Used for entity ID → byte array conversion in index storage
Query server (from ann/src/main/scala/com/twitter/ann/service/query_server/common/, 9 files, 27K chars):
- Finatra ThriftServer with standard filter chain
- Warmup handler for index pre-loading
- Supports multiple index types per server instance
Architecture Deep Dive: Navi Model Serving
Navi is a Rust-based high-performance model serving system. Key components:
Core types (from navi/navi/src/):
| Module | Key Types | Purpose |
|---|---|---|
batch.rs |
BatchPredictor |
Dynamic request batching |
bootstrap.rs |
TensorInput, TensorInputEnum |
Model bootstrap and input types |
cli_args.rs |
Args |
Command-line argument parsing (clap) |
lib.rs |
TensorReturnEnum, PredictResult, TensorScores, PredictMessage, Callback |
Core prediction types |
predict_service.rs |
PredictService |
Model management and routing |
tf_model.rs |
TFModel, TFTensorEnum |
TensorFlow backend |
torch_model.rs |
TorchModel |
PyTorch backend |
onnx_model.rs |
OnnxModel |
ONNX Runtime backend |
metrics.rs |
register_custom_metrics, metrics_handler |
Prometheus metrics |
Data processing pipeline (from navi/dr_transform/ and navi/segdense/):
| Crate | Purpose |
|---|---|
dr_transform |
Converts Thrift BatchPredictionRequest to PyTorch tensors |
segdense |
Segment-dense feature transform (schema-driven densification) |
bpr_thrift |
Thrift parser for Batch Prediction Request protocol |
Thrift data types (from navi/thrift_bpr_adapter/thrift/src/, 6 files, 127K chars):
DataRecord— feature container (binary, continuous, discrete, string, sparse features)CompactDataRecord— space-optimized variantTensorRecord— tensor-based feature containerBatchPredictionRequest/BatchPredictionResponse— request/response envelopes- Tensor types:
StringTensor,Int32Tensor,Int64Tensor,FloatTensor,DoubleTensor,BoolTensor,RawTypedTensor,BinaryTensor - Sparse tensor types:
COOSparseTensor,SparseTensor(enum)
Architecture Deep Dive: twml
twml is Twitter's ML library, split into Python and C++ layers:
Python layer (twml/twml/, 17 files, 181K chars):
| Module | Key Classes/Functions | Purpose |
|---|---|---|
argument_parser.py |
SortingHelpFormatter, DefaultSubcommandArgParse |
CLI argument parsing (70 args) |
dataset.py |
BlockFormatDataset |
TF Dataset from Twitter's block format |
hooks.py |
StepProgressHook, EarlyStopHook, MetricsUpdateHook, EarlyStopDuration, StopAtStepHook |
TF SessionRunHooks for training lifecycle |
feature_config.py |
FeatureConfig, FeatureConfigBuilder |
Feature specification and validation |
input_fns.py |
default_input_fn |
TF Estimator input function builders |
learning_rate_decay.py |
(6 decay strategies) | Learning rate scheduling |
metrics.py |
(comprehensive metric library) | Evaluation metrics |
array.py |
Array |
C++ array wrapper |
block_format_writer.py |
BlockFormatWriter |
Block format file writer |
tensorio.py |
TensorIO |
Tensor serialization I/O |
util.py |
(utility functions) | Checkpoint mgmt, feature conversion |
errors.py |
EarlyStopError, CheckpointNotFoundError |
Custom exceptions |
C++ layer (twml/libtwml/, 21 files, 112K chars + 8 internal files, 34K chars):
- Native implementations for data format parsing, array operations, and performance-critical paths
- Exposed to Python via the
ArrayandBlockFormatWriterwrapper classes
Common utilities (twml/twml_common/, 3 files, 1.8K chars):
PartitionInitializer— Keras initializer from numpy arraysserialize/deserialize— object serialization helperscreate_sparse_tensor/create_reference_input— sparse tensor construction
Architecture Deep Dive: Follow Recommendations Service
The Follow Recommendations Service is the largest service by model complexity:
Common models (follow-recommendations-service/common/src/main/scala/.../common/models/, 54 files, 78K chars):
Define the data model for recommendation candidates, scores, signals, and display contexts.
Candidate sources (16+ algorithms): Each in its own directory under common/candidate_sources/, with dedicated README explaining the algorithm, data sources, and parameters.
Ranking pipeline:
- Candidates from all sources are unified
- Feature hydration from RealGraph, Feature Store, social graph
- Multi-tower MLP model scores candidates (trained weekly on Profile Sidebar impression data)
- Filtering by safety, dedup, business rules
- Results published to Manhattan for serving
Controllers (follow-recommendations-service/server/src/main/scala/.../controllers/, 5 files, 8.5K chars):
Handle request routing, response assembly, and flow orchestration.
Architecture Deep Dive: TweetyPie
TweetyPie is the tweet storage and hydration service — one of the largest services in the codebase:
- Handlers (
tweetypie/server/src/main/scala/.../handler/, 49 files, 91K chars): Tweet CRUD operations, media handling, URL resolution, safety checks - Config (
tweetypie/server/src/main/scala/.../config/, 22 files, 245K chars): Service configuration, feature switches, dependency wiring - Core (
tweetypie/server/src/main/scala/.../core/, 15 files, 33K chars): Core tweet model, hydration, filtering - Observers (
tweetypie/server/src/main/scala/.../service/observer/, 11 files, 30K chars): Per-operation metrics and tracing - Servo subsystem: Request handling, repository pattern, JSON serialization, decider integration
Architecture Deep Dive: VisibilityLib
VisibilityLib evaluates content visibility rules:
- Models (
visibilitylib/src/main/scala/.../visibility/models/, 24 files, 126K chars): Content labels, visibility actions, rule outcomes, abuse types, media labels, safety levels - Features (
visibilitylib/src/main/scala/.../visibility/features/, 4 files, 15K chars): Feature definitions for rule evaluation - Rules (
visibilitylib/src/main/scala/.../visibility/rules/): Rule definitions and evaluation logic - Builders (
visibilitylib/src/main/scala/.../visibility/builder/): Rule context construction
Architecture Deep Dive: Push Service (Notifications)
The notification system uses a four-stage pipeline:
- Candidate generation — multiple sources produce notification candidates
- Light ranking — fast MLP pre-filter (
pushservice/src/main/python/models/light_ranking/) - Heavy ranking — ClemNet multi-task model (
pushservice/src/main/python/models/heavy_ranking/) - Quality control — policy predicates
Heavy ranker architecture (ClemNet):
class ClemNet(tf.keras.layers.Layer):
# Multi-block architecture with:
# - KerasConv1D layers (1D convolution on feature groups)
# - ChannelWiseDense layers (per-channel dense projection)
# - ResidualLayer connections
# - Block2 modules combining conv + dense + residual
Configuration via Pydantic-style params: ClemNetParams, BlockParams, ConvParams, DenseParams, TopLayerParams.
Predicate system (pushservice/src/main/scala/.../pushservice/predicate/event/, 1 file, 6.3K chars): Event-based filtering predicates for notification quality control.
Config (pushservice/src/main/scala/.../pushservice/config/, 5 files, 119K chars): The largest config module in the codebase, containing comprehensive notification pipeline configuration.
Architecture Deep Dive: SimClusters
SimClusters maps users and content into a shared topic embedding space:
- Summingbird common (
src/scala/com/twitter/simclusters_v2/summingbird/common/, 14 files, 51K chars): Real-time aggregation of cluster membership - Scalding embedding (
src/scala/com/twitter/simclusters_v2/scalding/embedding/common/, 4 files, 37K chars): Batch embedding generation - Scalding common (
src/scala/com/twitter/simclusters_v2/scalding/common/, 4 files, 14.6K chars): Shared Scalding utilities - Common (
src/scala/com/twitter/simclusters_v2/common/, 11 files, 43K chars): Core cluster models and utilities - Scio pipelines: BigQuery generation and multi-type graph processing
Architecture Deep Dive: Earlybird Internals
Earlybird's search index architecture (from src/java/com/twitter/search/):
- Core (
core/earlybird/): Inverted index, posting lists, term dictionaries (FST and MPH), column-stride field indexes, facet counting - Config (
earlybird/config/, 7 files, 26K chars +earlybird/common/config/, 2 files, 25K chars): Schema definitions, tier configurations - Ingester (
search/ingester/pipeline/app/, 4 files, 13K chars): Kafka-to-index pipeline - Earlybird Root (
earlybird_root/common/, 9 files, 38K chars +earlybird_root/config/, 1 file, 1.5K chars): Request routing and response merging across partitions - Relevance (
search/common/relevance/, config + features): Search result scoring and feature extraction - Schema (
search/common/schema/, 9 files, 62K chars): Tweet field definitions, analyzers, and index mappings
Code Patterns and Conventions
Dependency injection: All services use Google Guice via Finatra's module system. Services declare dependencies as Module sequences.
RPC pattern: Thrift-based RPC with Finagle. Services define Thrift IDL in thrift/ directories, generate Scala/Java stubs, and implement controllers.
Pipeline pattern: Product Mixer's step-based pipeline with fail-open policies, quality factors, and arrow-based execution (Twitter's Stitch library).
Configuration pattern: YAML configs in resources/config/ directories, loaded by service-specific config modules (Scala objects/classes).
Testing pattern: Test files co-located with source in parallel test/ directories. Unified User Actions includes test configs for client validation.
Error handling:
- Custom exception types (
EarlyStopError,CheckpointNotFoundError,InvalidStepStateException) - Fail-open policies for non-critical pipeline stages
- Stitch-based error propagation in Product Mixer
Testing Strategy
- Unit tests: Standard Scala/Java test frameworks, co-located in
src/test/ - Integration tests: Service-level tests using Finatra's testing framework
- Load tests: ANN load test framework with configurable QPS, duration, and parameter grid search
- ML model evaluation: twml's
eval.stepsandeval.periodfor online evaluation during training - CI: Minimal (
ci/ci.shexits 0); real validation through Bazel test targets
Potential Improvements
Based on code analysis:
- CI pipeline — The
ci/ci.shscript is a no-op. A real CI pipeline running./bazel test //...on key targets would prevent regressions. - Documentation —
repo_docs.cbfreturned 0 files at the root level. A root-level README summarizing the architecture would help new contributors. - Dependency versions — Navi pins specific Git tags for
ortcrate; upgrading to ONNX Runtime 1.15+ would bring performance improvements. - Python version — twml and the Dockerfile pin Python 3.7, which reached end-of-life in June 2023. Migration to 3.10+ is recommended.
- Configuration — Service configs are scattered across 12+ locations with no unified schema. A shared configuration framework would reduce duplication.
- Feature Store coupling — Multiple services depend on the internal Feature Store client. Abstracting this behind an interface would improve testability.
Contributing
The repository is read-only (archived). To study or adapt the code:
- Fork the repository
- Build individual services using Bazel or Cargo
- Service-specific READMEs exist in many subsystem directories (especially
follow-recommendations-service/common/src/main/scala/.../candidate_sources/) - The ANN subsystem has the most complete internal documentation with README.rst and README.md files
9. Integration Points
Inter-Service Communication
All services communicate via Thrift RPC over Finagle with mTLS:
| From | To | Protocol | Purpose |
|---|---|---|---|
| Home Mixer | Earlybird | Thrift | In-network tweet retrieval |
| Home Mixer | CR-Mixer | Thrift | Out-of-network candidates |
| Home Mixer | Timeline Ranker | Thrift | Candidate scoring |
| Home Mixer | VisibilityLib | Library | Content filtering |
| Timeline Ranker | Graph Feature Service | Thrift | Graph features |
| Timeline Ranker | User Signal Service | Thrift | User signals |
| Follow Recs | RealGraph | Thrift/Data | Engagement graph |
| Follow Recs | Feature Store | Thrift | Feature hydration |
| Push Service | Navi | gRPC | Model inference |
| All Services | Manhattan | Thrift | Key-value storage |
| All Services | Kafka | Protocol | Event streaming |
| Batch Jobs | HDFS | Protocol | Data storage |
Event Streaming (Kafka Topics)
Unified User Actions (UUA) standardizes event tracking across all surfaces:
- Adapters (
unified_user_actions/adapter/): Convert product-specific events to UUA format - Kafka serialization (
unified_user_actions/kafka/): Custom Kafka SerDe with internal type handling - Enrichment (
unified_user_actions/enricher/): Post-processing enrichment of UUA events - Client (
unified_user_actions/client/): Client library with environment-specific config (staging, production)
Embedding Infrastructure
| Component | Role |
|---|---|
| Representation Manager | Stores and versions embeddings, handles migration between versions |
| Representation Scorer | Computes similarity between embedding pairs |
| Feature Store | Serves embeddings as features for model inference |
| SimClusters | Generates topic embeddings via batch Scalding/Scio jobs |
| ANN Service | Indexes embeddings for nearest-neighbor queries |
The Representation Manager's migration module (representation-manager/server/src/main/scala/.../migration/, 1 file, 34K chars) handles version transitions for the embedding storage layer.