The proposal deck was too big to send on WhatsApp
A 20-site proposal deck weighed about 90 MB and a 40-site one about 180 MB, past what WhatsApp or email will carry, because every photo went in at full camera size.
You sell hoarding space. A client asks for a proposal, you pick twenty sites, and the system builds the deck: a slide per board, a photograph of each one, the rates and a cost summary. You go to send it on WhatsApp, where this client actually reads things, and it will not go. Email will not take it either.
Twenty sites came out at around 90 MB. Forty came out at around 180 MB. The deck was correct on every slide; it was simply too heavy to reach the person it was made for. So the proposal the software had just made in a minute went back to being a job done by hand: somebody exporting, compressing, splitting, or sending a link and hoping.
The cost was quieter than a lost sale. It was that the one document the software exists to produce arrived late or by a different route, and the conversation that was supposed to be here is your proposal became did you get the file. It was also costing storage: proposal decks were 39% of everything the system was putting away, and each time a deck was regenerated the previous copy was kept as well, so the bill grew with every edit.
What was actually going on
Every photograph of a board was going into the deck at the size the phone took it: 2,560 pixels across, 1.2 to 1.5 MB each. The slide showed it at a fraction of that size, but the file carried the whole thing. The software had no way to make a photograph smaller; nothing in it had ever been asked to.
The same fault sat on the other side of the screen. The site list in the office portal shows a small thumbnail of each board, about the size of a fingernail. Nothing had made a thumbnail either, so the list was fetching every full-size original to fill each tiny cell. A list of a hundred boards was pulling a hundred camera photographs.
We found this while costing the product, not while looking for a bug. Working out what one site costs us to host each month meant looking at what the system stores, and the storage arithmetic did not add up until we looked inside a deck.
What we changed
Photographs are now made smaller before they go into a deck: 1,600 pixels across for the large hero and proof images, 700 for the thumbnail strip. On a simulated capture that made the embedded image 5.7 times smaller. Every stored photograph also gained a thumbnail, and the site list serves that instead of the original, which measured 4.6 times lighter on the live system. The 107 photographs already stored were given thumbnails afterwards.
Then it was checked on the live system with a real upload rather than by reading the code: a 2,560-pixel photograph went in at 687 KB and came out at 39.9 KB, seventeen times smaller. That check caught a mistake of our own. The first version of it read a column the photo listing does not return, and reported that the fix had not run when it had.
What it did not fix
The log does not record what a 40-site deck weighs now. The measurements are of the photographs, not the finished file, so the honest claim is that the photographs are a fraction of their old size and the deck follows.
It did not change the retention. Every regeneration of a deck still keeps the old copy, because each one is filed under a fresh name and nothing ever removes the last. Smaller decks slow the growth; they do not stop it. And because the storage bucket is shared with another system, the real hosting cost of this product still cannot be read straight off the bill.
The mechanism
The resizing uses a drawing library that was already in the system for laying a client’s artwork onto a photograph of a board, so the deployment gained no new components. It has one trap worth recording: it takes image quality as a number from 0 to 100, where a browser takes 0 to 1, and passing 0.8 silently produces an 18 KB smear rather than an error.
Around the resize are guards, and they are the point. An image already under the size threshold is never re-encoded. A re-encode that comes out larger is thrown away. The file’s type is never relabelled unless the bytes actually changed, so a logo supplied as WebP or SVG passes through untouched. And every failure path returns the original photograph, so the worst outcome of the resizer failing is a large deck, never a broken one.
The deck in question is the one AdBoard builds from a site list, and a proposal is only a proposal once it has reached the client’s phone.