Loading...
Loading...
Senior · 35 min
Also asked as: Dropbox uploader, Google Drive upload, YouTube video upload, Figma asset import, S3 multipart upload UI.
Read it once, then start the timer and work without scrolling.
Design a file upload experience that handles multi-gigabyte files over unreliable connections, survives a page reload, and reports honest progress.
Everything below is a model answer. You will get far more out of this drill by attempting it cold first — set the timer, talk through it out loud, and only then compare.
A 35-minute phase plan you can practise against a clock.
Interactive · Drill rehearsal timer
5:00
Time remaining in Clarify and scope.
What you should establish before drawing a single box.
Architecture, data, interfaces, and optimisations — the RADIO body.
The half of the answer most candidates skip entirely.
Naming how the system breaks, and what the user sees when it does, is the cheapest way to signal production experience. Work through these before the interviewer has to ask.
The interviewer will challenge you. Rehearse the concession, not just the defence.
Interviewer
This is a lot of machinery. Why not just POST the whole file with a multipart form and let the browser handle it?
Model response
For files under roughly 100MB on a reasonable connection, that is genuinely the right answer and the extra complexity here would be waste. The reasons it stops working are concrete rather than theoretical. Proxies and load balancers commonly enforce body-size limits and idle timeouts around sixty seconds, so a large single request gets killed by infrastructure the frontend does not control. More importantly, a single request is all-or-nothing: a dropped connection at 95% costs the entire transfer, and on mobile that is not a rare event, it is the normal case for a multi-gigabyte file. And there is no resume story at all, because the browser gives you no handle on what was already transmitted. So my rule is a size threshold: single request below it, chunked above it, behind the same component API so the caller does not care which path ran.
Interviewer
You are skipping the content hash. That means you cannot deduplicate, and you cannot verify integrity end to end.
Model response
Both of those are real losses and I am trading them deliberately. Hashing four gigabytes means reading every byte before the first one is sent — a couple of minutes of CPU on a laptop, longer on a phone, during which the user sees a progress bar that has not moved. That is a bad enough experience that I would rather give up dedup by default. The integrity concern is partly covered elsewhere: object storage returns a per-chunk ETag, and the complete call verifies the assembled set, so corruption in transit is caught even without a client-side digest. Where I would reverse this is a product where dedup is a headline feature, like a backup client, and there the right implementation is incremental — hash chunk by chunk on a Worker as each chunk is uploaded, so hashing overlaps the transfer instead of preceding it, and ask the server after the first chunk whether it already has the file.
Interviewer
Uploading straight to object storage with presigned URLs means we lose the ability to scan, validate, or transform files before they land in our bucket.
Model response
That is accurate, and for a product with strict content requirements it is a serious objection. What I would not accept is the implied alternative of proxying every byte through the API tier, because that turns a stateless API into a bandwidth-bound service, makes autoscaling a function of upload volume, and puts multi-minute connections through infrastructure sized for millisecond requests. The pattern I would use instead is a quarantine bucket: presigned uploads land in a staging location the application cannot read from, an object-created event triggers scanning and validation, and only then is the object promoted to its real home. The frontend consequence is that complete does not mean available — there is a processing state after upload — and I would model that in the UI rather than hide it. If the requirement were synchronous rejection before the bytes are stored at all, then proxying is the only option and I would size the tier for it.
Interviewer
Resume across a browser restart is not actually possible. You cannot persist a File handle, so the user has to pick the file again anyway — why bother?
Model response
You are right about the constraint and I should be precise about what resume does and does not buy. A File reference from an input element is not serializable, so after a browser restart the user must reselect the file; I cannot avoid that. What persistence buys is that reselecting costs one click instead of re-uploading three gigabytes, because the chunk manifest tells me the file was 80% done and the server confirms which parts it holds. Within a session the value is larger still, since a navigation, a crash of a single tab, or an offline period resumes with no user action at all. There is a path to true handle persistence — the File System Access API lets you store a FileSystemFileHandle in IndexedDB and re-acquire it after a permission prompt — but it is not available across all browsers, so I would treat it as an enhancement rather than the design. The honest framing is that this is resumable transfer, not resumable file access.
Specific signals an interviewer is listening for on this prompt.
Turn one attempt into retained skill.