Free preview

High-Level Design and the API

In one line: the high-level design is deliberately three boxes, which makes it easy to draw and easy to attack. The API is where the version-on-name-collision behaviour shows up — a detail that reveals how the system handles concurrency.

What this design cannot do — worth volunteering before the interviewer asks

Three boxes is the right starting point and it leaves everything open:

  • Nothing decides where a blob goes. With many disks and many frontends, who chooses the target, and how does a reader find it again?
  • Nothing tracks metadata. Container membership, access levels, versions — none of it has a home.
  • Nothing handles a blob bigger than a disk. A 4 GB video on a full disk has nowhere to go.
  • Nothing replicates. Lesson 3 demanded data persist until explicitly deleted; one disk failure violates that.

Every component the next lesson adds — the manager node, metadata storage, chunking, replication — answers one of these. Naming the gaps yourself is how you move into the detailed design under your own steam.

API design

The following operations require a registered and authenticated user. Registration and authentication are omitted for brevity.

Containers

createContainer(containerName)
ParameterDescription
containerNameThe name of the container. It should be unique within a storage account
deleteContainer(containerPath)
listContainers(accountID)
ParameterDescription
containerPathThe path to the container the user wants to delete
accountIDThe ID of the user who wants to list their containers

Blobs

putBlob(containerPath, blobName, data)
ParameterDescription
containerPathThe path of the container to upload into. Consists of the accountID and containerID
blobNameThe name of the blob. Should be unique within a container — otherwise the system gives the blob uploaded later a version number
dataThe file the user wants to upload
getBlob(blobPath)
deleteBlob(blobPath)
listBlobs(containerPath)
ParameterDescription
blobPathThe fully qualified path of the data or file, including its unique ID
containerPathThe path to the container to list blobs from

deleteBlob marks a blob for deletion. The system removes the actual data asynchronously during garbage collection.

Note: this API definition is logical. In production, large data transfers may require multistep streaming.

Note: blob retrieval APIs provide metadata such as size, version number, access privileges, and name.

Name collisions create a version rather than failing or overwriting — that's a real design choice

putBlob with an existing blobName does not error and does not overwrite. It creates a new version.

That follows directly from Lesson 1's immutability. If blobs cannot be modified in place, an "overwrite" has to mean "store new bytes and point the name at them," and keeping the old bytes addressable by version costs nothing extra — they already exist as separate chunks.

Three consequences worth naming:

No lost updates. Two clients uploading the same name concurrently both succeed; neither silently destroys the other's data. Lesson 7 covers how the manager node serializes them.

Delete becomes the dangerous operation, not overwrite. In most systems an accidental overwrite is unrecoverable. Here it is just an older version — which is why the retention window from Lesson 3 is attached to deletion specifically.

Storage grows with writes, not with distinct names. Uploading the same blob a thousand times stores a thousand versions and bills for all of them, which is exactly what Lesson 2's lifecycle expiration rules exist to bound.

The upload contract the logical API hides

This API definition is logical. In production, large data transfers may require multistep streaming.

That one line hides the mechanism every real object store exposes and every interviewer asks about. Two pieces: the bytes must not go through your servers, and a large upload must survive a dropped connection.

The bytes bypass your servers

The obvious implementation has the client POST the file to an application server, which forwards it to storage. At blob-store scale that is wrong, and not marginally.

This is what "access a blob via a system-generated URL" from the requirements is for. Because the system generates the URL, it can sign it — and a signature can carry far more than a location.

A presigned URL is a cryptographic hash over the request details — method, object path, expiry — computed with the account's secret key. The storage service recomputes the same hash on arrival and compares. Two consequences worth knowing:

Generating one costs nothing. It is a hash computed in the application's own memory; there is no round trip to the storage service to mint it. So handing out a URL is cheap enough to do per request.

Conditions are baked into the signature, so they cannot be edited by whoever holds the link. The two that matter are a size range — without it, a URL meant for a 5 MB avatar accepts a 5 TB upload and your storage bill with it — and a content type, so an image endpoint cannot receive an executable.

The URL is a capability: whoever holds it can use it, which is why it expires in minutes rather than days.

Large uploads: parts, not one PUT

A 5 GB upload over a 100 Mb/s connection takes about seven minutes. A single PUT that fails at 99% starts over.

Four properties fall out of this, and they are the reason every provider offers it:

  • Resumability. A dropped connection costs one part, not the file.
  • Parallelism. Several parts in flight saturates the link.
  • Progress. Parts completed over parts total is a progress bar, for free.
  • Integrity. The per-part ETag lets the service verify it assembled the bytes the client actually sent.

Two operational details worth volunteering. Until the completion call succeeds there is no object — only parts, which are not readable. And incomplete uploads are billed, so an abandoned upload leaks storage forever unless a lifecycle rule deletes parts after a day or two. That is a nice callback to the previous lesson: the same lifecycle machinery that demotes cold data also sweeps up failed uploads.

deleteContainer is the operation to be careful about

deleteContainer removes a container and all blobs inside it — potentially millions of objects in one call.

Two things fall out. It cannot be synchronous: marking millions of blobs and reclaiming their chunks is garbage-collector work, so the call returns after marking the container and the rest happens in the background.

And it is the highest-blast-radius operation in the API, which is a good argument for the retention window applying at container level too. "Delete container" typed against the wrong container is a genuinely catastrophic, genuinely common mistake.

Key takeaway

Three components to start: clients, frontend servers, storage disks — with no placement logic, no metadata, no chunking, and no replication, all of which the detailed design adds. Seven API calls, where a name collision produces a version rather than an overwrite, and both delete operations are asynchronous by necessity.

Interview signal by level

LevelWhat a strong answer sounds like
L4"Clients talk to frontend servers which write to disks. The API is put, get, delete, and list."
L5Volunteers the gaps: "this has no way to decide which disk a blob goes to, no metadata store to find it again, and nothing handles a blob larger than one disk."
Staff+Reads the versioning behaviour: "a name collision creates a version rather than overwriting, which follows from immutability — so there are no lost updates, and delete rather than overwrite becomes the dangerous operation. I'd also expect multipart upload in practice: single-shot put isn't resumable and can't parallelize, and chunking the wire protocol lines up with how we're going to chunk server-side anyway."

Next: the components that fill in the gaps.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue