Senior Engineering Interview Handbook / Chapter 90
Content and Storage Systems
A sustained private-photo design that develops the authority, commit, delivery, deletion, and recovery decisions behind URL shorteners, file synchronization, object storage, CDN delivery, and backup platforms.
Page tools
When does an upload become a photo?
A user finishes uploading a private photo. The object store has all the bytes, but the thumbnail does not exist. The album database has a pending row. A malware scanner has not run. The client retries because the final response was lost.
Has the upload succeeded?
“The bytes are in storage” is not yet an answer. Neither is a diagram with an API, a database, an object store, a queue, and a CDN. The design needs a moment after which the product can truthfully tell the user that the original is theirs, and it needs a safe outcome for every partial state before that moment.
This question reaches beyond photo services. A short link exists only when its mapping can be trusted. A synchronized file exists as a particular version, not as whichever chunks arrived last. An object store must define when a completed multipart upload becomes readable. A backup exists only if a named recovery point can actually be restored.
The answer separates five kinds of state. Metadata owns identity, namespace, owner, permissions, current version, and lifecycle. Source bytes are the canonical payload. Derived artifacts include thumbnails, previews, indexes, and scan results. Cached copies include edge objects and local synchronized copies. Recovery state includes earlier versions, tombstones, snapshots, backup indexes, and restore jobs.
They do not deserve identical treatment. Permissions may need an immediate authoritative check. A thumbnail can lag. A cache copy can be stale only within a policy the product can tolerate. A backup may be durable yet useless if no one has verified the path back to a working system.
Work one promise all the way through
Suppose the prompt is:
Design a photo service in which people upload originals, arrange them in albums, share selected albums, and view photos on mobile and web.
Before choosing components, narrow the promise. Are originals private unless shared? Can a user edit an image or only add a new version? Must removing a person from an album stop new views immediately? Is there an undo window after deletion? Which dominates the bill: retained bytes, image processing, or egress? How quickly must a new upload become browsable?
Assume originals must survive infrastructure failure, common renditions should appear within seconds, sharing revocation should stop new access promptly, and deleted photos can be restored for thirty days. Those choices give the design something firm to protect:
A visible photo has an authoritative metadata version that points to a
durable, checksummed original. Renditions may lag or be regenerated. Every
serving path must honor deletion and the declared access-revocation bound.
The first sentence defines the commit boundary. The second keeps asynchronous work out of that boundary. The third prevents a fast delivery path from quietly weakening privacy.
Create an intent before accepting bytes
The client first asks the metadata service for an upload session. The request names the account, target album, expected size, media type, checksum, and an idempotency key. The service checks write permission, quota, file policy, and size limits. It creates a pending content item and returns short-lived, scoped targets for multipart upload.
The client can now retry individual parts without creating a second photo. Each part is checksummed. The session records what has arrived, expires after a bounded period, and cannot be used to write outside its assigned object. Large payloads can travel directly to storage without turning the application tier into a bandwidth relay.
An application-mediated upload is still reasonable when bytes must be inspected before the service can accept them. That choice purchases a simpler trust boundary with application bandwidth and capacity. The interview answer should connect the choice to the product requirement rather than declare one upload pattern universally superior.
Make finalize the one-way door
After all parts arrive, the client asks to finalize the session. Finalize verifies the assembled checksum and durable placement, then commits an immutable version and makes the content item visible. If its response is lost, the same request returns the already committed version. It does not create a new original.
A compact model is enough:
ContentItem
id, account_id, album_id, current_version, visibility, lifecycle_state
Version
id, content_id, blob_id, checksum, size, created_by, created_at
Blob
id, storage_location, checksum, durability_state
UploadSession
id, content_id, expected_checksum, received_parts, expiry, idempotency_key
The content item is the stable product object. A version is an immutable claim about content at a point in time. The blob records physical bytes. Keeping these separate permits version history, deduplicated chunks, movement between storage tiers, and repair without changing the user-visible identity.
There is no distributed transaction spanning the metadata database and every storage device. Ordering and reconciliation provide the safety:
- create a pending intent;
- accept and verify the bytes;
- ensure the original meets the chosen durability policy;
- atomically commit the visible version in metadata;
- emit work for renditions, scanning, indexing, and audit;
- reclaim expired sessions and unreferenced blobs in the background.
An orphaned blob is wasted storage and can be collected. Visible metadata that points to missing source bytes violates the promise. That asymmetry tells the system which side to make conservative.
Let the asynchronous path fall behind
The committed event starts thumbnail and web-rendition jobs. A processor reads the immutable version, creates a deterministic artifact key that includes the source version and transformation version, and records the result. A repeated job either finds the same valid result or replaces it safely. A failed job can be retried without touching the original.
If policy requires a successful scan before anyone may view the photo, the authoritative metadata remains quarantined until that result arrives. The asynchronous mechanism does not decide the promise; the visibility state does.
Album reads should expose the actual state. While a rendition is pending, the client can show that processing is under way or, for a rare format, request lazy generation. It should not turn “eventually consistent” into an unexplained broken image.
Queue age matters more than queue length alone. A growing oldest-job age says the user promise is drifting even if workers still report success. Operations also need poison-item inspection, replay, priority lanes for first-view renditions, and a backfill path when transformation code changes.
This is where the distinction between source and derived state pays for itself. A thumbnail loss is repair work. An original loss is a correctness incident.
Make the read path prove authorization
Public, private, and shared photos need different delivery policies. Immutable public renditions can use versioned keys and long cache lifetimes. Private album reads first authorize the viewer against current metadata, then mint a short-lived signed URL or make an edge authorization decision.
Neither mechanism makes revocation instantaneous by magic. A signed URL may remain usable until it expires. An already cached response may outlive a permission change unless cache keys, validation, and purge behavior were designed for it. The acceptable token lifetime and purge bound therefore belong in the product promise.
Suppose the owner removes a collaborator. The metadata change takes effect first: album listing stops, new delivery authorization fails, and no new URLs are minted. Existing capability URLs expire quickly; where the requirement is tighter, the system also invalidates a versioned access grant or runs an audited purge. The origin rejects bypass attempts, and CDN cache keys include the tenant, visibility class, asset version, and any access-grant version needed to prevent one authorization scope from receiving another’s object.
The revealing follow-up is not “Would you use a CDN?” It is:
Can the removed collaborator still fetch the private photo, and for how long, through every URL and cache the system has issued?
A senior answer gives a bound and traces the paths that enforce it.
Deletion is a sequence, not a verb
Now the owner deletes the photo. Three events are easy to conflate.
Access revocation happens first. Metadata marks the photo deleted, album reads hide it, and delivery authorization stops. Logical deletion preserves a tombstone, the previous version pointer, audit history, and the thirty-day recovery deadline. Physical reclamation later removes renditions, unreferenced blobs, expired versions, and backup material when retention policy permits.
This sequence reconciles two promises that initially pull in opposite directions: “do not serve this photo” and “let me undo an accidental delete.” It also forces the design to say what restore means. Restoring creates or reactivates an authoritative version after checking the actor and recovery window; it does not merely make an old blob public again.
Legal holds, regulated deletion, and immutable backups can change the policy. Do not promise instant erasure from replicas, caches, queues, audit records, and backups unless the prompt requires it and the design can account for each copy. State the user-visible revocation bound separately from the policy-bound reclamation time.
Scale the pressure, not the diagram
The same architecture changes depending on what grows.
If object count dominates, metadata partitions, namespace indexes, cursor pagination, and background scans become the pressure points. If byte volume dominates, multipart upload, repair bandwidth, storage tiers, replication or erasure-coding policy, and retention dominate. If a few albums go viral, read skew, CDN shielding, request coalescing, origin capacity, and egress cost matter more than average traffic.
The photo service should measure upload completion and checksum failures; metadata conflicts and hot partitions; unavailable source bytes and repair backlog; rendition queue age; authorization denials and purge lag; cache hit ratio alongside origin load; tombstone age and restore success; and storage, processing, and egress cost by tenant or feature.
Those signals follow the promise through the system. A storage dashboard can be green while private photos leak from a cache. A CDN hit ratio can improve while egress cost rises. A backup count can grow while restore time exceeds the product’s objective.
Useful operator tools follow the same path: inspect an item across metadata, versions, blobs, permissions, and audit; reconcile expired upload sessions; replay or backfill a rendition; revoke and purge an access grant; and run a restore with visible verification. These are not decorative admin panels. They are how the team discovers which representation currently tells the truth.
Transfer the invariant to five neighboring prompts
The photo design is not a component recipe. Its transferable method is to name the content unit, locate authority, choose the visible commit, and follow every derived or retained copy through failure.
URL shortener: the content is a mapping
There may be no blob at all. The authoritative state is the mapping from a short code to a normalized destination, together with ownership, expiry, takedown, and abuse status. Redirect reads are hot, while creation and policy updates are comparatively rare. Cache the mapping, but make a known-malicious destination or tombstone propagate within a declared bound. Keep analytics off the redirect path; imperfect counts should not delay the user or preserve a dangerous redirect.
Code generation depends on requirements. Random codes trade a collision check for opacity and easy distribution. Encoded sequences give compact uniqueness but may reveal volume and concentrate ownership. Neither choice answers abuse, expiration, or cache invalidation, which are often the harder parts of the system.
File synchronization: the content is a versioned namespace
A user sees files and folders; storage may see chunks. Metadata owns parentage, names, versions, permissions, moves, and conflict state. Clients upload checksummed chunks, finalize a version, and consume a change log or sync cursor after being offline.
“Last write wins” can silently discard work when two devices edit the same base version. Preserve explicit conflict copies or invoke a merge only for formats that support one honestly. Version history serves both product recovery and support investigation. Device revocation must stop new sync and content access without pretending that bytes already downloaded to an authorized device can be remotely erased.
Object storage: the content is an addressed object
The platform promises durable addressability under defined write, read, list, version, delete, and lifecycle semantics. Multipart finalize needs the same retry-safe one-way door as the photo upload. Metadata partitions can become hot even when the byte store has ample capacity, especially with skewed tenants or prefixes.
Listing is a product contract, not a free scan of storage. Define pagination, ordering, and consistency, then build partition-aware indexes to support that contract. Checksums, repair, replication or erasure coding across failure domains, versioning, delete markers, tenant isolation, and lifecycle movement all protect different parts of the promise.
CDN-backed delivery: the content at the edge is a copy
The origin remains authoritative. Edge nodes buy latency and origin relief by serving a copy under a cache key and freshness policy. Versioned immutable assets can live for a long time; mutable or private assets need validation, shorter lifetimes, signed access, edge authorization, or purge.
Include tenant, variant, encoding, and permission-relevant scope in cache identity. Protect the origin with shielding and request coalescing during miss storms. Observe hit, miss, authorization failure, origin error, and purge lag separately: they describe different failures hidden by one aggregate success rate.
Backup platform: the content is a recovery point
A pile of encrypted chunks is not yet a backup product. Authority includes the snapshot or backup index, retention state, encryption-key access, dependency order, and the durable data needed to reconstruct a named point in time.
Start with the recovery point objective and recovery time objective. They determine capture frequency, incremental chains, storage placement, and restore capacity. Protect backup deletion against operator error and ransomware, but preserve audited lifecycle controls. Verify checksums and run scheduled restores into a safe environment. The strongest evidence of a backup is a tested recovery, not a successful write count.
Rehearse the moment the promise changes
Record a twenty-minute answer to the private-photo prompt. Spend the first minute stating the visible commit, authoritative metadata, source bytes, and access-revocation bound. Carry one photo through upload, finalize, rendition, share, read, delete, and restore.
Then choose one pressure:
- the album becomes public and receives a million reads in ten minutes;
- deletion must stop every new fetch within thirty seconds;
- users edit originals offline on several devices;
- storage cost doubles while the recovery window must remain unchanged;
- one region loses metadata access after accepting upload parts;
- a restore must recover several dependent services to one consistent point.
Do not add boxes until the pressure requires them. Say which promise changed, then carry the consequence through the commit rule, metadata, API, cache, failure behavior, operating signals, and cost.
The design is ready when every copy has a role: authoritative, source, derived, cached, or retained for recovery. Confusion begins when one copy is allowed to change roles without the product admitting it. In a content system, the decisive question remains simple even when the architecture is not: which record may the user rely on now?
Related links
Continue reading
Full table of contents