The problem
You are designing a file upload and sharing service similar to Dropbox. Users can upload, download, and share files of various sizes, and expect fast, reliable access from anywhere. The business expects rapid growth, and downtime or data loss would severely impact user trust.
You are the lead backend engineer tasked with designing the core file upload and download system.
If the system cannot scale, users will experience slow uploads/downloads or lose files, leading to churn and reputational damage.
Must hold true
- Files up to 100 MB must be supported.
- Service must handle 10,000 daily active users at launch, growing 10x in a year.
- File metadata (name, owner, size, timestamps) must be queryable.
- Users expect file download/upload latency under 2 seconds for typical files.
Functional Requirements
What the system must DO.
| Requirement | Priority |
|---|---|
| Users must be able to upload files up to 100 MB. | Must have |
| Users must be able to download their previously uploaded files. | Must have |
| The system must store and allow querying of file metadata such as name, owner, size, and timestamps. | Must have |
| Users should be able to share files via a public link. | Nice to have |
Non-Functional Requirements
How well it must do it — latency, availability, scale. Measurable, not vibes.
| Requirement | Priority |
|---|---|
| Upload and download latency for typical files should be under 2 seconds. | Must have |
| Files must not be lost; durability must be at least 99.9999999%. | Must have |
| The system must scale to 100,000 daily active users within a year. | Must have |
What you learn by asking
In an interview these facts are NOT volunteered — the candidate earns them with clarifying questions. These are the answers behind this design.
| If you ask about… | The answer |
|---|---|
| Daily active users at launch | 10,000 |
| Expected growth in 1 year | 10x (100,000 daily active users) |
| Average files uploaded per user per day | 5 |
| Average file size | 10 MB |
| Peak upload/download concurrency | About 500 concurrent uploads/downloads at peak |
| File retention period | Indefinite (users rarely delete files) |
Capacity Estimation
Rough arithmetic turns the requirements into engineering decisions — it tells you which parts need scaling machinery and which are trivially easy. Only the order of magnitude matters.
| Quantity | Answer | How it is derived |
|---|---|---|
| Peak file upload QPS | ~6 uploads/sec | 100,000 users × 5 files/day = 500,000 uploads/day. Spread over 12 peak hours (43,200 seconds): 500,000 / 43,200 ≈ 12 uploads/sec at peak. With concurrency, estimate 6-12 uploads/sec. |
| Peak file download QPS | ~20 downloads/sec | Assume downloads are 3x uploads (users download more than they upload): 500,000 uploads/day × 3 = 1,500,000 downloads/day. 1,500,000 / 43,200 ≈ 35 downloads/sec at peak; round down to 20-35 for estimation. |
| Total storage needed after 1 year | ~1.8 TB | 500,000 uploads/day × 365 days × 10 MB = 1,825,000,000 MB = 1,825 TB after 1 year. |
Quick check
Without scrolling back: roughly what did we work out for peak file upload qps?
The architecture, built up
Nobody designs the final diagram in one stroke, so this design is built up one component at a time rather than shown finished. Start with the simplest thing that works, then add each piece and see where it attaches.
Each stage lets the numbers break the design, then fixes exactly one bottleneck with exactly one new component — and at each break you get a chance to call the fix yourself before it is revealed. You can stop reading after any stage and still hold a complete, working system in your head — that is the point.
The simplest thing that works
Start with a single application server and a database for metadata and file storage.
At launch, a simple architecture can suffice: the client interacts with a single application server, which stores both file metadata and the file contents in a database.
This design is easy to build and reason about, and works for very small scale.
The design so far
Where it breaks
At 1.8 PB/year, storing files in the database is not feasible; database performance and cost degrade rapidly.
Quick check
Which single component would you add to fix that?
Database cannot handle large files
The fix: Move file contents to object storage — a service designed for large, unstructured data.
Relational databases are not designed to store massive blobs efficiently. As file volume and size grow, storing files in the database becomes slow, expensive, and hard to scale.
Object storage (like Amazon S3 or Google Cloud Storage) is built for this use case, offering durability and scalability for large files. The database now stores only metadata and file paths.
The design so far
Where it breaks
At 6-12 uploads/sec and 20-35 downloads/sec, a single server cannot handle all traffic or survive failure.
Quick check
Which single component would you add to fix that?
One app server is now the weak point
The fix: Introduce a load balancer to distribute requests across multiple app servers.
As traffic grows, a single application server becomes a bottleneck and a single point of failure.
A load balancer spreads incoming requests across multiple app servers, improving throughput and availability.
The design so far
Where it breaks
With 1.8 PB/year and global users, download latency exceeds 2 seconds and origin bandwidth is saturated.
Quick check
Which single component would you add to fix that?
Downloads slow for global users
The fix: Add a CDN — a content delivery network caches files close to users for fast downloads.
As users spread worldwide, downloading files directly from object storage becomes slow and expensive.
A CDN caches popular files at edge locations, reducing latency and offloading bandwidth from the origin.
The design so far
Where it breaks
At 100,000 DAU, metadata DB is overwhelmed by frequent queries (listing files, checking links).
Quick check
Which single component would you add to fix that?
Metadata queries slow under heavy load
The fix: Add a metadata cache — a fast, in-memory store for common queries.
As usage grows, the metadata database becomes a bottleneck for frequent reads, like listing files or checking sharing links.
A cache (like Redis or Memcached) stores recent or popular metadata in memory, greatly reducing DB load and improving response times.
The finished design
The full reference design, with the reason behind every connection. When you practice, you will rebuild this from a palette that includes decoys.
Why each connection exists
- ClientLoad BalancerAll client requests are routed through the load balancer for distribution.
- Load BalancerApplication ServerThe load balancer forwards requests to available application servers.
- Application ServerMetadata DatabaseApp servers read/write file metadata for uploads, downloads, and queries.
- Application ServerObject StorageApp servers coordinate file uploads/downloads with object storage.
- Object StorageCDNCDN caches and serves frequently accessed files for fast downloads.
- Application ServerMetadata CacheApp servers use cache to speed up frequent metadata reads.
The design in one glance
Each stage fixed exactly one problem. If you can retell this table from memory, you can derive the whole architecture on a whiteboard.
| The design so far | What broke | The fix |
|---|---|---|
| The simplest thing that works | — the starting point | Start with a single application server and a database for metadata and file storage. |
| Database cannot handle large files | At 1.8 PB/year, storing files in the database is not feasible; database performance and cost degrade rapidly. | Move file contents to object storage — a service designed for large, unstructured data. |
| One app server is now the weak point | At 6-12 uploads/sec and 20-35 downloads/sec, a single server cannot handle all traffic or survive failure. | Introduce a load balancer to distribute requests across multiple app servers. |
| Downloads slow for global users | With 1.8 PB/year and global users, download latency exceeds 2 seconds and origin bandwidth is saturated. | Add a CDN — a content delivery network caches files close to users for fast downloads. |
| Metadata queries slow under heavy load | At 100,000 DAU, metadata DB is overwhelmed by frequent queries (listing files, checking links). | Add a metadata cache — a fast, in-memory store for common queries. |
Good to know — What this design leaves out — on purpose
Every real system also needs monitoring and metrics, logging and tracing, alerting and on-call, CI/CD, auth hardening and abuse limits, and a cost model. None of them appear above, and that is a choice: they sit beside EVERY system in much the same shape, so drawing them here would add the same five boxes to every problem in this library while crowding out the part that is actually specific to this one — the path a request takes and where it breaks. This is also why a metrics or monitoring block is a wrong answer in the practice canvas: it is never the thing that makes this design work. In a real interview, name these in one sentence once the data path is settled — "I would put metrics on the cache hit rate and alert when it drops" — and move on. Reaching for them before the core flow is drawn reads as avoiding the question.
API Design
Every operation traces back to a requirement — nothing extra, nothing missing.
| Endpoint | Purpose |
|---|---|
| POST /files/upload | Upload a new file to the user's account. |
| GET /files/{fileId}/download | Download a file by its unique ID. |
| GET /files | List all files for a user, optionally filtered by metadata. |
| POST /files/{fileId}/share | Generate a public sharing link for a file. |
Quick check
Which one would you call to upload a new file to the user's account?
Data Model
| Entity | Fields |
|---|---|
| File | fileId: string (Unique identifier), ownerId: string (User who owns the file), name: string (Original filename), size: integer (File size in bytes), createdAt: timestamp (Upload timestamp), updatedAt: timestamp (Last modified timestamp), storagePath: string (Location in storage backend), sharedLink: string (Public link if shared) |
Defending the Design
Interviewers close by stress-testing the design. Strong answers follow one shape: which component → what breaks there → the fix and its cost. Here are this problem's probes and the points a strong answer hits:
What could make object storage a bottleneck in your design, and how would you address it? High concurrent uploads/downloads can saturate bandwidth or IOPS. Object storage may have request rate limits or latency spikes. Mitigate with multi-part uploads, parallelization, or using a CDN for reads.
How does your system handle a failure of the metadata database? Metadata DB is critical for file lookup and access control. Use replication and backups to avoid data loss. Failover to a replica or read-only mode if primary is down.
How does adding a CDN help with scaling downloads as user base grows? CDN caches files close to users, reducing load on object storage. Reduces latency for global users. Absorbs traffic spikes and offloads bandwidth from origin.
What are the tradeoffs of adding a metadata cache between app servers and the database? Cache reduces DB load and speeds up frequent queries. Risk of stale data if cache invalidation is not handled well. Extra complexity in cache management.
Tip — Key takeaway
Designing a scalable file upload service requires separating metadata from file storage, using object storage for durability and scale, and adding components like load balancers, caches, and CDNs as demand grows. Each component addresses a concrete scaling or reliability need, and should be justified by real usage numbers.
Common trap — Common mistakes on this problem
Storing files directly in the database, which doesn't scale for large files or high throughput. · Ignoring the need for a CDN, leading to slow downloads for global users and high bandwidth costs. · Adding unnecessary components (like a queue or search index) before the core system justifies them, increasing complexity without benefit.
You've seen the whole design — now build it yourself
The practice run walks the same steps, but YOU do the work: gather the requirements, run the numbers, wire the architecture — with an AI interviewer and per-step feedback.