Compile Ready
All low level design problems
Low Level Design/Expert/Expert Systems

Design Dropbox (LLD)

Files, folders, versioning, chunking, and sync-state modelling for a file-hosting client.

Expert 65m interview 15m read Medium frequency Popularity 77
Composite Observer Strategy Memento Amazon Google Microsoft

Problem Statement

Design the low-level object model for Dropbox-style file sync and storage. The system should represent files and folders as a tree, split file contents into chunks, keep immutable file versions, synchronize local and remote copies, resolve conflicts, manage sharing permissions, and notify connected clients when something changes.

Focus on the LLD view: domain classes, responsibilities, relationships, and extension seams. You are not designing global metadata sharding, object storage internals, or the public REST API.

Business context

Dropbox is a strong expert-level LLD problem because it combines several interview themes in one domain: a Composite file tree, Memento snapshots for version history, Observer notifications for clients, and a Strategy seam for conflict resolution.

Interviewers use this problem to test whether you can separate storage structure from sync policy. A good answer makes versioning the source of truth, then lets the sync engine compare local and remote histories and publish changes without hard-coding a single conflict rule.

Functional Requirements

  • Model folders containing files and other folders using a uniform file-system item abstraction.

  • Split each file version into fixed-size chunks so storage and transfer can happen at chunk granularity.

  • Keep immutable file versions and allow restoring an older version as a new current version.

  • Detect local-only, remote-only, and concurrent local-plus-remote changes during sync.

  • Resolve sync conflicts through a pluggable strategy such as keep latest, keep local, or keep remote.

  • Notify observers such as desktop clients or mobile clients whenever files, folders, or synced copies change.

  • Share files or folders with collaborators using read, write, and owner permissions.

  • Expose a small demo flow that creates a tree, writes versions, shares a file, and runs a conflict-aware sync.

Non-Functional Requirements

Correct version history

A version is immutable once created. Restoring an older version appends a new version instead of mutating history, preserving auditability.

Extensibility of sync policy

Conflict resolution must be a strategy. The sync engine detects the situation; policy decides which side wins.

Efficient transfer boundary

File content is represented by chunks so a production implementation can deduplicate, hash, and transfer only changed chunks.

Client notification

Changes should flow through observers so UI clients, background daemons, and audit listeners subscribe without being embedded in domain logic.

Permission safety

Sharing state is centralized in a service so callers consistently answer read/write questions before exposing content or allowing edits.

Requirement Clarification

QDo we need to design the distributed storage backend?

No. Model the LLD classes a client or metadata service would use. Object storage, replication, and global sharding are HLD concerns.

QShould deletion, move, and rename be fully implemented?

Rename and folder add/remove are part of the model. Full tombstone-based deletion sync can be discussed as an extension.

QHow should conflicting edits be handled?

The base design detects that both local and remote changed from the last synced checksum, then delegates the winner choice to a conflict strategy.

QAre permissions inherited from folders?

The base implementation stores direct grants per item. Production inheritance is an extension that walks ancestors or materializes effective ACLs.

QDo observers receive every low-level chunk event?

No. Observers receive domain-level events such as file updated, folder changed, and conflict resolved. Chunk internals remain encapsulated.

UML Class Diagram

Rendering diagram…
The core structure is a Composite tree. Files own immutable version mementos, versions own chunks, the sync engine depends on a conflict strategy, and observers hang off file-system items to receive change events.

Sequence Diagram

Rendering diagram…
The important flow is version-first. A write creates chunks and a version, observers are notified, and sync later compares checksums before invoking conflict policy.

Entity Identification

FileSystemItem

Abstract base for both files and folders. Carries identity, name, parent link, modified time, path calculation, and observer registration.

idnameparentmodifiedAtobservers

File

Leaf node in the Composite. Accepts writes, turns content into chunks, appends immutable versions, exposes the current version, and restores old versions by appending a new snapshot.

historychunkSize

Folder

Composite node that owns children and computes aggregate size by summing child sizes. Folder changes also emit observer notifications.

children

FileVersion

Memento snapshot of one file state. Stores version number, author, creation time, chunks, checksum, and reconstructs readable content.

numberauthorcreatedAtchunkschecksum

FileChunk

Immutable byte range of a file version. Provides chunk splitting and defensive copies so version state cannot be mutated by callers.

indexbytes

SyncEngine

Compares local and remote checksums against the last synced checksum, decides whether to upload, download, no-op, or resolve a conflict, and records the new synced state.

conflictResolutionStrategylastSyncedChecksumByLocalId

ShareService

Centralizes collaborator grants and answers permission checks for read and write access.

grantsPermission

ChangeObserver

Observer contract used by clients and listeners. It receives a change event whenever an item updates or a child change bubbles up the tree.

onChange(event)

Design Patterns Used

Composite

FileSystemItem is the component, File is the leaf, and Folder is the composite. Callers can compute size, paths, and observer behavior uniformly across the tree.

Memento

FileVersion is an immutable snapshot of file state. Restoring a version creates a new snapshot, so history remains auditable and undo does not mutate the past.

Observer

FileSystemItem maintains observers and emits ChangeEvent objects when files or folders change. Clients react without being hard-wired into the domain objects.

Strategy

SyncEngine detects sync states but delegates conflict choice to ConflictResolutionStrategy. Keep-latest, keep-local, and manual-merge policies become interchangeable classes.

Step-by-Step Design

  1. 1Start with a Composite file tree

    Make FileSystemItem the common abstraction. Folder stores children and File stores versions, but both expose identity, path, size, and observer behavior.

    public abstract class FileSystemItem {
        public abstract long size();
    }
    
    public class Folder extends FileSystemItem {
        private final List<FileSystemItem> children = new ArrayList<>();
        public long size() {
            return children.stream().mapToLong(FileSystemItem::size).sum();
        }
    }
  2. 2Represent every write as a version memento

    A file write chunks the new content and appends a FileVersion. The old versions stay immutable, so restore and audit are natural operations.

    public FileVersion write(String author, String content) {
        List<FileChunk> chunks = FileChunk.from(content.getBytes(StandardCharsets.UTF_8), chunkSize);
        FileVersion version = FileVersion.create(history.size() + 1, author, chunks);
        history.add(version);
        markUpdated("file-updated:v" + version.number());
        return version;
    }
  3. 3Keep chunks immutable and independently hashable

    The sample keeps one checksum per version, but chunk objects are already independent immutable byte ranges. A production version can add per-chunk hashes and deduplication without changing the file tree.

  4. 4Make sync detection separate from conflict policy

    SyncEngine compares local, remote, and last synced checksums. If both changed and differ, it calls a ConflictResolutionStrategy instead of embedding a policy.

    if (localChanged && remoteChanged) {
        File winner = conflictResolutionStrategy.chooseWinner(local, remote);
        File loser = winner == local ? remote : local;
        copyContent(winner, loser, "conflict-resolution");
        return new SyncResult(SyncAction.CONFLICT_RESOLVED, "Conflict resolved.");
    }
  5. 5Notify observers from the domain boundary

    Writes, restores, folder adds, and folder removes call markUpdated. The event is sent to observers and bubbles to the parent folder so a root-level client can watch the whole tree.

  6. 6Keep sharing outside storage objects

    ShareService owns grants by item id. That avoids polluting File and Folder with collaboration rules and gives one place to enforce read/write semantics.

Complete Java Implementation

Loading…

Explanation of Every Class

FileSystemItem

The abstract Composite component. It stores common metadata, computes paths through the parent link, owns observer registration, emits ChangeEvent, and bubbles child changes to parent folders.

File

The leaf node. write chunks content and appends a FileVersion; restoreVersion appends a copied memento so history stays immutable.

Folder

The composite node. It owns child items, supports add/remove, implements iteration, and computes size by summing children.

FileVersion

The Memento. It is immutable, captures author/time/chunks/checksum, reconstructs content, and exposes checksum for sync comparisons.

FileChunk

Immutable chunk value object. It splits byte arrays into fixed-size chunks and returns defensive byte copies.

SyncEngine

The synchronization coordinator. It compares local and remote checksums with the last synced checksum, then uploads, downloads, no-ops, or delegates conflict selection to ConflictResolutionStrategy.

ShareService

Permission facade for collaboration. It stores item grants and answers canRead/canWrite based on read, write, and owner levels.

Main

A runnable demo that builds a tree, registers an observer, writes and restores file versions, grants write access, and triggers a conflict-aware sync.

Dry Run

Sample input

Folder /root/projects contains roadmap.txt with chunk size 8. Alice writes v1 and v2, restores v1, shares write access with Bob, then Alice and Bob both edit before the next sync.

StepActionVersion stateSync stateObserver or permission result
1Alice writes v1roadmap v1 has chunked contentlast synced emptyRoot observer sees file-updated:v1
2Alice writes v2roadmap v2 becomes currentlast synced emptyRoot observer sees file-updated:v2
3Alice restores v1roadmap v3 copies v1 contentlast synced emptyHistory still keeps v1 and v2
4Share with Bobversions unchangedlast synced emptyBob canWrite is true
5Alice edits local and Bob edits remotelocal v4 and remote v4 divergeboth differ from last syncedSyncEngine calls KeepLatestStrategy
6Conflict resolvedloser receives winning content as a new versionlast synced checksum updatedObservers receive sync write notification

The dry run highlights the key interview point: versions are never mutated. Even restore and conflict resolution create new versions, and observers see each domain-level change.

Complexity Analysis

OperationTimeSpaceNote
write fileO(N)O(N)N is content size. The write splits bytes into chunks and stores an immutable version.
restore versionO(V)O(1)V versions are scanned by number; chunks are shared because they are immutable.
folder sizeO(T)O(H)T items in the subtree, H recursion/iterator depth conceptually. The sample uses stream traversal over children.
sync fileO(N)O(N)Checksum lookup is O(1), but copying winning content creates a new version from N bytes when upload/download/conflict occurs.
permission checkO(1)O(1)Direct item grants are stored in hash maps.

The reference implementation is intentionally readable. Production Dropbox would avoid copying whole content on sync by comparing chunk hashes and transferring only missing chunks.

Extensibility

New conflict policy

Implement ConflictResolutionStrategy for keep-local, keep-remote, keep-latest, manual merge, or create-conflicted-copy. SyncEngine does not change.

Chunk deduplication

Add per-chunk hashes and a ChunkStore repository. FileVersion can store chunk ids instead of byte arrays while keeping the same Memento shape.

Permission inheritance

Enhance ShareService to evaluate ancestor folders or cache effective ACLs. Files and folders still remain storage objects, not policy engines.

Deletion sync

Introduce tombstone versions and deletion events. Sync can then compare create/update/delete histories instead of only content checksums.

Multiple client observers

Observers can be UI refreshers, activity feeds, audit loggers, or websocket publishers. Domain events remain stable.

Alternative Designs

Repository-backed metadata model

Move file, folder, version, and share state behind repositories so the sync engine works against persistent metadata rows.

Tradeoffs

Closer to production and easier to scale across processes, but heavier for an LLD interview and less focused on object collaboration.

Event-sourced version history

Store every write, restore, share, rename, and conflict as an append-only event stream, then rebuild current state from events.

Tradeoffs

Excellent auditability and replay, but much more complex than immutable FileVersion mementos for the base problem.

Manual conflict branch

Instead of choosing a winner, create a second file such as roadmap conflicted copy and notify the user to merge.

Tradeoffs

Safer for user data, but sync no longer converges automatically and clients need extra UX for conflict files.

Common Mistakes

  • ×

    Storing versions as mutable references to the current file content, which makes restore and audit incorrect.

  • ×

    Putting sync conflict policy directly inside File or Folder instead of keeping it in SyncEngine plus a strategy.

  • ×

    Treating folders and files as unrelated classes, causing duplicated path, size, and observer logic instead of using Composite.

  • ×

    Notifying clients from UI code only, so background sync and programmatic writes never publish events.

  • ×

    Over-designing distributed storage before the file tree, versioning, and sync state are clear.

  • ×

    Mixing sharing grants into file content classes, which makes storage and authorization hard to evolve independently.

  • ×

    Resolving concurrent local and remote edits by blindly overwriting one side without detecting a conflict.

Follow-up Interview Questions

QHow would you sync only changed chunks?

Give each FileChunk a content hash and store versions as ordered chunk ids. Sync compares chunk manifests and uploads only missing hashes.

QHow would you support delete and undelete?

Represent deletion as a tombstone version or event. Undelete appends a new version that points back to the last live content snapshot.

QHow do folder permissions inherit to files?

ShareService can walk parent folders to compute effective permission, or maintain a denormalized ACL cache updated on share changes.

QWhat happens if both sides edit offline?

The engine sees local and remote checksums both differ from the last synced checksum. That is a conflict, so it delegates to the configured strategy.

QWhere would websocket or push notifications live?

As observers or observer adapters. They subscribe to domain events and publish externally without changing File or Folder.

Production Considerations

Durable metadata

Persist item ids, parent ids, version manifests, checksums, and grants in a transactional store. In-memory objects become a domain layer over repositories.

Chunk storage

Store chunks by hash in object storage with reference counts. This enables deduplication and avoids re-uploading common blocks.

Conflict safety

Prefer user-safe policies such as conflicted copies for real products. Automatic keep-latest is simple but can surprise users.

Observer delivery

Use durable queues for notifications so client updates are retried and ordered. The in-process observer is the LLD seam, not the distributed transport.

Security and audit

Check permissions on every read/write/share call, audit grant changes, encrypt file chunks at rest, and never expose raw object-store keys to clients.

What Interviewers Look For

  • Did the candidate identify Composite as the clean way to model folders and files uniformly?

  • Did they make version history immutable and use restore as a new version rather than mutation?

  • Can they explain local-only, remote-only, same, and conflict sync states clearly?

  • Is conflict handling a Strategy instead of a hard-coded branch that will grow over time?

  • Do observers notify clients from domain changes without coupling the model to UI or networking?

  • Are permissions centralized so sharing rules do not leak into every file operation?

Quiz

0/5 answered

  1. 1.Why is **FileVersion** modeled as immutable?

  2. 2.Which pattern best describes **Folder** containing both files and folders through **FileSystemItem**?

  3. 3.When should **SyncEngine** invoke **ConflictResolutionStrategy**?

  4. 4.What does the Observer pattern buy in this design?

  5. 5.Why keep **ShareService** separate from **File** and **Folder**?

Practice Variants

Add conflicted-copy resolution

Advanced

Implement a strategy that keeps both edits by creating a new sibling file named as a conflicted copy, then notifies observers.

Add chunk hash deduplication

Expert

Extend FileChunk with SHA-256 hashes and introduce a chunk store that reuses identical chunks across versions and files.

Add inherited folder permissions

Advanced

Make ShareService compute effective permissions by walking parent folders and resolving direct grants versus inherited grants.

Flashcards

Cheat Sheet

Entities: FileSystemItem, File, Folder, FileVersion, FileChunk, SyncEngine, ShareService.

Patterns: Composite for the tree, Memento for file versions, Observer for client notifications, Strategy for conflict resolution.

Write flow: client writes content → File chunks bytes → FileVersion snapshot appended → observers notified.

Sync flow: compare local checksum, remote checksum, and last synced checksum → upload, download, no-op, or resolve conflict through strategy → write winning content as a new version.

Sharing: ShareService stores direct grants and answers canRead/canWrite. Inheritance and ACL caching are extensions.

Invariants: old versions are immutable; restore appends; chunks are defensively copied; conflicts are detected before overwrite; observers publish domain-level events.

References

  • BookDesigning Data-Intensive ApplicationsMartin Kleppmann
  • BookDesign Patterns: Elements of Reusable Object-Oriented SoftwareGamma, Helm, Johnson, Vlissides
  • BlogDropbox Tech BlogDropbox Engineering