My install package is 89MB. A lot of Japanese shared hosts cap uploads somewhere between 2 and 50MB. So the simplest distribution path I offer — download a zip, upload it through a control panel — just doesn't work for a chunk of the people I built it for.
Docker never told me this. A container doesn't care about a 90MB layer. A shared host's upload form does, and that's a constraint my whole dev setup was structurally blind to.
I blamed the wrong fonts
My first instinct was the heading fonts. The app bundles IPAex for headings — a few megabytes of TTF — and that was the obvious suspect. I even had a build report that agreed with me and pointed the finger there.
Then I actually unzipped the release and measured.
IPAex wasn't the problem. The heading fonts were about 9MB compressed — real, but not the story. The story was the PDF library's bundled CJK fonts. Two files alone:
-
Sun-ExtA.ttf— about 22MB on disk -
Sun-ExtB.ttf— about 17MB on disk
Those two, plus the rest of that library's font directory, were the single largest region of the package by a wide margin — roughly half the compressed zip. I'd been ready to optimize the 9MB I could see instead of the 40MB I couldn't, because I trusted a summary instead of the artifact.
Lesson one, before anything else: when something is too big, measure where the bytes actually are. Not where a report says. Where unzip -v says.
Why the fonts can't just go
The tempting move is "delete the giant fonts." I can't.
The app generates PDF invoices, and it has to render Japanese — including the rare Extension-B kanji that show up in real personal and place names (the kind where someone's surname uses a character most fonts don't have). Drop those fonts and the invoice renders tofu — little empty boxes — for exactly the customers whose names are unusual. For a compliance document, that's not a cosmetic bug. It's wrong output.
So the fonts are load-bearing. The size isn't waste; it's the cost of rendering a whole writing system correctly.
The design (which I have not shipped)
Here's where I have to be careful, because it would be easy to write this as a finished story. It isn't one.
What I've designed — and written up as an architecture decision, not yet built — is a deferred font pack:
- Ship only the minimal fonts the PDF engine needs to boot, plus the small heading set. That gets the base package under typical upload limits.
- Publish the big CJK fonts as a separate, versioned, signed artifact. Verify it on arrival with a SHA-256 and a bundled public key; refuse to use it if the signature doesn't match.
- Fetch it two ways: server-side over HTTPS by default, with a manual upload fallback for hosts that block outbound connections.
- Fail closed. Before generating a PDF, check that the required fonts are present. If they're missing and can't be fetched, don't emit a tofu invoice — stop with a clear error.
That's the plan. The architecture decision is written down and currently marked proposed; the implementation issue is open; there is no slimmed build yet.
Which means I'm not going to tell you it dropped to 20MB or 40MB or any number, because I haven't built it and measured it. I've seen enough of my own writing lately claim outcomes that hadn't happened. The honest version is: here's an 89MB problem, here's where the bytes are, and here's the design I'm going to try. If the slimmed number turns out interesting, that's a follow-up post with a real measurement in it.
The takeaway that generalizes
Two things I'll actually carry forward:
Distribution size is a first-class constraint, and Docker hides it. If you ship anything people install by hand — a plugin, a zip, an appliance — the size limit lives in someone's upload form, and your container-based workflow will never surface it. Put it in CI or a checklist, because you won't feel it otherwise.
Optimize the measured bytes, not the visible ones. The fonts I could name were not the fonts that mattered. I nearly spent effort on the 9MB in front of me because I hadn't opened the box.
What's unexpectedly blown up an artifact or image size for you — and when you finally measured, was it the thing you first blamed?
── Hideyuki Mori (Ayane International) 🔗 hideyuki-mori.com
Top comments (0)