System Design
Part 4 of 5Facebook Frontend System Architecture: Building for Billions
Facebook Frontend System Architecture: Building for Billions
1. Product Overview & Scale
Facebook operates the largest social network on Earth—a product that fundamentally changed how humanity communicates. The frontend must handle interactions from 3+ billion monthly active users, rendering personalized content in real-time while maintaining sub-second responsiveness.
Scale Metrics:
- 3+ billion monthly active users
- 2+ billion daily active users
- 100+ billion messages sent daily (Messenger + WhatsApp)
- 3+ billion Stories shared daily across Meta apps
- 500,000+ comments per minute
- 4+ million reactions per minute
- 350 million photos uploaded daily
- 100+ languages supported
- 95%+ availability target (5 nines)
Facebook didn't just build a social network—they invented the tools to build it. React, GraphQL, Relay, Flow, Jest, and countless other technologies were born from the challenges of building Facebook's frontend.
┌─────────────────────────────────────────────────────────────────────────────┐
│ FACEBOOK FRONTEND SCALE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ CONCURRENT USERS │
│ │ │
│ 50M │ ████ │
│ │ █████ │
│ 40M │ ██████ Peak (New Year's Eve) │
│ │ ███████ │
│ 30M │ ████ ████████ │
│ │ █████ █████████ │
│ 20M │ ██████ ██████████ │
│ │ ███████ ███████████ │
│ 10M │████████ ████████████ │
│ │█████████ █████████████ │
│ 0 └─────────────────────────────────────────────────────────────────────│
│ 6am 12pm 6pm 12am 6am 12pm 6pm 12am │
│ │
│ INTERACTIONS PER SECOND (Peak): │
│ • Page views: 6+ million/sec │
│ • API requests: 50+ million/sec │
│ • WebSocket messages: 100+ million/sec │
│ • GraphQL queries: 10+ million/sec │
│ │
│ RENDERING COMPLEXITY: │
│ • News Feed: 50-200 stories per session │
│ • Each story: 10-50 components │
│ • Total components per session: 5,000-10,000 │
│ • Re-renders per minute: 1,000+ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2. Core Frontend Challenges
The News Feed Problem
News Feed is Facebook's crown jewel and its most complex frontend challenge. It must render an infinitely scrolling, personalized, real-time updating stream of heterogeneous content:
┌─────────────────────────────────────────────────────────────────────────────┐
│ NEWS FEED COMPLEXITY │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ STORY TYPES (Each with unique rendering logic): │
│ ───────────────────────────────────────────────── │
│ • Text post • Photo (single) • Photo album │
│ • Video (uploaded) • Video (live) • Video (reel) │
│ • Link share • Event • Group post │
│ • Page post • Ad (sponsored) • Marketplace listing │
│ • Memory • Birthday • Friend suggestion │
│ • Story tray • Fundraiser • Poll │
│ • Check-in • Life event • Feeling/Activity │
│ │
│ EACH STORY CONTAINS: │
│ ───────────────────────────────────────────────────────────────────────── │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ┌─────┐ Author Name Privacy Icon ••• Menu │ │
│ │ │Photo│ Timestamp • Location │ │
│ │ └─────┘ │ │
│ │ │ │
│ │ Post content with @mentions, #hashtags, links, translations... │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Media (photo/video/link preview) │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 👍 1.2K 💬 234 comments ↗️ 56 shares │ │
│ │ ─────────────────────────────────────────────────────────────────── │ │
│ │ 👍 Like 💬 Comment ↗️ Share │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Comments (nested, real-time, expandable) │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ CHALLENGES: │
│ • 20+ story types × infinite scroll = unbounded component tree │
│ • Real-time updates (new comments/reactions) without re-fetching │
│ • Personalization: same component, different data shapes │
│ • Privacy: different users see different parts of same story │
│ • Performance: must scroll at 60fps with video autoplay │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
The Six Core Challenges
| Challenge | Impact | Facebook's Innovation |
|---|---|---|
| Component Reusability | 20+ story types share common patterns | React component model |
| Data Fetching | Heterogeneous data with complex relationships | GraphQL + Relay |
| Real-Time Updates | Comments, reactions, typing indicators | GraphQL Subscriptions |
| Rendering Performance | Infinite scroll with media | React Fiber, concurrent rendering |
| Code Size | Full app would be 100MB+ JS | Incremental code loading |
| Consistency | Billions of users, same experience | Atomic CSS (Stylex) |
3. High-Level Frontend Architecture
Facebook's frontend is built on technologies they created specifically to solve their problems:
┌─────────────────────────────────────────────────────────────────────────────┐
│ FACEBOOK FRONTEND ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EDGE NETWORK │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ PoPs │ │ Static │ │ HTML │ │ API │ │ │
│ │ │ (100+ │ │ Assets │ │ Shell │ │ Gateway │ │ │
│ │ │ locations) │ │ (CDN) │ │ Cache │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RENDERING TIER │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ BigPipe │ │ Relay │ │ Streaming │ │ │
│ │ │ (Pagelets) │ │ Server │ │ SSR │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ GRAPHQL GATEWAY │ │ │
│ │ │ • Query parsing & validation │ │ │
│ │ │ • Authorization (viewer context) │ │ │
│ │ │ • Data loader batching │ │ │
│ │ │ • Persisted queries │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CLIENT APPLICATION │ │
│ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ React 18 │ │ Relay │ │ Stylex │ │ Recoil │ │ │
│ │ │ (Fiber) │ │ (Data) │ │ (CSS) │ │ (State) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ INCREMENTAL LOADING │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │
│ │ │ │ Tier 1 │ │ Tier 2 │ │ Tier 3 │ │ On-demand │ │ │ │
│ │ │ │ (Shell) │ │ (Core) │ │ (Feature)│ │ (Lazy) │ │ │ │
│ │ │ │ 50KB │ │ 200KB │ │ 500KB │ │ As needed │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ REAL-TIME LAYER │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │
│ │ │ │ GraphQL │ │ MQTT │ │ Presence │ │ Push │ │ │ │
│ │ │ │ Subscr. │ │ (Mobile) │ │ Service │ │ Notifs │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4. React at Facebook Scale
Facebook invented React, and their usage patterns inform the framework's evolution. They run the largest React application in the world.
React Fiber Architecture
// Understanding React Fiber - Facebook's rendering engine
// Fiber is a unit of work - each component instance has a fiber
interface Fiber {
// Type and key
type: string | Function | Class;
key: string | null;
// Tree structure
child: Fiber | null;
sibling: Fiber | null;
return: Fiber | null; // Parent
// State
pendingProps: Props;
memoizedProps: Props;
memoizedState: State;
// Effects
flags: Flags; // Placement, Update, Deletion, etc.
subtreeFlags: Flags;
// Priority
lanes: Lanes; // Concurrent rendering priority
childLanes: Lanes;
// Alternate (double buffering)
alternate: Fiber | null;
}
// Facebook's usage of concurrent features
function NewsFeed({ userId }: { userId: string }) {
// useTransition for non-urgent updates
const [isPending, startTransition] = useTransition();
// useDeferredValue for expensive renders
const deferredFilter = useDeferredValue(filter);
const handleFilterChange = (newFilter: FilterState) => {
// Urgent: update filter UI immediately
setFilter(newFilter);
// Non-urgent: re-fetch and re-render feed
startTransition(() => {
refetchFeed(newFilter);
});
};
return (
<Suspense fallback={<FeedSkeleton />}>
<Feed
userId={userId}
filter={deferredFilter}
isPending={isPending}
/>
</Suspense>
);
}
// Concurrent rendering with priorities
// Facebook uses lane-based priority system
const SyncLane = 0b0000000000000000000000000000001; // Sync (highest)
const InputContinuousLane = 0b0000000000000000000000000000100; // User input
const DefaultLane = 0b0000000000000000000000000000010000; // Normal updates
const TransitionLane = 0b0000000000000000000000001000000; // Transitions
const IdleLane = 0b0100000000000000000000000000000; // Idle work
// Priority scheduling in practice
function CommentComposer({ postId }: { postId: string }) {
const [text, setText] = useState('');
const [mentions, setMentions] = useState<Mention[]>([]);
const handleTextChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
const newText = e.target.value;
// Sync priority: update text immediately (user typing)
setText(newText);
// Transition priority: parse mentions (can be interrupted)
startTransition(() => {
const parsedMentions = parseMentions(newText);
setMentions(parsedMentions);
});
};
return (
<div className="comment-composer">
<textarea
value={text}
onChange={handleTextChange}
placeholder="Write a comment..."
/>
<MentionSuggestions text={text} mentions={mentions} />
</div>
);
}
Component Architecture Patterns
// Facebook's component composition patterns
// 1. Compound Components for complex UI
interface StoryContextValue {
storyId: string;
authorId: string;
viewerId: string;
privacy: PrivacyLevel;
interactions: InteractionState;
}
const StoryContext = createContext<StoryContextValue | null>(null);
function Story({ story, children }: StoryProps) {
const contextValue = useMemo(() => ({
storyId: story.id,
authorId: story.author.id,
viewerId: useViewerId(),
privacy: story.privacy,
interactions: story.interactions,
}), [story]);
return (
<StoryContext.Provider value={contextValue}>
<article className="story" data-story-id={story.id}>
{children}
</article>
</StoryContext.Provider>
);
}
// Compound component parts
Story.Header = function StoryHeader() {
const { storyId, authorId } = useStoryContext();
const author = useFragment(StoryAuthorFragment, authorId);
return (
<header className="story-header">
<ProfilePhoto user={author} size="medium" />
<div className="story-meta">
<ProfileLink user={author} />
<StoryTimestamp storyId={storyId} />
</div>
<StoryMenu storyId={storyId} />
</header>
);
};
Story.Content = function StoryContent({ story }: { story: StoryFragment }) {
// Polymorphic content rendering based on story type
return (
<div className="story-content">
<StoryText text={story.message} />
<StoryAttachments attachments={story.attachments} />
</div>
);
};
Story.Footer = function StoryFooter() {
const { storyId, interactions } = useStoryContext();
return (
<footer className="story-footer">
<ReactionSummary storyId={storyId} reactions={interactions.reactions} />
<ActionBar storyId={storyId} />
<Comments storyId={storyId} />
</footer>
);
};
// Usage: composable, readable, maintainable
function FeedStory({ story }: { story: StoryData }) {
return (
<Story story={story}>
<Story.Header />
<Story.Content story={story} />
<Story.Footer />
</Story>
);
}
// 2. Render Props for flexible data handling
interface FeedDataProviderProps {
userId: string;
children: (data: {
stories: Story[];
loading: boolean;
error: Error | null;
loadMore: () => void;
hasMore: boolean;
}) => React.ReactNode;
}
function FeedDataProvider({ userId, children }: FeedDataProviderProps) {
const { data, loading, error, fetchMore } = useQuery(
FeedQuery,
{ variables: { userId } }
);
const loadMore = useCallback(() => {
if (data?.feed.pageInfo.hasNextPage) {
fetchMore({
variables: {
cursor: data.feed.pageInfo.endCursor,
},
});
}
}, [data, fetchMore]);
return children({
stories: data?.feed.edges.map(e => e.node) ?? [],
loading,
error,
loadMore,
hasMore: data?.feed.pageInfo.hasNextPage ?? false,
});
}
5. GraphQL & Relay Architecture
Facebook invented GraphQL and Relay to solve their data fetching challenges. This is the most sophisticated client-side data layer in production.
┌─────────────────────────────────────────────────────────────────────────────┐
│ RELAY ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ COMPONENT TREE DATA REQUIREMENTS │
│ ────────────── ───────────────── │
│ │
│ NewsFeed query NewsFeedQuery { │
│ │ viewer { │
│ ├── FeedStory feed(first: 10) { │
│ │ │ edges { │
│ │ ├── StoryHeader node { │
│ │ │ └── ProfilePhoto ...FeedStory_story │
│ │ │ } │
│ │ ├── StoryContent } │
│ │ │ } │
│ │ └── StoryFooter } │
│ │ ├── Reactions } │
│ │ └── Comments │
│ │ │
│ └── FeedStory fragment FeedStory_story on Story { │
│ └── ... id │
│ ...StoryHeader_story │
│ ...StoryContent_story │
│ KEY INSIGHT: ...StoryFooter_story │
│ Each component declares } │
│ exactly what data it needs. │
│ Relay compiles this into fragment StoryHeader_story on Story { │
│ a single optimized query. author { │
│ ...ProfilePhoto_user │
│ ...ProfileLink_user │
│ } │
│ createdTime │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Relay Implementation
// Relay fragment co-location - components declare their data needs
// StoryHeader.tsx
import { graphql, useFragment } from 'react-relay';
const StoryHeaderFragment = graphql`
fragment StoryHeader_story on Story {
id
createdTime
privacy
author {
id
name
profilePicture(size: 40) {
uri
}
}
# Conditional field based on viewer
canViewerEdit
canViewerDelete
}
`;
interface StoryHeaderProps {
story: StoryHeader_story$key;
}
export function StoryHeader({ story }: StoryHeaderProps) {
const data = useFragment(StoryHeaderFragment, story);
return (
<header className="story-header">
<ProfilePhoto
uri={data.author.profilePicture.uri}
name={data.author.name}
/>
<div className="story-meta">
<Link to={`/profile/${data.author.id}`}>
{data.author.name}
</Link>
<span className="timestamp">
<RelativeTime time={data.createdTime} />
</span>
<PrivacyIcon privacy={data.privacy} />
</div>
<StoryMenu
storyId={data.id}
canEdit={data.canViewerEdit}
canDelete={data.canViewerDelete}
/>
</header>
);
}
// Parent component composes fragments
// FeedStory.tsx
const FeedStoryFragment = graphql`
fragment FeedStory_story on Story {
id
...StoryHeader_story
...StoryContent_story
...StoryFooter_story
}
`;
export function FeedStory({ story }: FeedStoryProps) {
const data = useFragment(FeedStoryFragment, story);
return (
<article className="feed-story">
<StoryHeader story={data} />
<StoryContent story={data} />
<StoryFooter story={data} />
</article>
);
}
// Root query with pagination
// NewsFeed.tsx
const NewsFeedQuery = graphql`
query NewsFeedQuery($cursor: String, $count: Int = 10) {
viewer {
feed(first: $count, after: $cursor)
@connection(key: "NewsFeed_feed") {
edges {
node {
id
...FeedStory_story
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
`;
export function NewsFeed() {
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment(
graphql`
fragment NewsFeed_viewer on Viewer
@refetchable(queryName: "NewsFeedPaginationQuery") {
feed(first: $count, after: $cursor)
@connection(key: "NewsFeed_feed") {
edges {
node {
id
...FeedStory_story
}
}
}
}
`,
viewerRef
);
return (
<VirtualizedList
data={data.feed.edges}
renderItem={({ item }) => (
<FeedStory key={item.node.id} story={item.node} />
)}
onEndReached={() => {
if (hasNext && !isLoadingNext) {
loadNext(10);
}
}}
/>
);
}
Relay Store & Normalization
// Relay normalizes all data into a flat store
interface RelayStore {
// Normalized records by ID
records: {
[dataID: string]: Record;
};
// Root records
roots: {
[queryName: string]: DataID;
};
}
// Example normalized store state
const storeSnapshot = {
records: {
'client:root': {
__id: 'client:root',
__typename: '__Root',
viewer: { __ref: 'user:123' },
},
'user:123': {
__id: 'user:123',
__typename: 'User',
id: '123',
name: 'John Doe',
'profilePicture(size:40)': { __ref: 'client:user:123:profilePicture' },
},
'client:user:123:profilePicture': {
__id: 'client:user:123:profilePicture',
__typename: 'Image',
uri: 'https://cdn.facebook.com/...',
},
'story:456': {
__id: 'story:456',
__typename: 'Story',
id: '456',
message: 'Hello world!',
author: { __ref: 'user:123' },
reactions: { __ref: 'client:story:456:reactions' },
},
// Connections are also normalized
'client:user:123:feed': {
__id: 'client:user:123:feed',
__typename: 'FeedConnection',
edges: [
{ __ref: 'client:user:123:feed:edges:0' },
{ __ref: 'client:user:123:feed:edges:1' },
],
pageInfo: { __ref: 'client:user:123:feed:pageInfo' },
},
},
};
// Optimistic updates for instant feedback
function useAddReaction(storyId: string) {
const [commit, isInFlight] = useMutation<AddReactionMutation>(graphql`
mutation AddReactionMutation($input: AddReactionInput!) {
addReaction(input: $input) {
story {
id
reactions {
count
viewerReaction
}
}
}
}
`);
return useCallback((reactionType: ReactionType) => {
commit({
variables: {
input: { storyId, reactionType },
},
// Optimistic update - instant UI feedback
optimisticUpdater: (store) => {
const story = store.get(storyId);
if (!story) return;
const reactions = story.getLinkedRecord('reactions');
if (!reactions) return;
// Increment count optimistically
const currentCount = reactions.getValue('count') as number;
reactions.setValue(currentCount + 1, 'count');
reactions.setValue(reactionType, 'viewerReaction');
},
// Handle actual response
updater: (store, response) => {
// Relay automatically updates normalized store
// from response data
},
// Rollback on error
onError: (error) => {
// Optimistic update is automatically rolled back
showErrorToast('Failed to add reaction');
},
});
}, [commit, storyId]);
}
Real-Time Updates with GraphQL Subscriptions
// GraphQL subscriptions for real-time updates
const StoryUpdatedSubscription = graphql`
subscription StoryUpdatedSubscription($storyId: ID!) {
storyUpdated(storyId: $storyId) {
story {
id
reactions {
count
viewerReaction
topReactions {
type
count
}
}
comments {
count
}
}
}
}
`;
function useStorySubscription(storyId: string) {
useSubscription<StoryUpdatedSubscription>(
useMemo(
() => ({
subscription: StoryUpdatedSubscription,
variables: { storyId },
updater: (store, data) => {
// Subscription data automatically merged into store
// because it returns the same node (story) with same ID
},
}),
[storyId]
)
);
}
// New comment subscription with optimistic ordering
const NewCommentSubscription = graphql`
subscription NewCommentSubscription($storyId: ID!) {
newComment(storyId: $storyId) {
comment {
id
...Comment_comment
}
story {
id
comments {
count
}
}
}
}
`;
function useNewCommentSubscription(storyId: string) {
useSubscription<NewCommentSubscription>({
subscription: NewCommentSubscription,
variables: { storyId },
updater: (store, data) => {
// Get the story's comments connection
const story = store.get(storyId);
if (!story) return;
const commentsConnection = ConnectionHandler.getConnection(
story,
'Comments_comments'
);
if (!commentsConnection) return;
// Create edge for new comment
const newComment = store.getRootField('newComment')?.getLinkedRecord('comment');
if (!newComment) return;
const newEdge = ConnectionHandler.createEdge(
store,
commentsConnection,
newComment,
'CommentEdge'
);
// Insert at beginning (newest first)
ConnectionHandler.insertEdgeBefore(commentsConnection, newEdge);
},
});
}
6. BigPipe & Streaming Architecture
Facebook pioneered BigPipe for streaming page content before React existed. The concept evolved into modern streaming SSR.
┌─────────────────────────────────────────────────────────────────────────────┐
│ BIGPIPE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ TRADITIONAL PAGE LOAD: │
│ ────────────────────── │
│ Server ████████████████████████████████████████████████ Response │
│ [────── All processing ──────][──── Send ────] │
│ │
│ Browser ████████████████ Render │
│ [── Parse ──][─ Render ─] │
│ │
│ BIGPIPE PAGE LOAD: │
│ ────────────────── │
│ Server ████ Shell ████████████████████████████████████ Pagelets │
│ [──][Pagelet1][Pagelet2][Pagelet3][Pagelet4][Pagelet5] │
│ │ │ │ │ │ │ │
│ Browser ▼ ▼ ▼ ▼ ▼ ▼ │
│ ██████████████████████████████████████████████████ │
│ [Shell][P1 render][P2 render][P3 render][P4 render][P5 render] │
│ │
│ BENEFIT: Parallel processing on server + incremental rendering │
│ Time to first paint: 200ms vs 2000ms │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Modern Streaming SSR with React 18
// Facebook's streaming SSR implementation
import { renderToPipeableStream } from 'react-dom/server';
interface PageletConfig {
id: string;
component: React.ComponentType<any>;
priority: 'critical' | 'high' | 'normal' | 'low';
dataLoader: () => Promise<any>;
}
class StreamingRenderer {
private pagelets: Map<string, PageletConfig> = new Map();
registerPagelet(config: PageletConfig) {
this.pagelets.set(config.id, config);
}
async render(request: Request): Promise<Response> {
const url = new URL(request.url);
// Start data loading for all pagelets immediately
const dataPromises = new Map<string, Promise<any>>();
for (const [id, config] of this.pagelets) {
dataPromises.set(id, config.dataLoader());
}
// Create streaming response
const stream = new TransformStream();
const writer = stream.writable.getWriter();
// Send shell immediately
await this.sendShell(writer);
// Stream pagelets as they resolve
const sortedPagelets = Array.from(this.pagelets.values())
.sort((a, b) => this.priorityOrder(a.priority) - this.priorityOrder(b.priority));
// Process in priority order, but don't wait for lower priority
for (const pagelet of sortedPagelets) {
this.streamPagelet(writer, pagelet, dataPromises.get(pagelet.id)!);
}
// Wait for all pagelets then close
await Promise.all(dataPromises.values());
await writer.close();
return new Response(stream.readable, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Transfer-Encoding': 'chunked',
},
});
}
private async sendShell(writer: WritableStreamDefaultWriter) {
const shell = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Facebook</title>
<link rel="stylesheet" href="/static/css/core.css">
<script src="/static/js/bigpipe.js" async></script>
</head>
<body>
<div id="globalContainer">
<div id="header" class="pagelet-placeholder"></div>
<div id="content">
<div id="feed" class="pagelet-placeholder"></div>
<div id="sidebar" class="pagelet-placeholder"></div>
</div>
</div>
`;
await writer.write(new TextEncoder().encode(shell));
}
private async streamPagelet(
writer: WritableStreamDefaultWriter,
pagelet: PageletConfig,
dataPromise: Promise<any>
) {
try {
const data = await dataPromise;
// Render pagelet to HTML
const html = renderToString(
<pagelet.component data={data} />
);
// Stream as BigPipe pagelet
const pageletScript = `
<script>
BigPipe.onPageletArrive({
id: "${pagelet.id}",
content: ${JSON.stringify(html)},
css: [],
js: []
});
</script>
`;
await writer.write(new TextEncoder().encode(pageletScript));
} catch (error) {
// Stream error state
const errorScript = `
<script>
BigPipe.onPageletError({ id: "${pagelet.id}", error: "Failed to load" });
</script>
`;
await writer.write(new TextEncoder().encode(errorScript));
}
}
private priorityOrder(priority: PageletConfig['priority']): number {
const order = { critical: 0, high: 1, normal: 2, low: 3 };
return order[priority];
}
}
// Client-side BigPipe handler
class BigPipe {
private arrived = new Map<string, PageletData>();
private waiting = new Map<string, (data: PageletData) => void>();
onPageletArrive(data: PageletData) {
const { id, content, css, js } = data;
// Load CSS first
this.loadCSS(css);
// Insert content
const placeholder = document.getElementById(id);
if (placeholder) {
placeholder.innerHTML = content;
placeholder.classList.remove('pagelet-placeholder');
}
// Load and execute JS
this.loadJS(js).then(() => {
// Hydrate React components
const container = document.getElementById(id);
if (container && window.__REACT_HYDRATE__) {
window.__REACT_HYDRATE__(id, container);
}
});
// Notify waiters
const waiter = this.waiting.get(id);
if (waiter) {
waiter(data);
this.waiting.delete(id);
}
this.arrived.set(id, data);
}
onPageletError(data: { id: string; error: string }) {
const placeholder = document.getElementById(data.id);
if (placeholder) {
placeholder.innerHTML = `
<div class="pagelet-error">
<p>Something went wrong</p>
<button onclick="location.reload()">Retry</button>
</div>
`;
placeholder.classList.remove('pagelet-placeholder');
}
}
private async loadCSS(urls: string[]): Promise<void> {
await Promise.all(urls.map(url => {
return new Promise<void>((resolve) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.onload = () => resolve();
link.onerror = () => resolve();
document.head.appendChild(link);
});
}));
}
private async loadJS(urls: string[]): Promise<void> {
for (const url of urls) {
await new Promise<void>((resolve) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => resolve();
script.onerror = () => resolve();
document.body.appendChild(script);
});
}
}
}
window.BigPipe = new BigPipe();
7. Incremental Code Loading
Facebook's application is massive—the full bundle would be 100MB+. They use sophisticated code splitting:
// Tier-based code loading
// Tier 1: Shell (~50KB) - Loads immediately
// - Navigation
// - Basic layout
// - Loading states
// Tier 2: Core (~200KB) - Loads with initial data
// - News Feed rendering
// - Story components
// - Basic interactions
// Tier 3: Features (~500KB) - Loads on interaction
// - Comments expansion
// - Sharing dialog
// - Reactions picker
// Tier 4: On-demand - Loads when needed
// - Settings pages
// - Marketplace
// - Groups
// - Events
// Facebook's resource loader
interface ResourceModule {
id: string;
type: 'js' | 'css';
src: string;
dependencies: string[];
tier: 1 | 2 | 3 | 4;
}
class ResourceLoader {
private loaded = new Set<string>();
private loading = new Map<string, Promise<void>>();
private modules: Map<string, ResourceModule>;
constructor(manifest: ResourceModule[]) {
this.modules = new Map(manifest.map(m => [m.id, m]));
}
async load(moduleId: string): Promise<void> {
if (this.loaded.has(moduleId)) return;
if (this.loading.has(moduleId)) return this.loading.get(moduleId);
const module = this.modules.get(moduleId);
if (!module) throw new Error(`Unknown module: ${moduleId}`);
const loadPromise = this.loadModule(module);
this.loading.set(moduleId, loadPromise);
try {
await loadPromise;
this.loaded.add(moduleId);
} finally {
this.loading.delete(moduleId);
}
}
private async loadModule(module: ResourceModule): Promise<void> {
// Load dependencies first
await Promise.all(
module.dependencies.map(dep => this.load(dep))
);
// Load the module itself
if (module.type === 'js') {
await this.loadScript(module.src);
} else {
await this.loadStylesheet(module.src);
}
}
private loadScript(src: string): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load: ${src}`));
document.head.appendChild(script);
});
}
private loadStylesheet(src: string): Promise<void> {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = src;
link.onload = () => resolve();
link.onerror = () => reject(new Error(`Failed to load: ${src}`));
document.head.appendChild(link);
});
}
}
// React integration with lazy loading
const CommentsSection = lazy(() =>
resourceLoader.load('comments-section').then(() =>
import('./CommentsSection')
)
);
const ShareDialog = lazy(() =>
resourceLoader.load('share-dialog').then(() =>
import('./ShareDialog')
)
);
const ReactionPicker = lazy(() =>
resourceLoader.load('reaction-picker').then(() =>
import('./ReactionPicker')
)
);
// Prefetch on hover
function StoryFooter({ storyId }: { storyId: string }) {
const prefetchComments = useCallback(() => {
resourceLoader.load('comments-section');
}, []);
const prefetchShare = useCallback(() => {
resourceLoader.load('share-dialog');
}, []);
return (
<footer className="story-footer">
<button
onMouseEnter={prefetchComments}
onClick={() => setShowComments(true)}
>
Comment
</button>
<button
onMouseEnter={prefetchShare}
onClick={() => setShowShareDialog(true)}
>
Share
</button>
</footer>
);
}
8. Stylex: Atomic CSS at Scale
Facebook created Stylex to solve CSS at scale—type-safe, atomic CSS with zero runtime:
// Stylex: Facebook's CSS-in-JS solution
import * as stylex from '@stylexjs/stylex';
// Define styles - compiled to atomic CSS at build time
const styles = stylex.create({
container: {
display: 'flex',
flexDirection: 'column',
padding: 16,
backgroundColor: {
default: 'white',
':hover': '#f5f5f5',
},
borderRadius: 8,
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
},
header: {
display: 'flex',
alignItems: 'center',
gap: 12,
marginBottom: 12,
},
content: {
fontSize: 15,
lineHeight: 1.4,
color: '#1c1e21',
},
// Conditional styles
highlighted: {
backgroundColor: '#fff3cd',
borderLeft: '4px solid #ffc107',
},
});
// Apply styles
function StoryCard({ story, isHighlighted }: StoryCardProps) {
return (
<article
{...stylex.props(
styles.container,
isHighlighted && styles.highlighted
)}
>
<header {...stylex.props(styles.header)}>
<ProfilePhoto user={story.author} />
<StoryMeta story={story} />
</header>
<div {...stylex.props(styles.content)}>
{story.message}
</div>
</article>
);
}
// Compiled output (atomic CSS)
/*
.x1 { display: flex; }
.x2 { flex-direction: column; }
.x3 { padding: 16px; }
.x4 { background-color: white; }
.x5:hover { background-color: #f5f5f5; }
.x6 { border-radius: 8px; }
.x7 { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
...
<article class="x1 x2 x3 x4 x5 x6 x7">
*/
// Theme variables with Stylex
const tokens = stylex.defineVars({
primaryColor: '#1877f2',
secondaryColor: '#65676b',
backgroundColor: '#ffffff',
textColor: '#1c1e21',
borderColor: '#dddfe2',
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px',
},
fontSize: {
sm: '13px',
md: '15px',
lg: '17px',
xl: '20px',
},
});
// Dark mode override
const darkTheme = stylex.createTheme(tokens, {
backgroundColor: '#18191a',
textColor: '#e4e6eb',
borderColor: '#3e4042',
});
// Use theme tokens
const themedStyles = stylex.create({
card: {
backgroundColor: tokens.backgroundColor,
color: tokens.textColor,
borderColor: tokens.borderColor,
padding: tokens.spacing.md,
fontSize: tokens.fontSize.md,
},
});
function ThemedStoryCard({ story, isDarkMode }: Props) {
return (
<div {...stylex.props(isDarkMode && darkTheme)}>
<article {...stylex.props(themedStyles.card)}>
{/* ... */}
</article>
</div>
);
}
9. Real-Time Systems
Facebook's real-time infrastructure handles billions of events per second:
// Real-time event system architecture
interface RealtimeEvent {
type: string;
payload: unknown;
timestamp: number;
targets: string[]; // User IDs who should receive this
}
// Client-side real-time manager
class RealtimeManager {
private socket: WebSocket | null = null;
private subscriptions = new Map<string, Set<(event: RealtimeEvent) => void>>();
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
private reconnectDelay = 1000;
connect(userId: string, token: string) {
const url = `wss://realtime.facebook.com/ws?user=${userId}&token=${token}`;
this.socket = new WebSocket(url);
this.socket.onopen = () => {
this.reconnectAttempts = 0;
this.resubscribeAll();
};
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data) as RealtimeEvent;
this.handleEvent(data);
};
this.socket.onclose = () => {
this.scheduleReconnect(userId, token);
};
this.socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
subscribe(eventType: string, callback: (event: RealtimeEvent) => void) {
if (!this.subscriptions.has(eventType)) {
this.subscriptions.set(eventType, new Set());
// Notify server of new subscription
this.send({
type: 'subscribe',
payload: { eventType },
});
}
this.subscriptions.get(eventType)!.add(callback);
// Return unsubscribe function
return () => {
const callbacks = this.subscriptions.get(eventType);
if (callbacks) {
callbacks.delete(callback);
if (callbacks.size === 0) {
this.subscriptions.delete(eventType);
this.send({
type: 'unsubscribe',
payload: { eventType },
});
}
}
};
}
private handleEvent(event: RealtimeEvent) {
const callbacks = this.subscriptions.get(event.type);
if (callbacks) {
callbacks.forEach(callback => callback(event));
}
}
private send(message: object) {
if (this.socket?.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
private scheduleReconnect(userId: string, token: string) {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnect attempts reached');
return;
}
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
this.reconnectAttempts++;
setTimeout(() => {
this.connect(userId, token);
}, delay);
}
private resubscribeAll() {
for (const eventType of this.subscriptions.keys()) {
this.send({
type: 'subscribe',
payload: { eventType },
});
}
}
}
// React integration
const RealtimeContext = createContext<RealtimeManager | null>(null);
function useRealtimeSubscription<T>(
eventType: string,
handler: (event: RealtimeEvent & { payload: T }) => void
) {
const realtime = useContext(RealtimeContext);
useEffect(() => {
if (!realtime) return;
return realtime.subscribe(eventType, handler as any);
}, [realtime, eventType, handler]);
}
// Usage: Real-time typing indicator
function useTypingIndicator(conversationId: string) {
const [typingUsers, setTypingUsers] = useState<User[]>([]);
useRealtimeSubscription<TypingEvent>(
`typing:${conversationId}`,
(event) => {
const { userId, isTyping } = event.payload;
setTypingUsers(current => {
if (isTyping) {
// Add user if not already typing
if (!current.find(u => u.id === userId)) {
return [...current, { id: userId }];
}
} else {
// Remove user
return current.filter(u => u.id !== userId);
}
return current;
});
}
);
return typingUsers;
}
// Usage: Real-time reactions
function useRealtimeReactions(storyId: string) {
const [commit] = useMutation(UpdateReactionsMutation);
useRealtimeSubscription<ReactionEvent>(
`reactions:${storyId}`,
(event) => {
const { reactionType, userId, action } = event.payload;
// Update Relay store
commit({
variables: { storyId },
updater: (store) => {
const story = store.get(storyId);
if (!story) return;
const reactions = story.getLinkedRecord('reactions');
if (!reactions) return;
// Update reaction count
const currentCount = reactions.getValue('count') as number;
const newCount = action === 'add' ? currentCount + 1 : currentCount - 1;
reactions.setValue(newCount, 'count');
},
});
}
);
}
10. Comments System Architecture
The comments system is one of Facebook's most complex features—infinitely nested, real-time, with rich media support:
// Comments data model with nested replies
const CommentFragment = graphql`
fragment Comment_comment on Comment {
id
body {
text
ranges {
entity {
__typename
... on User { id name }
... on Page { id name }
... on Hashtag { tag }
}
offset
length
}
}
author {
id
name
profilePicture(size: 32) { uri }
}
createdTime
reactions {
count
viewerReaction
}
replies(first: 3) @connection(key: "Comment_replies") {
count
edges {
node {
id
...Comment_comment
}
}
}
canViewerReply
canViewerEdit
canViewerDelete
}
`;
// Recursive comment component
interface CommentProps {
comment: Comment_comment$key;
depth: number;
maxDepth?: number;
}
function Comment({ comment, depth, maxDepth = 4 }: CommentProps) {
const data = useFragment(CommentFragment, comment);
const [showReplies, setShowReplies] = useState(depth < 2);
const [isReplying, setIsReplying] = useState(false);
// Subscribe to real-time updates
useRealtimeSubscription(`comment:${data.id}`, handleCommentUpdate);
return (
<div
className="comment"
style={{ marginLeft: depth > 0 ? 40 : 0 }}
>
<div className="comment-content">
<ProfilePhoto user={data.author} size={32} />
<div className="comment-bubble">
<ProfileLink user={data.author} className="comment-author" />
<CommentBody body={data.body} />
</div>
</div>
<div className="comment-actions">
<ReactionButton
targetId={data.id}
targetType="comment"
currentReaction={data.reactions.viewerReaction}
/>
{data.canViewerReply && depth < maxDepth && (
<button onClick={() => setIsReplying(true)}>Reply</button>
)}
<RelativeTime time={data.createdTime} />
{data.reactions.count > 0 && (
<span className="reaction-count">
{data.reactions.count}
</span>
)}
</div>
{/* Reply composer */}
{isReplying && (
<CommentComposer
parentCommentId={data.id}
onSubmit={() => setIsReplying(false)}
onCancel={() => setIsReplying(false)}
autoFocus
/>
)}
{/* Nested replies */}
{data.replies.count > 0 && (
<div className="comment-replies">
{!showReplies ? (
<button onClick={() => setShowReplies(true)}>
View {data.replies.count} {data.replies.count === 1 ? 'reply' : 'replies'}
</button>
) : (
<>
{data.replies.edges.map(edge => (
<Comment
key={edge.node.id}
comment={edge.node}
depth={depth + 1}
maxDepth={maxDepth}
/>
))}
{data.replies.count > data.replies.edges.length && (
<LoadMoreReplies commentId={data.id} />
)}
</>
)}
</div>
)}
</div>
);
}
// Comment body with rich formatting
function CommentBody({ body }: { body: CommentBody }) {
const parts = useMemo(() => {
const result: React.ReactNode[] = [];
let lastIndex = 0;
// Sort ranges by offset
const sortedRanges = [...body.ranges].sort((a, b) => a.offset - b.offset);
for (const range of sortedRanges) {
// Add text before this range
if (range.offset > lastIndex) {
result.push(body.text.slice(lastIndex, range.offset));
}
// Add the range with appropriate formatting
const rangeText = body.text.slice(range.offset, range.offset + range.length);
switch (range.entity.__typename) {
case 'User':
case 'Page':
result.push(
<Link
key={range.offset}
to={`/${range.entity.id}`}
className="mention"
>
{rangeText}
</Link>
);
break;
case 'Hashtag':
result.push(
<Link
key={range.offset}
to={`/hashtag/${range.entity.tag}`}
className="hashtag"
>
{rangeText}
</Link>
);
break;
default:
result.push(rangeText);
}
lastIndex = range.offset + range.length;
}
// Add remaining text
if (lastIndex < body.text.length) {
result.push(body.text.slice(lastIndex));
}
return result;
}, [body]);
return <p className="comment-text">{parts}</p>;
}
// Optimistic comment creation
function useCreateComment(storyId: string) {
const [commit, isInFlight] = useMutation(CreateCommentMutation);
const viewerId = useViewerId();
return useCallback((text: string, parentCommentId?: string) => {
const tempId = `temp-comment-${Date.now()}`;
commit({
variables: {
input: {
storyId,
text,
parentCommentId,
},
},
// Optimistic update
optimisticResponse: {
createComment: {
comment: {
id: tempId,
body: { text, ranges: [] },
author: {
id: viewerId,
name: 'You', // Will be replaced
profilePicture: { uri: '' },
},
createdTime: new Date().toISOString(),
reactions: { count: 0, viewerReaction: null },
replies: { count: 0, edges: [] },
canViewerReply: true,
canViewerEdit: true,
canViewerDelete: true,
},
},
},
// Insert into connection
updater: (store, response) => {
const comment = store.getRootField('createComment')?.getLinkedRecord('comment');
if (!comment) return;
if (parentCommentId) {
// Add to parent comment's replies
const parent = store.get(parentCommentId);
if (!parent) return;
const replies = ConnectionHandler.getConnection(parent, 'Comment_replies');
if (!replies) return;
const edge = ConnectionHandler.createEdge(store, replies, comment, 'CommentEdge');
ConnectionHandler.insertEdgeAfter(replies, edge);
} else {
// Add to story's comments
const story = store.get(storyId);
if (!story) return;
const comments = ConnectionHandler.getConnection(story, 'Story_comments');
if (!comments) return;
const edge = ConnectionHandler.createEdge(store, comments, comment, 'CommentEdge');
ConnectionHandler.insertEdgeAfter(comments, edge);
}
},
});
}, [commit, storyId, viewerId]);
}
11. Privacy & Permissions System
Facebook's privacy system is foundational—every piece of content has viewer-dependent visibility:
// Privacy checking at the data layer
// GraphQL schema with privacy
/*
type Story implements Node {
id: ID!
message: String
privacy: Privacy!
# These fields check viewer permissions
canViewerSee: Boolean!
canViewerComment: Boolean!
canViewerShare: Boolean!
canViewerEdit: Boolean!
canViewerDelete: Boolean!
# Viewer-dependent data
viewerReaction: ReactionType
}
type Privacy {
type: PrivacyType!
allowList: [User!]
denyList: [User!]
friendsOfFriends: Boolean
}
enum PrivacyType {
PUBLIC
FRIENDS
FRIENDS_EXCEPT
SPECIFIC_FRIENDS
ONLY_ME
CUSTOM
}
*/
// Client-side privacy-aware components
interface PrivacyGateProps {
permission: 'canViewerSee' | 'canViewerComment' | 'canViewerShare' | 'canViewerEdit' | 'canViewerDelete';
fallback?: React.ReactNode;
children: React.ReactNode;
}
function PrivacyGate({ permission, fallback = null, children }: PrivacyGateProps) {
const hasPermission = usePermission(permission);
if (!hasPermission) {
return <>{fallback}</>;
}
return <>{children}</>;
}
// Usage in Story component
function Story({ story }: { story: StoryData }) {
return (
<PrivacyGate permission="canViewerSee" fallback={<HiddenStoryPlaceholder />}>
<article className="story">
<StoryHeader story={story} />
<StoryContent story={story} />
<StoryFooter story={story} />
</article>
</PrivacyGate>
);
}
function StoryFooter({ story }: { story: StoryData }) {
return (
<footer className="story-footer">
<ReactionButton story={story} />
<PrivacyGate permission="canViewerComment">
<CommentButton story={story} />
</PrivacyGate>
<PrivacyGate permission="canViewerShare">
<ShareButton story={story} />
</PrivacyGate>
<PrivacyGate permission="canViewerEdit">
<EditButton story={story} />
</PrivacyGate>
<PrivacyGate permission="canViewerDelete">
<DeleteButton story={story} />
</PrivacyGate>
</footer>
);
}
// Privacy selector component
const PRIVACY_OPTIONS = [
{
type: 'PUBLIC',
icon: '🌍',
label: 'Public',
description: 'Anyone on or off Facebook',
},
{
type: 'FRIENDS',
icon: '👥',
label: 'Friends',
description: 'Your friends on Facebook',
},
{
type: 'FRIENDS_EXCEPT',
icon: '👥',
label: 'Friends except...',
description: 'Friends; Except: select friends',
},
{
type: 'SPECIFIC_FRIENDS',
icon: '👤',
label: 'Specific friends',
description: 'Only show to some friends',
},
{
type: 'ONLY_ME',
icon: '🔒',
label: 'Only me',
description: 'Only visible to you',
},
{
type: 'CUSTOM',
icon: '⚙️',
label: 'Custom',
description: 'Include and exclude friends and lists',
},
];
function PrivacySelector({
value,
onChange,
}: {
value: Privacy;
onChange: (privacy: Privacy) => void;
}) {
const [showModal, setShowModal] = useState(false);
return (
<>
<button
className="privacy-selector"
onClick={() => setShowModal(true)}
>
<PrivacyIcon type={value.type} />
{PRIVACY_OPTIONS.find(o => o.type === value.type)?.label}
</button>
{showModal && (
<Modal onClose={() => setShowModal(false)}>
<PrivacySelectorModal
value={value}
onChange={(newValue) => {
onChange(newValue);
setShowModal(false);
}}
/>
</Modal>
)}
</>
);
}
12. A/B Testing (Gatekeeper)
Facebook runs thousands of A/B tests simultaneously. Their system, Gatekeeper, is deeply integrated into the frontend:
// Gatekeeper: Facebook's A/B testing system
interface Experiment {
name: string;
variants: string[];
weights: number[];
targetingRules: TargetingRule[];
}
interface ExperimentAssignment {
experimentName: string;
variant: string;
loggedAt: number;
}
class GatekeeperClient {
private assignments: Map<string, ExperimentAssignment> = new Map();
private userId: string;
private userContext: UserContext;
constructor(userId: string, userContext: UserContext) {
this.userId = userId;
this.userContext = userContext;
}
// Check if user is in experiment variant
check(experimentName: string, variant: string): boolean {
const assignment = this.getAssignment(experimentName);
return assignment.variant === variant;
}
// Get assigned variant
getVariant(experimentName: string): string {
return this.getAssignment(experimentName).variant;
}
private getAssignment(experimentName: string): ExperimentAssignment {
// Check cache
if (this.assignments.has(experimentName)) {
return this.assignments.get(experimentName)!;
}
// Get experiment config (prefetched from server)
const experiment = this.getExperimentConfig(experimentName);
// Deterministic assignment based on user ID
const assignment = this.assignVariant(experiment);
// Cache and log
this.assignments.set(experimentName, assignment);
this.logExposure(assignment);
return assignment;
}
private assignVariant(experiment: Experiment): ExperimentAssignment {
// Check targeting rules first
if (!this.matchesTargeting(experiment.targetingRules)) {
return {
experimentName: experiment.name,
variant: 'control',
loggedAt: Date.now(),
};
}
// Deterministic hash of userId + experimentName
const hash = this.hash(`${this.userId}:${experiment.name}`);
const bucket = hash % 100;
// Find variant based on weights
let cumulative = 0;
for (let i = 0; i < experiment.variants.length; i++) {
cumulative += experiment.weights[i];
if (bucket < cumulative) {
return {
experimentName: experiment.name,
variant: experiment.variants[i],
loggedAt: Date.now(),
};
}
}
return {
experimentName: experiment.name,
variant: experiment.variants[experiment.variants.length - 1],
loggedAt: Date.now(),
};
}
private matchesTargeting(rules: TargetingRule[]): boolean {
return rules.every(rule => this.evaluateRule(rule));
}
private evaluateRule(rule: TargetingRule): boolean {
const value = this.userContext[rule.field];
switch (rule.operator) {
case 'equals': return value === rule.value;
case 'contains': return Array.isArray(value) && value.includes(rule.value);
case 'greaterThan': return value > rule.value;
case 'lessThan': return value < rule.value;
case 'in': return rule.value.includes(value);
default: return false;
}
}
private hash(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash);
}
private logExposure(assignment: ExperimentAssignment) {
// Log to analytics
analytics.logEvent('experiment_exposure', {
experiment: assignment.experimentName,
variant: assignment.variant,
timestamp: assignment.loggedAt,
});
}
}
// React integration
const GatekeeperContext = createContext<GatekeeperClient | null>(null);
function useExperiment(experimentName: string): {
variant: string;
isControl: boolean;
isVariant: (name: string) => boolean;
} {
const gatekeeper = useContext(GatekeeperContext);
if (!gatekeeper) throw new Error('GatekeeperProvider not found');
const variant = useMemo(
() => gatekeeper.getVariant(experimentName),
[gatekeeper, experimentName]
);
return {
variant,
isControl: variant === 'control',
isVariant: (name: string) => variant === name,
};
}
// Usage
function NewsFeedHeader() {
const { isVariant } = useExperiment('news_feed_header_redesign');
if (isVariant('new_design')) {
return <NewFeedHeader />;
}
return <LegacyFeedHeader />;
}
// Conditional rendering with experiment boundaries
function ExperimentBoundary({
experiment,
variants,
}: {
experiment: string;
variants: Record<string, React.ReactNode>;
}) {
const { variant } = useExperiment(experiment);
return <>{variants[variant] ?? variants.control}</>;
}
// Usage
<ExperimentBoundary
experiment="reaction_picker_style"
variants={{
control: <ClassicReactionPicker />,
floating: <FloatingReactionPicker />,
expanded: <ExpandedReactionPicker />,
}}
/>
13. Performance Monitoring
Facebook's performance monitoring is the most comprehensive in the industry:
// Performance instrumentation
interface PerformanceMarker {
name: string;
timestamp: number;
metadata?: Record<string, unknown>;
}
interface PerformanceSpan {
name: string;
startTime: number;
endTime?: number;
metadata?: Record<string, unknown>;
children: PerformanceSpan[];
}
class PerformanceTracer {
private markers: PerformanceMarker[] = [];
private spans: Map<string, PerformanceSpan> = new Map();
private activeSpans: PerformanceSpan[] = [];
mark(name: string, metadata?: Record<string, unknown>) {
this.markers.push({
name,
timestamp: performance.now(),
metadata,
});
}
startSpan(name: string, metadata?: Record<string, unknown>): () => void {
const span: PerformanceSpan = {
name,
startTime: performance.now(),
metadata,
children: [],
};
// Add as child of current span if one exists
if (this.activeSpans.length > 0) {
const parent = this.activeSpans[this.activeSpans.length - 1];
parent.children.push(span);
}
this.activeSpans.push(span);
this.spans.set(name, span);
// Return end function
return () => {
span.endTime = performance.now();
this.activeSpans.pop();
// Log if this is a root span
if (this.activeSpans.length === 0) {
this.logSpan(span);
}
};
}
// React integration
useTraceSpan(name: string, deps: unknown[] = []) {
useEffect(() => {
const endSpan = this.startSpan(name);
return endSpan;
}, deps);
}
// Component render tracking
wrapComponent<P>(
Component: React.ComponentType<P>,
name: string
): React.ComponentType<P> {
const tracer = this;
return function TracedComponent(props: P) {
const renderStart = performance.now();
useEffect(() => {
const renderEnd = performance.now();
tracer.mark(`${name}:render`, {
duration: renderEnd - renderStart,
});
});
return <Component {...props} />;
};
}
private logSpan(span: PerformanceSpan) {
const duration = (span.endTime ?? performance.now()) - span.startTime;
// Send to analytics
analytics.logPerformance({
name: span.name,
duration,
metadata: span.metadata,
children: this.serializeSpanTree(span),
});
}
private serializeSpanTree(span: PerformanceSpan): object {
return {
name: span.name,
duration: (span.endTime ?? performance.now()) - span.startTime,
children: span.children.map(c => this.serializeSpanTree(c)),
};
}
}
// Page-level performance tracking
class PagePerformanceTracker {
private tracer = new PerformanceTracer();
constructor() {
this.trackCoreWebVitals();
this.trackCustomMetrics();
}
private trackCoreWebVitals() {
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1] as PerformancePaintTiming;
analytics.logMetric('LCP', {
value: lastEntry.startTime,
element: (lastEntry as any).element?.tagName,
});
}).observe({ type: 'largest-contentful-paint', buffered: true });
// FID / INP
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
analytics.logMetric('interaction', {
value: entry.duration,
name: entry.name,
interactionId: (entry as any).interactionId,
});
}
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });
// CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!(entry as any).hadRecentInput) {
clsValue += (entry as any).value;
}
}
}).observe({ type: 'layout-shift', buffered: true });
// Report CLS on page hide
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
analytics.logMetric('CLS', { value: clsValue });
}
});
}
private trackCustomMetrics() {
// Time to News Feed interactive
this.tracer.mark('page:start');
// Wait for feed to render
const observer = new MutationObserver((mutations, obs) => {
const feedElement = document.querySelector('[data-testid="news-feed"]');
if (feedElement && feedElement.children.length > 0) {
this.tracer.mark('feed:visible');
obs.disconnect();
// Calculate custom metrics
const pageStart = performance.getEntriesByName('page:start')[0]?.startTime ?? 0;
const feedVisible = performance.now();
analytics.logMetric('TimeToFeedVisible', {
value: feedVisible - pageStart,
});
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
}
// Component-level performance tracking
function usePerfMark(name: string) {
useEffect(() => {
performance.mark(`${name}:mount`);
return () => performance.mark(`${name}:unmount`);
}, [name]);
}
function TrackedStory({ story }: { story: StoryData }) {
usePerfMark(`story:${story.id}`);
const [isPending, startTransition] = useTransition();
return (
<article data-perf-tracked data-story-id={story.id}>
{/* ... */}
</article>
);
}
14. Accessibility at Scale
Facebook serves billions of users including millions with disabilities:
// Accessibility infrastructure
// Focus management system
class FocusManager {
private focusStack: HTMLElement[] = [];
private announcementQueue: string[] = [];
// Trap focus in modal/dialog
trapFocus(container: HTMLElement) {
const previousFocus = document.activeElement as HTMLElement;
this.focusStack.push(previousFocus);
// Find focusable elements
const focusableElements = this.getFocusableElements(container);
if (focusableElements.length === 0) return;
// Focus first element
focusableElements[0].focus();
// Setup trap
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
};
container.addEventListener('keydown', handleKeyDown);
// Return cleanup function
return () => {
container.removeEventListener('keydown', handleKeyDown);
const previousElement = this.focusStack.pop();
previousElement?.focus();
};
}
// Announce to screen readers
announce(message: string, priority: 'polite' | 'assertive' = 'polite') {
const announcer = document.getElementById(`aria-announcer-${priority}`);
if (announcer) {
announcer.textContent = message;
// Clear after announcement
setTimeout(() => {
announcer.textContent = '';
}, 1000);
}
}
private getFocusableElements(container: HTMLElement): HTMLElement[] {
const selector = [
'button:not([disabled])',
'a[href]',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(', ');
return Array.from(container.querySelectorAll(selector));
}
}
// Accessible components
interface AccessibleButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
isLoading?: boolean;
loadingText?: string;
}
function AccessibleButton({
children,
isLoading,
loadingText = 'Loading',
disabled,
...props
}: AccessibleButtonProps) {
return (
<button
{...props}
disabled={disabled || isLoading}
aria-disabled={disabled || isLoading}
aria-busy={isLoading}
>
{isLoading ? (
<>
<Spinner aria-hidden="true" />
<span className="sr-only">{loadingText}</span>
</>
) : (
children
)}
</button>
);
}
// Accessible modal
function AccessibleModal({
isOpen,
onClose,
title,
children,
}: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
const focusManager = useFocusManager();
useEffect(() => {
if (isOpen && modalRef.current) {
const cleanup = focusManager.trapFocus(modalRef.current);
focusManager.announce(`Dialog opened: ${title}`);
return () => {
cleanup();
focusManager.announce('Dialog closed');
};
}
}, [isOpen, title, focusManager]);
// Close on Escape
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}
}, [isOpen, onClose]);
if (!isOpen) return null;
return createPortal(
<div
className="modal-overlay"
onClick={onClose}
aria-hidden="true"
>
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className="modal"
onClick={(e) => e.stopPropagation()}
>
<h2 id="modal-title">{title}</h2>
{children}
<button
onClick={onClose}
aria-label="Close dialog"
className="modal-close"
>
×
</button>
</div>
</div>,
document.body
);
}
// Screen reader announcements
function AriaAnnouncer() {
return (
<>
<div
id="aria-announcer-polite"
aria-live="polite"
aria-atomic="true"
className="sr-only"
/>
<div
id="aria-announcer-assertive"
aria-live="assertive"
aria-atomic="true"
className="sr-only"
/>
</>
);
}
// Skip links for keyboard navigation
function SkipLinks() {
return (
<nav aria-label="Skip links" className="skip-links">
<a href="#main-content" className="skip-link">
Skip to main content
</a>
<a href="#news-feed" className="skip-link">
Skip to News Feed
</a>
<a href="#search" className="skip-link">
Skip to search
</a>
</nav>
);
}
15. Architecture Evolution
┌─────────────────────────────────────────────────────────────────────────────┐
│ FACEBOOK FRONTEND EVOLUTION │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 2004 2009 2011 2015 2017 2020 2023 │
│ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ PHP │ │ Big │ │React│ │React│ │Relay│ │React│ │React│ │
│ │ HTML│ │Pipe │ │Birth│ │Native│ │Modern│ │Fiber│ │ RSC │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ │
│ INNOVATIONS BORN AT FACEBOOK: │
│ ───────────────────────────── │
│ 2009: BigPipe (streaming HTML) │
│ 2011: XHP (PHP templating) │
│ 2013: React (component model) │
│ 2012: GraphQL (query language) │
│ 2015: Relay (data fetching) │
│ 2015: React Native (mobile) │
│ 2016: Yarn (package manager) │
│ 2017: React Fiber (reconciliation) │
│ 2019: Hermes (JS engine) │
│ 2021: React Server Components │
│ 2023: Stylex (atomic CSS) │
│ │
│ KEY METRICS IMPROVEMENT: │
│ ──────────────────────── │
│ Time to Interactive: 12s → 3s → 1.5s │
│ Bundle size per route: 2MB → 500KB → 150KB │
│ Re-render time: 100ms → 16ms → 4ms │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
16. Lessons & Tradeoffs
Key Architectural Decisions
1. Co-located Data Requirements (Relay)
// TRADEOFF: Tight coupling vs optimal data fetching
// Pro: Components declare exactly what they need
// Pro: No over-fetching or under-fetching
// Pro: Compile-time query optimization
// Con: Learning curve
// Con: Requires GraphQL infrastructure
// Con: Fragment composition complexity
// Decision: Worth it at scale - data efficiency matters
// when serving billions of requests
2. Atomic CSS (Stylex)
// TRADEOFF: Developer experience vs bundle size
// Pro: Constant CSS size regardless of codebase growth
// Pro: No specificity wars
// Pro: Type-safe styles
// Con: Requires build tooling
// Con: Class names not human-readable
// Con: Learning curve
// Decision: Essential at Facebook scale
// Traditional CSS would be 10MB+, Stylex keeps it under 200KB
3. Incremental Loading
// TRADEOFF: Initial load speed vs code organization
// Pro: Fast initial load (50KB shell)
// Pro: Pay only for what you use
// Con: Complexity in dependency management
// Con: Waterfall requests if not careful
// Con: Flash of loading states
// Decision: Non-negotiable at scale
// Full bundle would be 100MB+
Conclusion
Facebook's frontend represents 20 years of continuous innovation at unprecedented scale. Key takeaways:
-
Invent When Necessary: React, GraphQL, Relay, Stylex—Facebook creates tools when existing solutions don't scale.
-
Co-location is Key: Data requirements, styles, and logic live together in components for maintainability.
-
Performance is Product: Every millisecond matters when serving billions. Optimizations compound.
-
Privacy is Architecture: Permission checks are woven into the data layer, not bolted on.
-
Incremental Everything: Loading, rendering, updating—break everything into small, schedulable pieces.
Facebook continues to push the boundaries of what's possible in frontend engineering, and their open-source contributions have fundamentally shaped how we build web applications.
Current production metrics:
- Time to Interactive: ~1.5s (median)
- News Feed LCP: ~2.0s
- Comment INP: ~100ms
- JS Bundle (initial): ~150KB
- Concurrent Users Supported: 50M+
What did you think?