Design a GitHub-Scale Repo Hosting System
Design a distributed system that hosts 200M+ repositories at GitHub scale.
Introduction
30 Second Summary
Every time you push code to GitHub, your changes are instantly replicated across multiple servers, searchable across 200 million repositories, and available to collaborators worldwide. Behind this seamless experience is one of the most sophisticated distributed systems ever built.
In this project, you will design the architecture for a GitHub-scale repository hosting platform from scratch. You will start with a single-server design, watch it fail, and rebuild it layer by layer using the same distributed systems techniques GitHub actually uses.
What You'll Build
A diagram-rich system design document you can walk someone through, showing exactly how GitHub stores, replicates, and serves 200M+ repositories while keeping data safe and collaboration fast.
By the end of this project, you'll have:
- A high-level architecture showing how five independent subsystems (Git transport, core app, code search, CI fleet, event system) work together to form one platform, with Mermaid diagrams you can render and share.
- A replication flow demonstrating how writes are committed to three replicas using three-phase commit and quorum writes, so no single server failure loses data.
- A code search pipeline design using trigram indexing that can search across 200M+ repositories without scanning every file.
- Secret Mission: Design the failure detection and auto-repair system that keeps GitHub available when servers inevitably go down.
Are there any prerequisites?
None. This guide includes setup for all required tools. You just need VS Code installed on your computer.
Before We Start
Before We Start...
Before we dive in, let's lock in what you're about to build and why it matters for your growth as an engineer.
Set Up Your Environment
You are about to design a full system architecture for a platform that serves 200 million repositories. To document and communicate that design, you need a tool that renders architecture diagrams instantly as you write them.
In this step, you will verify that VS Code can render Mermaid diagrams natively, create your project folder, and set up the document skeleton you will fill in throughout this project.
In this step, get ready to:
- Verify VS Code supports built-in Mermaid rendering.
- Create your project folder and test your first diagram.
- Set up the document header and table of contents skeleton.
Verify your VS Code version
VS Code added built-in Mermaid diagram rendering in version 1.121. If your version is 1.121 or higher, diagrams render automatically in the Markdown preview with no extensions needed.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter to open it.
- On macOS, click Code in the top menu bar, then click About Visual Studio Code.
- On Windows or Linux, click Help in the top menu bar, then click About.
- Look at the version number in the dialog that appears.
✔️ I see version 1.121 or higher
Your VS Code has built-in Mermaid support. No extensions needed.
- Close the About dialog and continue to the next substep.
ⓧ I see a version lower than 1.121
Your version does not include built-in Mermaid rendering. You have two options: update VS Code, or install the fallback extension.
The fastest option is to install the extension:
- Click the Extensions icon in the left sidebar (it looks like four squares).
- Search for Markdown Preview Mermaid Support.
- Find the extension by Matt Bierner and click Install.
- Once installed, Mermaid diagrams will render in the Markdown preview just like the built-in version.
Updating VS Code instead
If you prefer to update, click Help then Check for Updates (Windows/Linux) or Code then Check for Updates (macOS). Restart VS Code after the update completes.
Create your project folder and test Mermaid rendering
Before diving into system design, you need to confirm that Mermaid diagrams actually render visually in your preview. A quick test now saves confusion later.
- Open the integrated terminal by pressing Ctrl+` (the backtick key, above Tab on most keyboards).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder by running this command:
mkdir github-system-design
- Open this folder in VS Code by clicking File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the github-system-design folder, and click Open.
What just happened?
VS Code now treats github-system-design as your workspace root. Any files you create will live inside this folder, and the integrated terminal will start here automatically.
- Create a new file by clicking File then New File.
- When prompted for a name, type github-system-design.md and press Enter.
- Paste the following test content into the file:
# Test Diagram
```mermaid
flowchart TD
A[Server] --> B[Database]
```
What does this code do?
The ```mermaid fence tells the Markdown preview to render the contents as a diagram instead of plain text.
The flowchart TD declaration creates a top-down flowchart. The --> arrow draws a connection from one node to the next.
- Save the file by pressing Cmd+S (macOS) or Ctrl+S (Windows).
Before you open the preview, what do you think you will see? Raw code text, or a visual diagram with boxes and an arrow?
- Open the Markdown preview by pressing Cmd+Shift+V (macOS) or Ctrl+Shift+V (Windows).
You should see a visual flowchart with a "Server" box connected by an arrow to a "Database" box. If you see the actual diagram rendered as shapes and lines instead of raw code text, Mermaid rendering is working.
Seeing raw code instead of a diagram?
- Make sure you saved the file before opening the preview. Unsaved changes will not render.
- Double-check that your fenced code block starts with exactly ```mermaid (three backticks followed by the word mermaid, no spaces).
- If you are on a VS Code version below 1.121 and skipped installing the extension, go back and install Markdown Preview Mermaid Support from the Extensions panel.
Still stuck? Why isn't my Mermaid diagram rendering in the VS Code preview?
Add the document header and table of contents
Your Mermaid rendering works. Now replace the test content with the real document skeleton that you will fill in step by step throughout this project.
- Click back into the editor tab for github-system-design.md (not the preview tab).
- Select all the test content by pressing Cmd+A (macOS) or Ctrl+A (Windows).
- Replace it by pasting the following document structure:
# GitHub System Design: Repository Hosting at Scale
## Table of Contents
1. [Naive Design and Failure Analysis](#naive-design-and-failure-analysis)
2. [Storage and Replication with Spokes](#storage-and-replication-with-spokes)
3. [The Collaboration Layer](#the-collaboration-layer)
4. [Code Search and CI at Scale](#code-search-and-ci-at-scale)
5. [System Summary](#system-summary)
---
What does this structure do?
This is your document skeleton. Each linked heading in the Table of Contents maps to a major section you will build in the coming steps.
The --- at the bottom creates a horizontal rule, visually separating the table of contents from the design sections that follow.
- Save the file by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Switch to the Markdown preview tab. You should see a large heading that reads "GitHub System Design: Repository Hosting at Scale" followed by a numbered list of clickable section links and a horizontal line.
Preview not updating?
- Make sure you saved the file. The preview updates on save.
- If the preview tab closed, reopen it with Cmd+Shift+V (macOS) or Ctrl+Shift+V (Windows).
- Confirm you are editing github-system-design.md and not a different file. The filename appears in the tab at the top of the editor.
Still stuck? My Markdown preview is not showing my updated content after saving.
✔️ Awesome, I've got everything!
Great. Double check you have saved github-system-design.md and that the preview shows your heading, numbered list, and horizontal rule.
ⓧ I'd like to double check the full code
Your github-system-design.md should contain exactly this:
# GitHub System Design: Repository Hosting at Scale
## Table of Contents
1. [Naive Design and Failure Analysis](#naive-design-and-failure-analysis)
2. [Storage and Replication with Spokes](#storage-and-replication-with-spokes)
3. [The Collaboration Layer](#the-collaboration-layer)
4. [Code Search and CI at Scale](#code-search-and-ci-at-scale)
5. [System Summary](#system-summary)
---
Your environment is ready and your document skeleton is in place. Next up, you will design a naive single-server architecture and watch it crumble under real-world demands.
Design the Naive Architecture
Your environment is ready and Mermaid diagrams are rendering in VS Code's preview. Now it is time to start designing.
Every good distributed system starts by understanding what a bad one looks like. In this step, you will design the simplest possible architecture for GitHub and then systematically document why it cannot survive at scale. The failures you uncover here become the motivation for every technique in the steps ahead.
In this step, get ready to:
- Diagram a naive single-server architecture that handles all traffic on one machine.
- Analyze specific failure scenarios and document their consequences.
- Define the real requirements and document GitHub's CAP theorem trade-off.
Design and diagram a single-server architecture
The simplest imaginable design puts everything on one machine: Git storage, the web application, the database, and all user traffic. This is where most systems start before they need to scale.
- In github-system-design.md, place your cursor after the --- separator line at the end of the file.
- Add the section heading and a Mermaid flowchart showing the single-server architecture by pasting this below the separator:
## Naive Design and Failure Analysis
### Single-Server Architecture
```mermaid
flowchart TD
Dev[Developer] -->|SSH/HTTPS| LB[Load Balancer]
LB --> App[Application Server]
App --> Git[Git Storage - Single Disk]
App --> DB[(Database)]
```
What does this diagram show?
The flowchart TD creates a top-down diagram. A Developer connects over SSH or HTTPS to a Load Balancer, which routes to a single Application Server.
That server connects to one Git Storage disk (using square brackets for a rectangle) and one Database (using [( )] for a cylinder shape). Everything depends on a single machine.
- Save github-system-design.md.
- Check your Markdown preview (Ctrl+Shift+V on Windows or Cmd+Shift+V on macOS). You should see a rendered flowchart with boxes connected by arrows, not raw text.
Diagram showing as raw text?
Make sure you typed the opening ```mermaid and closing ``` exactly, with three backticks on each line. A missing backtick breaks the code fence.
If you are on VS Code below 1.121, install the "Markdown Preview Mermaid Support" extension by Matt Bierner as a fallback.
Help me fix my Mermaid diagram rendering
Now add the scale targets that define what "GitHub scale" actually means. These numbers establish the bar this architecture must clear.
- Add the following Scale Targets table directly below the closing ``` of your Mermaid diagram:
### Scale Targets
| Metric | Target |
|--------|--------|
| Repositories | 200M+ |
| Users | 100M+ |
| Git operations/day | 1B |
| Availability (git ops) | 99.99% |
| Clone/fetch latency | Less than 5s median |
| Push latency | Less than 2s |
What do these numbers mean?
These are the real scale targets for a GitHub-level platform. 200M+ repositories with 1 billion Git operations per day means the system handles roughly 11,500 requests per second on average.
The 99.99% availability target means at most 52 minutes of downtime per year. A single server cannot realistically achieve this.
- Save github-system-design.md and check your preview. You should see the rendered flowchart above and a formatted table below it with two columns.
Analyze why this design fails
Look at the single-server diagram you just drew. Every arrow passes through one machine, and every piece of data lives on one disk.
- Add the following Failure Analysis table below the Scale Targets section. Each row documents a specific failure and its consequence in terms of data loss or downtime:
### Failure Analysis
| Failure Scenario | Consequence |
|-----------------|-------------|
| Disk failure | All repository data lost permanently. No redundancy means no recovery. |
| Server crash | All repositories inaccessible. Single point of failure means complete downtime. |
| Disk I/O bottleneck | Cannot serve 1B operations/day from a single machine. Performance degrades for all users. |
| Network partition | Server becomes unreachable from some regions. No alternative path exists. |
| Database corruption | All metadata (users, PRs, permissions) lost. No secondary copy. |
Why document failures explicitly?
Each row in this table maps directly to a distributed systems technique you will learn in the next step. "Disk failure" motivates replication. "Single point of failure" motivates multiple replicas. "I/O bottleneck" motivates horizontal scaling.
By naming the failures first, you build a mental map of WHY distributed systems exist. The techniques are direct answers to specific problems.
- Save github-system-design.md and check your preview. You should see the Failure Analysis table with five rows, each naming a scenario and its consequence.
Table not rendering properly?
Markdown tables need exact alignment of the | characters. Make sure the header separator row (|---|---|) has the same number of columns as the header.
Help me fix my Markdown table
Document requirements and the CAP trade-off
The failure analysis proves the naive design cannot work. Now you need to state what the system actually requires, and acknowledge the fundamental trade-off that constrains every distributed system.
- Add the Requirements section below your Failure Analysis table:
### Requirements
The system needs:
- **Durability**: No data loss after a confirmed write
- **Availability**: 99.99% for git operations (roughly 52 minutes downtime per year)
- **Consistency**: All users see the same repository state after a write completes
- **Low latency**: Clone/fetch under 5s median, push under 2s
Why state requirements separately from failures?
The failure analysis tells you what goes wrong. The requirements section tells you what "right" looks like. You need both because a fix that solves one failure (replication for durability) might violate another requirement (low latency) if not designed carefully.
- Save and check your preview. You should see a bold list of four requirements below the failure table.
Here is the hard truth: you cannot have all four requirements perfectly. The CAP theorem forces a trade-off between consistency and availability when network partitions happen.
- Add the CAP Theorem Trade-off section directly below your Requirements section:
### CAP Theorem Trade-off
The CAP theorem states that a distributed system cannot simultaneously guarantee Consistency, Availability, and Partition tolerance. You must choose two.
GitHub's choice: **Consistency + Partition Tolerance (CP)**
- Writes are rejected if a quorum of replicas cannot confirm the update
- This means brief write unavailability during network partitions
- But it guarantees no data corruption or inconsistency between replicas
- Users never see a stale or conflicting repository state
What does CP mean in practice?
When a network partition happens (servers cannot talk to each other), GitHub chooses to reject writes rather than risk two servers accepting conflicting changes. Users might see "push failed, try again" for a few seconds.
The alternative (AP) would accept all writes during a partition and try to merge them later. For source code, that means two developers could push conflicting changes to the same branch and both succeed. Merging them automatically is dangerous and often impossible.
- Save github-system-design.md.
Before you check the preview, think about this: does the naive single-server design you diagrammed at the top of this section meet ANY of the four requirements you just wrote?
Check your preview now. The answer is no. A single disk means zero durability after disk failure. A single server means zero availability after a crash. The naive design fails every requirement. That gap is exactly what you will close in the next step.
Section not appearing in preview?
Make sure there is a blank line between the end of the Requirements list and the ### CAP Theorem Trade-off heading. Markdown headings need a blank line before them to render correctly.
Help me fix my Markdown heading rendering
✔️ Awesome, I've got everything!
Great. Double check you have saved github-system-design.md and your preview shows all five subsections: the flowchart diagram, Scale Targets table, Failure Analysis table, Requirements list, and CAP Theorem Trade-off.
ⓧ I'd like to double check the full code
Scroll through your file and confirm these sections are present in order, each below the --- separator:
- The document header (# GitHub System Design: Repository Hosting at Scale) and Table of Contents (from Step 1).
- ### Single-Server Architecture with the flowchart TD Mermaid block showing Dev, LB, App, Git, and DB nodes.
- ### Scale Targets with a 6-row table (Repositories through Push latency).
- ### Failure Analysis with a 5-row table (Disk failure through Database corruption).
- ### Requirements with four bold bullet points (Durability, Availability, Consistency, Low latency).
- ### CAP Theorem Trade-off with GitHub's CP choice and four bullet points explaining the trade-off.
If any section is missing, scroll up to the corresponding chunk in this step and re-paste it.
You have documented a complete failure analysis of the naive design. In the next step, you will build the distributed storage layer that solves every failure you just identified.
Build the Spokes Replication System
The naive design's fatal flaw is now documented: one disk failure means permanent data loss because there is only one copy of everything.
GitHub solved this with a system called Spokes. It keeps three replicas of every repository on separate machines, spread across different server racks. A three-phase commit protocol ensures all replicas stay in sync before confirming a write to the user.
In this step, get ready to:
- Design the three-replica storage architecture with Spokes.
- Document the write path using three-phase commit.
- Design the read path with availability guarantees.
Design the three-replica architecture
The single-server design stored everything on one disk. The Spokes architecture replaces that single disk with three file servers, each on a different rack, all coordinated by a Git Proxy.
- In github-system-design.md, scroll to the bottom of the file (after the CAP Theorem Trade-off section).
- Add a horizontal rule --- followed by the new section heading ## Storage and Replication with Spokes and the subheading ### Three-Replica Architecture.
- Below the subheading, add a Mermaid code fence (triple backticks followed by mermaid), then paste the following diagram content inside it:
flowchart TD
subgraph Edge["Edge Layer"]
Client[Git Client]
end
subgraph Application["Application Tier"]
Proxy[Git Proxy]
end
subgraph Metadata["Metadata Tier"]
MetaDB[(MySQL / Vitess)]
end
subgraph Storage["Spokes Storage Tier"]
FS1[File Server 1 - Rack A]
FS2[File Server 2 - Rack B]
FS3[File Server 3 - Rack C]
end
Client -->|push/fetch| Proxy
Proxy -->|lookup replica locations| MetaDB
Proxy -->|read/write| FS1
Proxy -->|read/write| FS2
Proxy -->|read/write| FS3
- Close the Mermaid fence with triple backticks on their own line.
What does this diagram show?
The diagram uses Mermaid subgraphs to group components into four architectural layers. The Git Proxy sits in the Application Tier and acts as the single entry point for all git operations.
The Proxy queries a Metadata DB (MySQL sharded via Vitess) to discover which three file servers hold a given repository. Each file server lives on a separate rack (A, B, C) so a rack-level power failure only takes down one replica.
- Below the closed Mermaid fence, add the following explanation text that documents the design decisions behind this architecture:
**Why plain Git on local SSDs?**
Git is extremely sensitive to disk latency. A single `git log` or `git blame` can touch thousands of objects sequentially. Any abstraction layer (distributed filesystem, SAN, network block storage) that adds even 1ms of latency per object lookup becomes a serious performance tax. Spokes lives one layer above the filesystem and treats each replica as a normal bare Git repository it communicates with over Git protocols.
**Rack-level separation**: No two replicas of the same repository share a rack. A power or top-of-rack network failure takes down an entire rack, so this placement ensures one rack failure never loses more than one replica.
Why not a distributed filesystem?
You might expect GitHub to use something like HDFS or Ceph. The reason they do not is that Git's access pattern (touching thousands of small objects in sequence during a single git log) makes any network round-trip per object catastrophic for latency. Plain local SSDs eliminate that overhead entirely.
- Save github-system-design.md.
- Switch to your Markdown preview. You should see the three-replica flowchart rendered as a visual diagram with four labeled boxes (Edge Layer, Application Tier, Metadata Tier, Spokes Storage Tier) connected by arrows.
Diagram showing as raw text?
Make sure your Mermaid code fence starts with exactly three backticks followed by mermaid (no space before the word). Check that it ends with exactly three backticks on their own line.
If you are on VS Code below version 1.121, you need the "Markdown Preview Mermaid Support" extension installed from Step 1.
Help me fix my Mermaid diagram rendering
Design the write path using three-phase commit
The flowchart shows the static architecture. Now you need to show how a write actually flows through the system, step by step, using a sequence diagram.
- Below the rack-level separation paragraph, add the subheading ### Write Path: Three-Phase Commit.
- Add a Mermaid code fence and paste the following sequence diagram content inside it:
sequenceDiagram
participant C as Client
participant P as Proxy
participant M as Metadata DB
participant F1 as File Server 1
participant F2 as File Server 2
participant F3 as File Server 3
C->>P: git push (pack data)
P->>M: Which servers hold this repo?
M-->>P: FS1, FS2, FS3
Note over P,F3: Phase 1 - Prepare
P->>F1: Prepare (ref updates)
P->>F2: Prepare (ref updates)
P->>F3: Prepare (ref updates)
F1-->>P: Ready
F2-->>P: Ready
F3-->>P: Timeout/Fail
Note over P,F3: Phase 2 - Commit (quorum reached: 2/3)
P->>F1: Commit
P->>F2: Commit
Note over P,F3: Phase 3 - Acknowledge
F1-->>P: Committed
F2-->>P: Committed
P-->>C: Push successful
Note over P,F3: F3 marked unhealthy, repair scheduled
- Close the Mermaid fence with triple backticks on their own line.
What happens in each phase?
- Phase 1 (Prepare): The Proxy sends the proposed ref updates to all three file servers and waits for confirmation that each can apply the change.
- Phase 2 (Commit): Once at least 2 of 3 servers respond "Ready" (a quorum), the Proxy tells them to commit. The third server timed out, so it is skipped.
- Phase 3 (Acknowledge): The committed servers confirm success. The Proxy acknowledges the push to the client. The failed server is flagged for automatic repair later.
- Save github-system-design.md and check your Markdown preview.
- You should see a vertical sequence diagram with six participants across the top and arrows flowing downward through the three labeled phases.
Sequence diagram not rendering?
Check that the first line inside your code fence is exactly sequenceDiagram with no leading spaces. Mermaid is strict about indentation on the declaration line.
Verify that arrows use the correct syntax: ->> for solid arrows and -->> for dotted return arrows. A single missing > breaks the entire diagram.
Help me troubleshoot my sequence diagram
Document the read path and availability guarantees
The write path shows how data gets stored safely. The read path shows how the system serves data quickly and survives server failures.
- Below the closed sequence diagram fence, add the following content that documents the quorum rule, read path behavior, and availability guarantees:
**Quorum rule**: A write is only accepted if a strict majority (at least 2 of 3 replicas) can apply the change and produce the same result. If quorum cannot be reached, the write is rejected entirely. Any replica that disagrees with the majority is marked unhealthy and scheduled for automatic repair.
### Read Path
For reads (clone, fetch), the Proxy:
1. Queries metadata for available replicas
2. Ranks replicas by latency (nearest first)
3. Routes to the closest in-sync replica
4. If that replica fails, fails over to the next in the list
**Availability guarantees**:
| Servers Down | Impact |
|-------------|--------|
| 1 of 3 | No user-visible effect. Full read/write. |
| 2 of 3 | Read-only for affected repos (no quorum for writes). |
| 3 of 3 | Repository unavailable (requires entire rack + more). |
Why does 1 server down have no user-visible effect?
With three replicas on separate racks, losing one server still leaves two healthy copies. Two servers are enough for a quorum write (strict majority of 2/3). Reads fail over to the next replica in the sorted list, so the client never notices.
Only when two servers go down does the system lose write capability. It can still serve reads from the one remaining replica, which is why it degrades to read-only rather than going fully offline.
Before you check the preview, predict what you expect to see in the rendered output for the availability table.
- Save github-system-design.md and check your Markdown preview.
- You should see the quorum rule in bold, a numbered list for the read path, and a formatted table with three rows showing the impact of 1, 2, and 3 server failures.
Table not rendering correctly?
Markdown tables require the header separator row (|---|---|) immediately after the header row. If you are missing this line or have extra spaces, the table renders as plain text.
Each row must have the same number of pipe characters. Count that every row has exactly 3 pipes (start, middle, end).
Help me fix my Markdown table formatting
✔️ Awesome, I've got everything!
Great. Double check you have saved github-system-design.md and that your preview shows both diagrams rendering correctly.
ⓧ I'd like to double check the full code
Your file is now quite long. Scroll through and confirm these sections appear in order:
- The original ## Naive Design and Failure Analysis section (from the previous step) with its flowchart, tables, and CAP trade-off.
- A horizontal rule --- separator.
- The ## Storage and Replication with Spokes heading.
- The ### Three-Replica Architecture subheading with its Mermaid flowchart showing Edge, Application, Metadata, and Storage subgraphs.
- The "Why plain Git on local SSDs?" and "Rack-level separation" explanation paragraphs.
- The ### Write Path: Three-Phase Commit subheading with its sequence diagram showing all six participants and three phases.
- The quorum rule paragraph in bold.
- The ### Read Path subheading with the numbered list and availability guarantees table.
Your document now has a complete replication design. The three-phase commit protocol guarantees that confirmed writes exist on at least two servers. Next up, you will design the collaboration layer that makes GitHub more than just replicated file storage.
Add the Collaboration Layer
The Spokes storage tier you built in the last step solves the data durability problem. Three replicas on separate racks mean no single failure loses a repository.
But GitHub is not just replicated file storage. Its core value is collaboration: Pull Requests, code review, webhooks, permissions, and CI integrations. All of these live in a separate metadata and event layer that sits alongside Spokes. In this step, you will design that collaboration layer and show how events flow from a push all the way through to a merged PR.
In this step, get ready to:
- Design the full system architecture diagram with all six subsystem layers.
- Map the complete Push-to-PR-to-Merge lifecycle as a sequence diagram.
- Document why GitHub chose MySQL/Vitess for the metadata tier.
Design the full system architecture
Your Spokes diagram from the last step showed a single application tier talking to storage. The real architecture has six distinct layers, each with a clear responsibility.
- In github-system-design.md, add a horizontal rule and the new section heading after the Spokes section by copying the following content below the availability guarantees table:
---
## The Collaboration Layer
### Full System Architecture
```mermaid
flowchart TD
subgraph Edge["Edge Layer"]
DNS[DNS / Anycast]
LB[Load Balancer]
end
subgraph App["Application Tier"]
Auth[Auth - SSH Keys / PAT / OAuth]
API[Rails + Go API]
GitProxy[Git Proxy]
end
subgraph Storage["Spokes Storage"]
FS1[File Server 1]
FS2[File Server 2]
FS3[File Server 3]
end
What are these subgraphs?
Each subgraph groups related components into a named box in the rendered diagram. Edge handles incoming traffic, Application handles business logic and authentication, and Storage holds the three Spokes replicas you designed in the previous step.
- Directly below the code you just added (with no blank line between), add the remaining three subsystem layers plus all the connections that wire the architecture together:
subgraph Meta["Metadata Tier"]
MySQL[(MySQL / Vitess)]
end
subgraph Events["Event System"]
Bus[Event Bus]
Webhooks[Webhook Delivery]
Notify[Notifications]
end
subgraph LFS["Large File Storage"]
ObjStore[Object Storage]
end
DNS --> LB
LB --> Auth
Auth --> API
Auth --> GitProxy
API --> MySQL
GitProxy --> FS1
GitProxy --> FS2
GitProxy --> FS3
GitProxy -->|lookup| MySQL
GitProxy -->|push event| Bus
Bus --> Webhooks
Bus --> Notify
API --> ObjStore
```
**Key insight**: The Git Proxy publishes events to the Event Bus after a quorum commit succeeds. The file servers do not know what a webhook is. This separation keeps the storage tier simple and focused on one job: storing and replicating Git data.
Why does the Git Proxy publish events?
The connection arrows tell the full story. The Git Proxy talks directly to Spokes for reads and writes, but it also publishes a push event to the Event Bus after a successful quorum commit. This means the file servers never need to know about webhooks, notifications, or CI. They just store Git data.
The Event Bus then fans out to webhook delivery and notifications independently. Adding a new consumer (like a code search indexer) means subscribing to the bus, not modifying the storage tier.
- Save github-system-design.md.
- Open the Markdown preview with Ctrl+Shift+V (Windows/Linux) or Cmd+Shift+V (macOS).
- Scroll down to the new section. You should see a rendered flowchart with six labelled boxes (Edge, Application Tier, Spokes Storage, Metadata Tier, Event System, Large File Storage) connected by directional arrows.
Diagram shows as raw text?
If you see the Mermaid source code instead of a rendered diagram, your VS Code version may be below 1.121. Check your version under Help > About. If it is below 1.121, update VS Code or install the "Markdown Preview Mermaid Support" extension as a fallback.
Also confirm your code fence uses exactly ```mermaid (three backticks followed by the word mermaid, no space). A typo here prevents rendering.
Help me debug why my Mermaid diagram is not rendering in VS Code's Markdown preview.
Design the Pull Request lifecycle
The architecture diagram shows which components exist. Now you need to show how they interact over time when a developer pushes code, opens a PR, and merges it.
- Below the "Key insight" paragraph you just added, paste the following sequence diagram heading and the first half of the lifecycle (push through CI reporting):
### Push-to-PR-to-Merge Lifecycle
```mermaid
sequenceDiagram
participant Dev as Developer
participant Proxy as Git Proxy
participant Spokes as Spokes (3 replicas)
participant Bus as Event Bus
participant CI as External CI
participant App as API Server
participant DB as MySQL
Dev->>Proxy: git push feature-branch
Proxy->>Spokes: Three-phase commit
Spokes-->>Proxy: Quorum confirmed
Proxy-->>Dev: Push successful
Proxy->>Bus: Publish "push" event
Bus->>CI: Webhook POST (HMAC-signed)
CI-->>App: Report status via Checks API
App->>DB: Store check result
What does this flow show?
The push travels through the same three-phase commit you designed in the Spokes section. After quorum confirms, the Proxy publishes a push event to the Event Bus. The Bus delivers an HMAC-signed webhook to an external CI system, which reports results back through the Checks API.
Notice the Proxy does not wait for CI to finish. The push is already confirmed before the webhook fires. This keeps push latency low while still triggering downstream automation.
- Directly below (still inside the same mermaid block), add the PR creation, review, and merge flow to complete the lifecycle:
Dev->>App: Open Pull Request
App->>DB: Create PR record (source, target, author)
Note over App,DB: Code review happens
Dev->>App: Request merge
App->>DB: Verify: approvals met, checks passed, branch protection satisfied
App->>Proxy: Merge commit push
Proxy->>Spokes: Three-phase commit (merge)
Spokes-->>Proxy: Quorum confirmed
Proxy->>Bus: Publish "merge" event
Bus->>CI: Webhook POST (merge notification)
```
Why does the merge go back through the Proxy?
A merge is just another Git write. The API server does not write directly to Spokes. It sends the merge commit through the Git Proxy so the same three-phase commit and quorum rules apply. This guarantees the merge is durable across all three replicas before the user sees "Merged".
The Note over App,DB: Code review happens line represents an asynchronous human process. Mermaid renders it as a highlighted annotation spanning the two participants.
- Save github-system-design.md and check the Markdown preview.
- You should see a sequence diagram with seven participants across the top and message arrows flowing downward from push through CI, PR creation, review, merge, and final webhook delivery.
Sequence diagram not rendering?
Make sure the closing ``` (three backticks) sits on its own line immediately after the last message line. Any extra indentation on the closing fence breaks the block.
Also check that ->> (solid arrow) and -->> (dotted arrow) are typed correctly. A missing or extra hyphen produces a parse error.
Help me fix my Mermaid sequence diagram that is not rendering.
Document the metadata tier design decisions
The sequence diagram shows metadata being stored in MySQL. A reader of your design document will want to know why you chose a relational database over a NoSQL alternative for this layer.
- Below the closing fence of your sequence diagram, add the metadata design rationale section:
### Metadata Tier Design Decisions
**Why MySQL (sharded via Vitess)?**
Pull Requests involve complex relationships:
- Author, reviewers, assignees (user references)
- Comments anchored to specific file + line + commit SHA
- Check statuses from multiple CI systems
- Branch protection rules with multiple conditions
- Merge queue ordering and conflict detection
These relationships benefit from:
- Relational schema with foreign keys and joins
- Transactional guarantees (ACID) for operations like merge
- Mature tooling for horizontal sharding (Vitess)
**Partitioning strategy**: Functionally partitioned (separate tables for repos, PRs, users, permissions) and horizontally sharded within each partition. Not tied to a single shard key.
What is Vitess?
Vitess is a database clustering system for horizontal scaling of MySQL. It lets you shard a single logical database across many physical MySQL instances while presenting a unified query interface to the application. GitHub uses it to handle the scale of 100M+ users without rewriting their data layer for a distributed database.
- Save github-system-design.md and check the Markdown preview.
- Scroll to the bottom. You should see the "Metadata Tier Design Decisions" heading followed by two bulleted lists (complex relationships and benefits) and a bold partitioning strategy summary.
Bullet lists not rendering as lists?
Markdown requires a blank line before the first - item in a list. If your bullets render as plain text with dashes, add a blank line between the introductory text ("Pull Requests involve complex relationships:") and the first bullet.
Help me fix my Markdown bullet lists that are not rendering correctly.
Before you move on, think about this: what happens if the metadata tier goes down but Spokes is still healthy? Do users lose access to their code?
They do not. Git operations (push, clone, fetch) go through the Proxy directly to Spokes. The metadata tier only affects collaboration features like PRs and issues. This separation is a deliberate availability trade-off.
✔️ Awesome, I've got everything!
Great. Confirm you have saved github-system-design.md and that all three diagrams in this section render correctly in preview.
ⓧ I'd like to double check the full code
Scroll through your Collaboration Layer section and confirm these chunks are present in order:
- Section separator (---) and ## The Collaboration Layer heading.
- Full System Architecture heading with a flowchart TD diagram containing six subgraphs (Edge, App, Storage, Meta, Events, LFS) and 13 connection arrows.
- The bold "Key insight" paragraph about the Git Proxy publishing events.
- Push-to-PR-to-Merge Lifecycle heading with a sequenceDiagram containing seven participants and messages from push through merge webhook.
- Metadata Tier Design Decisions heading with the MySQL/Vitess rationale, two bullet lists, and the partitioning strategy line.
Your system design now shows how GitHub's collaboration features sit on top of the storage tier without tangling with it. Next up, you will tackle two specialized subsystems that operate at massive scale: code search across 200M+ repositories and CI/CD that safely runs untrusted code.
Design Code Search and CI
You now have a working repository hosting platform with collaboration features. But two critical capabilities remain unsolved: searching across 200M+ repositories and running untrusted CI code safely.
These workloads demand fundamentally different architectures from the core platform. In this step, you will design a trigram-based code search engine and a per-job VM isolation model for CI/CD.
In this step, get ready to:
- Design the code search system using trigram indexing.
- Design the CI/CD per-job VM isolation model.
- Write the system summary tying all five subsystems together.
Design the code search indexing pipeline
GitHub needs to search across trillions of lines of code in 200M+ repositories. Full-text scanning at that scale is impossible, so the system splits code into 3-character sequences called trigrams and indexes them in a sharded inverted index.
- Add the code search section heading and trigram indexing flowchart to your document by copying this below your Collaboration Layer section:
---
## Code Search and CI at Scale
### Code Search: Trigram Inverted Index
```mermaid
flowchart LR
subgraph Indexing["Indexing Pipeline"]
Push[Push Event] --> Extract[File Extraction]
Extract --> Trigram[Trigram Extraction]
Trigram --> Index[(Sharded Inverted Index)]
end
subgraph Query["Query Path"]
User[User Query] --> Parse[Query Parser]
Parse --> Lookup[Trigram Lookup]
Lookup --> Intersect[Intersect Posting Lists]
Intersect --> Verify[Verify Against Source]
Verify --> Rank[Rank and Return Results]
end
Index -.->|serves| Lookup
```
What does this diagram show?
The left-to-right flowchart shows two parallel pipelines. The indexing pipeline processes every push event by extracting files, splitting code into trigrams, and writing to the inverted index.
The query path handles searches by parsing the query into trigrams, looking them up in the index, intersecting the results to find candidate files, then verifying against real source code to eliminate false positives.
- Save github-system-design.md.
- Return to your Markdown preview. You should see a left-to-right flowchart with two subgraph boxes and a dotted line connecting the index to the lookup node.
Diagram showing as raw text?
Make sure your VS Code version is 1.121 or later (built-in Mermaid support). If you are on an older version, install the "Markdown Preview Mermaid Support" extension by Matt Bierner as a fallback.
Also check that your code fences use exactly ```mermaid (three backticks followed by the word mermaid, no space).
My Mermaid diagram is showing as raw text in VS Code Markdown preview. How do I fix this?
- Add the trigram explanation below the closing code fence of your flowchart:
**How trigram search works:**
A search for `handleRequest` generates these trigrams:
```
han, and, ndl, dle, leR, eRe, Req, equ, que, ues, est
```
The search engine:
1. Looks up each trigram in the inverted index (which files contain this 3-char sequence?)
2. Intersects the posting lists (which files contain ALL trigrams?)
3. Fetches candidate files from Git storage
4. Verifies the actual match against the source code (eliminates false positives)
5. Ranks results by relevance
**Why trigrams over full-text search?**
- Supports regex search without scanning every file
- Index is compact (every possible 3-char sequence is bounded)
- Fast intersection of posting lists narrows candidates quickly
- Works for any programming language without language-specific parsing
Why does this approach work at scale?
A trigram is any 3-character sequence from the source code. The total number of possible trigrams is bounded (at most 256^3 for ASCII), so the index stays compact regardless of how many repositories exist.
Intersecting posting lists is extremely fast because you are asking "which files contain ALL of these short sequences?" This narrows millions of files down to a handful of candidates before touching any actual source code.
- Save github-system-design.md and check the preview. You should see the trigram example rendered as a code block and the numbered search steps below it.
Design the CI/CD isolation model
GitHub Actions runs arbitrary code from any user on the internet. A container escape would compromise the host and potentially leak secrets from other jobs.
- Add the CI/CD section heading and per-job VM flowchart below your trigram explanation:
### CI/CD: Per-Job VM Isolation
```mermaid
flowchart TD
subgraph Orchestration["Orchestration"]
YAML[Workflow YAML] --> Parser[Parser]
Parser --> Matrix[Matrix Expansion]
Matrix --> Queue[Job Queue]
end
subgraph Scheduling["Scheduling"]
Queue --> Scheduler[Scheduler]
Scheduler --> Pool[Warm VM Pool]
end
subgraph Execution["Execution"]
Pool --> VM[Ephemeral VM]
VM --> Run[Execute Job]
Run --> Complete[Job Complete]
Complete --> Reimage[Reimage VM]
Reimage --> Pool
end
```
What does this diagram show?
The orchestration layer parses workflow YAML and expands matrix builds into individual jobs. Each job enters a queue, gets assigned to a pre-warmed VM from a pool, executes in complete isolation, then the VM is reimaged (wiped clean) before returning to the pool.
The cycle from pool to execution and back to pool is the key insight. Reimaging guarantees no state leaks between jobs from different users.
- Save and check the preview. You should see a top-down flowchart with three subgraph boxes showing the job lifecycle from YAML to reimaging.
Diagram not rendering correctly?
Confirm that the ```mermaid opening fence and the closing ``` are on their own lines with no leading spaces. Indented fences are treated as part of a code block, not as Mermaid.
My CI/CD flowchart is not rendering in VS Code Markdown preview. What could be wrong?
- Add the comparison table explaining why VMs are used instead of containers below the closing code fence of your CI/CD flowchart:
**Why per-job VMs instead of containers?**
| Concern | Containers | VMs |
|---------|-----------|-----|
| Isolation | Namespace-based (shared kernel) | Hardware-level (separate kernel) |
| Security | Kernel exploits can escape | Hypervisor boundary is stronger |
| Trust model | Suitable for trusted code | Safe for arbitrary untrusted code |
| Cleanup | Filesystem layers can leak state | Full reimage guarantees clean slate |
| Performance | Faster startup | Slightly slower, mitigated by warm pools |
CI runs arbitrary untrusted code from any GitHub user. The stronger security boundary of VM-level isolation justifies the performance cost. Pre-warmed VM pools mitigate the startup latency.
Why not just use containers?
Containers share the host kernel. A kernel exploit in one container can escape to the host and access other containers' data. For trusted internal workloads this risk is acceptable, but CI runs code from strangers.
VMs run a separate kernel behind a hypervisor boundary. Escaping a VM requires exploiting the hypervisor itself, which is a much smaller and more hardened attack surface.
- Save github-system-design.md and check the preview. You should see a comparison table with five rows covering isolation, security, trust model, cleanup, and performance.
Tie the subsystems together in a system summary
Your design document now covers Git storage, collaboration, code search, and CI. The final section ties all five subsystems into one coherent architecture view.
- Add the System Summary section with the five-subsystems table below your CI/CD content:
---
## System Summary
### Five Subsystems, One Platform
GitHub is five systems sharing identity and permissions:
| # | Subsystem | Role | Key Technology |
|---|-----------|------|----------------|
| 1 | Git Transport | Clone, push, fetch over SSH/HTTPS | Spokes (3 replicas, three-phase commit) |
| 2 | Core App | PRs, issues, reviews, permissions, APIs | Rails + Go, MySQL/Vitess |
| 3 | Code Search | Regex search across 200M+ repos | Trigram inverted index |
| 4 | CI Fleet | Run workflows in isolated environments | Per-job ephemeral VMs |
| 5 | Event System | Webhooks, notifications, indexer fan-out | Event bus with HMAC-signed payloads |
What does this table capture?
Each subsystem scales independently. Code search can add index shards without touching Git storage. The CI fleet can grow its VM pool without affecting the application tier.
The only thing they share is identity and permissions. A user authenticates once and that identity carries across all five systems.
- Save and check the preview. You should see a new "System Summary" heading with a five-row table listing each subsystem, its role, and key technology.
- Add the first part of the complete architecture diagram below your five-subsystems table:
### Complete Architecture
```mermaid
flowchart TD
subgraph Edge["Edge"]
DNS[DNS/Anycast] --> LB[Load Balancer]
end
subgraph Core["Core Application"]
Auth[Authentication]
API[API - Rails + Go]
GitProxy[Git Proxy]
end
subgraph GitStore["Git Storage - Spokes"]
FS1[FS1 - Rack A]
FS2[FS2 - Rack B]
FS3[FS3 - Rack C]
end
subgraph MetaStore["Metadata"]
DB[(MySQL/Vitess)]
Cache[Redis Cache]
end
subgraph Search["Code Search"]
Indexer[Trigram Indexer]
SearchIndex[(Search Index)]
end
- Continue adding the remaining subgraphs and all connections directly below (still inside the same Mermaid block):
subgraph CI["CI/CD"]
Orchestrator[Orchestrator]
VMPool[VM Pool]
end
subgraph EventSys["Events"]
EventBus[Event Bus]
WebhookWorker[Webhook Workers]
end
LB --> Auth
Auth --> API
Auth --> GitProxy
API --> DB
API --> Cache
GitProxy --> FS1
GitProxy --> FS2
GitProxy --> FS3
GitProxy --> DB
GitProxy --> EventBus
EventBus --> WebhookWorker
EventBus --> Indexer
EventBus --> Orchestrator
Indexer --> SearchIndex
Orchestrator --> VMPool
```
What does the complete architecture show?
This diagram unifies everything you have designed across the previous steps. Traffic enters through the edge, hits authentication, then routes to either the API or Git Proxy.
The Git Proxy publishes events that fan out to webhooks, the trigram indexer, and the CI orchestrator. Each subsystem is drawn as its own subgraph, showing how they connect without being tightly coupled.
- Save and check the preview. You should see a large top-down flowchart with seven labeled subgraph boxes and arrows showing how traffic flows from the edge through to each subsystem.
Only seeing part of the diagram?
Make sure both parts are inside the same ```mermaid fence. The opening ```mermaid should appear before flowchart TD and the closing ``` should come after the last connection line (Orchestrator --> VMPool).
Help me debug my complete architecture Mermaid diagram
- Add the key design principles section below the closing fence of your architecture diagram:
### Key Design Principles
1. **Simplicity at the core**: Plain Git repos on local SSDs. No distributed filesystem. The complexity lives in the replication layer above, not below.
2. **Separation of concerns**: Each subsystem scales independently. Code search scaling does not affect Git storage scaling.
3. **Events as the connective tissue**: The Event Bus decouples subsystems. A push event triggers webhooks, indexing, and CI without the file server knowing about any of them.
4. **Quorum over consensus**: Three replicas with majority vote is the simplest protocol that provides both durability and availability. No complex Paxos or Raft needed for this use case.
5. **Isolation for untrusted workloads**: CI code runs in ephemeral VMs, not in the same trust domain as the platform itself.
What do these principles capture?
These five principles are the recurring patterns behind every design decision in the document. Simplicity at the storage layer, independence between subsystems, events as the glue, quorum for durability, and isolation for untrusted code.
Any future feature you add to the system should follow these same principles. If a new feature breaks one of them, that is a signal to reconsider the approach.
- Save github-system-design.md and check the preview. You should see a numbered list of five principles, each with a bold label followed by the explanation.
Before you scroll through the full preview, ask yourself: can you name all five subsystems and explain how they connect through the Event Bus?
- Scroll through your entire Markdown preview from top to bottom. You should see the trigram code search flowchart (left-to-right), the CI/CD per-job VM flowchart (top-down), the five-subsystems table, and the complete architecture diagram with all seven subgraphs connected.
✔️ Awesome, I've got everything!
Great work. Double check you have saved your file.
ⓧ I'd like to double check the full code
Your file is now quite long. Scroll through and confirm these sections appear in order:
- The ## Code Search and CI at Scale heading with a horizontal rule above it.
- The ### Code Search: Trigram Inverted Index heading followed by a flowchart LR Mermaid diagram.
- The "How trigram search works" explanation with the handleRequest example and "Why trigrams over full-text search?" bullet list.
- The ### CI/CD: Per-Job VM Isolation heading followed by a flowchart TD Mermaid diagram.
- The "Why per-job VMs instead of containers?" comparison table with 5 rows.
- The ## System Summary heading with a horizontal rule above it.
- The ### Five Subsystems, One Platform heading with the 5-row subsystems table.
- The ### Complete Architecture heading with a flowchart TD diagram containing 7 subgraphs and connection lines.
- The ### Key Design Principles heading with 5 numbered principles.
Your system design document now covers all five subsystems from edge to CI fleet. Next up, you will tackle the Secret Mission: designing the failure detection and auto-repair system that keeps this entire architecture running when servers inevitably go down.
Secret mission
Design the Failure Detection and Auto-Repair System
Your system keeps three copies of every repository, but what happens when servers inevitably fail? At GitHub's scale, servers crash constantly. Without automated detection and repair, human operators cannot keep up. Design the self-healing subsystem that makes the 'three copies' guarantee actually work in production.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Your system design document is a Markdown file stored on your machine.
Resources you used:
- Local project folder containing github-system-design.md.
Keep everything running
No action needed. Choose this if you plan to keep iterating on your system design document or want to reference it in the future.
- Your github-system-design.md file stays in your project folder on your Desktop.
- You can reopen it anytime in VS Code to view the rendered Mermaid diagrams.
Pause - I'll come back to this later
Since this is a local file with no running processes, there is nothing to pause. Your document is already saved and waiting for you whenever you return.
- No background processes or servers to stop.
- Your file remains on disk until you explicitly delete it.
Delete - I don't want to use this again
Remove the project folder and its contents entirely.
- Delete your project folder (the one containing github-system-design.md) from your Desktop by dragging it to the Trash (macOS) or Recycle Bin (Windows).
- Empty the Trash or Recycle Bin to free disk space.
That's a Wrap!
That's a Wrap!
Nice work. You just designed a complete system architecture for a GitHub-scale repository hosting platform, from a naive single-server design all the way through distributed replication, collaboration, search, and CI isolation.
You've learned how to:
- Design a distributed replication system using three-phase commit and quorum writes to guarantee data durability across three replicas, applying the CAP theorem trade-off GitHub chose.
- Architect a full collaboration layer where Pull Requests, webhooks, and metadata live separately from Git storage, connected by an event-driven architecture.
- Design specialized subsystems for code search using trigram indexing and CI/CD isolation using per-job ephemeral VMs, each scaling independently from the core platform.
- Secret Mission: Design a self-healing failure detection and auto-repair system using real-traffic monitoring and automated replica repair with checksum verification.
Ready to quiz yourself?