Design File Storage, Snapshots, And Versioning
Asked of: Software Engineer
Last updated

What's being tested
This topic tests whether you can design a durable, low-latency storage model for large collaborative documents while preserving historical versions. The interviewer is probing your command of event logs, immutable snapshots, content-addressed storage, consistency, recovery, and garbage collection—not merely whether you can choose a database. Figma cares because design files are structured graphs edited concurrently, must load quickly, support version history and restore, and cannot lose user changes.
Core knowledge
-
Document representation should separate logical file metadata from the document graph: pages, nodes, styles, components, and binary assets. Store metadata and indexes in a transactional database, while large immutable payloads belong in object storage such as
`S3`. -
A practical design combines a materialized current state with an append-only operation log. The current state makes reads fast; operations provide auditability and replay. Replaying every edit is too slow, so create snapshots after a bounded number of operations or bytes.
-
A snapshot should be immutable and identified by a stable version ID, parent version, author, timestamp, and content hash. Immutability simplifies caching, replication, rollback, integrity verification, and concurrent readers.
-
Content-addressed storage names blobs by a cryptographic digest, typically
SHA-256:blob_id = SHA256(canonical_bytes). Identical subtrees or assets can be deduplicated, but canonical serialization must be deterministic or logically identical objects produce different IDs. -
For large files, use incremental snapshots or a Merkle-tree-like structure rather than copying the entire document. A root points to immutable chunks; updating one node rewrites only affected paths. This reduces write amplification, though reads require more indirection and garbage collection becomes harder.
-
Optimistic concurrency control prevents lost updates. Each write includes the client’s base revision; the server accepts it only if it matches the current head, or transforms/merges operations when the application supports that model. A compare-and-swap on
(file_id, revision)is a useful primitive. -
Collaborative editing usually needs an operation protocol such as OT or CRDTs, depending on semantics. CRDTs simplify convergence under reordering and retries but can consume more metadata; OT can be more compact but requires carefully designed transformation functions and server ordering.
-
Version history should distinguish an automatically generated checkpoint from a user-visible named version. A restore should normally create a new head whose parent is the current version, rather than deleting later history. This preserves auditability and makes undo operationally safe.
-
Durability and atomicity require ordering writes carefully: persist referenced blobs, commit the revision metadata and parent pointer transactionally, then publish visibility. If metadata commits before blobs are durable, readers can observe broken references; if blobs remain orphaned, a sweeper must reclaim them.
-
Use idempotency keys for retries. A request keyed by
(client_id, operation_id)should return the original result rather than append the operation twice. This matters when clients lose responses after the server has already committed. -
Reads need a clear consistency model. A file editor generally needs read-your-writes and monotonic revisions, while historical versions can be served from eventually consistent replicas. Route writes and immediately subsequent reads through a leader or session-aware mechanism.
-
Garbage collection must retain every reachable version, chunk, asset, and dependency from active files, published versions, legal holds, and restore points. Mark-and-sweep over the version graph is safer than deleting blobs by age; use a grace period for in-flight references and replica lag.
Worked example
For a representative prompt, “Design file storage, snapshots, and versioning for a collaborative editor,” I would first clarify file size, edit rate, concurrent editors, required recovery point objective, version-history retention, and whether arbitrary historical reads must be low latency. I would state an assumption such as files containing a structured node graph, frequent small edits, and a requirement that acknowledged edits survive machine failure. I would organize the answer around the data model, write path and concurrency control, snapshot and historical-read strategy, and durability plus garbage collection. The core design would append idempotent operations, periodically materialize immutable snapshots, and maintain a transactional current-head pointer referencing durable content-addressed chunks. I would explicitly compare full snapshots with Merkle-tree or incremental snapshots: full copies simplify reads and deletion, while incremental snapshots reduce storage and write amplification for large files. I would explain how a restore creates a new revision pointing to an older immutable root, rather than mutating or deleting history. I would also define failure behavior for a crash between blob upload and metadata commit, using retries and orphan sweeping. If I had more time, I would add multi-region replication, schema migration for old snapshots, and load-test targets for p95 open latency and commit throughput.
A second angle
A different framing is “How would you support browsing and restoring any version without slowing normal editing?” Here the primary concern is read amplification and indexing rather than collaborative conflict resolution. Keep immutable version manifests with parent pointers and periodic full checkpoints, then reconstruct intermediate states from a bounded operation range or precomputed historical indexes. The design should guarantee that browsing never changes the live head, while restore is a new conditional write against the current revision.
Common pitfalls
Pitfall: Treating the latest document as the only source of truth and overwriting it in place makes rollback, auditing, crash recovery, and concurrent writes difficult. Prefer immutable revisions plus a small mutable pointer to the current head.
Pitfall: Saying “use a database” without specifying what is transactional and what is blob storage leaves the critical failure modes unanswered. Identify the atomic boundary between operations, revision metadata, chunk references, and object durability.
Pitfall: Claiming snapshots solve collaboration ignores concurrent edits and retry behavior. Explain revision preconditions, idempotency keys, operation ordering, and whether the system rejects, transforms, or merges stale writes.
Connections
The interviewer may pivot to optimistic concurrency control, CRDTs versus OT, Merkle trees, object-store consistency, schema evolution, or multi-region replication. They may also ask how to test crash recovery, deduplication correctness, and garbage collection without deleting live content.
Related concepts
- Caching And Stateful Data Structure DesignCoding & Algorithms
- Document Layer Data ModelsCoding & Algorithms
- Real-Time Collaboration SystemsSystem Design
- Storage, Indexing, APIs, And Secure ExecutionSystem Design
- Adobe Document Cloud real-time collaboration and offline sync
- Cloud File Storage ServiceSystem Design