← all writing tech

Compress before upload and show the bar, or users will upload it five times

On my laptop the delivery photo uploads instantly; on one bar of signal in a dusty yard, the same photo takes five minutes with no feedback, so the driver taps again and I get three copies.

On my laptop, a proof-of-delivery photo uploads the instant I let go of the button. For a driver standing in a yard on one bar of signal, that exact same upload takes five minutes and shows nothing while it happens. So they assume it failed, and they tap again. And again. I’d open the order later and find the same crate photographed three times, each copy a full-resolution file that took minutes to land.

The bug wasn’t the server. The server was fine. The bug was that I’d built the upload path on my own connection and never once watched it run on theirs.

A photo is heavier than you think

A modern phone camera produces a four or five megabyte image without trying. On wifi that number is invisible. On a congested cell tower at the edge of coverage, four megabytes is a sentence, not a transfer. The driver isn’t being impatient. They genuinely have no way to tell a slow upload from a dead one, because I gave them no way.

The fix isn’t a faster server, and it isn’t telling drivers to be patient. It’s two changes on the device, both of which are really just empathy turned into code.

  • Resize and compress before the file leaves the phone. A proof-of-delivery shot does not need to be print quality. Scale the longest edge down to something sane and re-encode at a sensible quality, and the four megabytes becomes a few hundred kilobytes that the eye cannot tell apart on a crate of goods. You did the expensive work on the one machine that has spare cycles and isn’t paying for the bytes: the phone in their hand.
  • Always show real progress. Not a spinner, which says only “something might be happening.” A bar that moves, tied to actual bytes sent, that reaches the end and then says done. A spinner is a question. A progress bar is a promise, and a driver who can see the promise being kept does not tap twice.

A spinner tells the user something might be happening. A progress bar tells them how much is left. On a bad network, that difference is whether they upload the file once or five times.

The duplicates were a UI bug wearing a backend costume

For a while I treated the triple-uploads as a data problem. I added dedup logic, I thought about hashing the images, I considered rejecting near-identical photos within a time window. All of that is treating the symptom. The driver tapped three times because the interface gave them no reason to believe the first tap worked. Fix the feedback and the duplicates mostly stop arriving in the first place. The cheapest dedup is a progress bar.

Compress on the way in, too

There’s a second half nobody mentions, and it lives on the office side. Even after I shrank the upload, the back-office review screen was still pulling full-weight images into a grid, twenty orders at a time, and the people doing reconciliation were watching a page crawl. So I compress again on ingest: generate a small thumbnail for every photo and serve that in lists, and only fetch the full image when someone actually clicks to inspect a delivery.

That turns a heavy gallery into a fast one, and it costs nothing the user can perceive, because nobody studies a thumbnail. They glance at it to confirm the right crate showed up, and pull the full frame only when something looks off.

The shape of the lesson is the same on both ends: image weight is a first-class performance budget, not an afterthought you bolt on when someone complains. Every photo in the system has a cost, paid by whoever is on the worst connection, and that person is never you at your desk. Treat the bytes as the expensive thing they are, compress them at every door they pass through, and always, always show the bar.

Email