The personal website for Brandon Steili. A work in progress - both the site and the human.

← Writing

Essay

What Broke When I Actually Lived on My Own Software

Published2026·07·11
Filed underosshp
The osshp logo

I've mentioned before that shipping v0.1 of osshp wasn't the finish line, it was the point where I moved steili.com onto it and started actually running my life through it. I've listed the punch list that came out of that briefly in other posts. This one tells the actual stories, because the debugging in each one taught me something the bug report alone didn't, and because "it worked in my tests" turned out to be a much lower bar than I thought it was.

The iPhone photos that just... didn't upload

The first real content I tried to put into the Photos module was a batch of pictures from my phone. Most of them failed. Not with a helpful error, just failed.

The cause was almost embarrassingly obvious in hindsight: iPhones shoot HEIC by default, and the platform simply didn't know how to decode it. I'd tested the photo pipeline, of course, but I'd tested it against a small set of sample JPEG fixtures I'd put together myself, because that's what was convenient to have lying around during development. Not one of them was HEIC, because I hadn't thought to go grab an actual, unmodified photo straight off a phone and run it through the pipeline. The fix was building a real conversion path using heic-convert, and the thing that actually validated the fix wasn't a new unit test with a synthetic fixture, it was pointing the pipeline at the exact photos that had failed the first time and watching them go through. "The platform doesn't support the default output format of the most common camera in the world" is not a bug you can leave unfixed on a personal photo site, but it's exactly the kind of bug that a test suite built entirely from convenient fixtures will never catch, because convenient fixtures are, almost by definition, not the messy real thing.

Photos that displayed sideways, upside down, or on their side depending on which phone took them

Once HEIC uploads actually worked, a chunk of the photos that made it through the pipeline still looked wrong: sideways, upside down, rotated 90 degrees from how they were actually taken. This one took a little longer to pin down, because the images themselves weren't corrupted and the resize step wasn't throwing any errors. The problem was ordering, not correctness in isolation.

Modern phones don't rotate the actual pixel data when you take a photo held sideways; they write the correct orientation into the image's EXIF metadata and let the viewer rotate it on display. osshp's resize pipeline strips EXIF data on upload, for the same privacy reason it strips GPS data: nobody publishing a photo wants location or device metadata riding along invisibly. The bug was that the EXIF orientation tag was getting stripped along with everything else, before it had been read and applied to the actual pixels. The fix wasn't complicated once I saw it: apply the rotation implied by the EXIF orientation tag first, bake it into the actual pixel data, and only then strip the metadata. But finding it meant actually looking at photos taken in portrait orientation on more than one device, because a landscape photo from a camera that writes orientation as "normal" will never expose this bug at all. If every photo I'd tested with happened to be shot in the default orientation, I'd never have found it.

The upload that failed silently on one specific photo

This one was maddening for about twenty minutes before it became obvious. A single, large, high-resolution image failed to upload, with a generic error and no useful detail about why. Smaller images from the same camera worked fine. It wasn't a corrupt file; I could open it, edit it, everything about the file itself was ordinary.

The actual cause was a request-body size cap, set at a level that every fixture I'd ever tested against comfortably fit under, because my test fixtures were, again, small, convenient files I'd put together for fast iteration during development, not realistic worst-case inputs. A genuinely large photo straight off a modern camera or phone sailed past the size my test data had ever exercised and hit a ceiling nothing in the test suite had ever come close to. The lesson here isn't subtle, and it's one I should have already internalized from the HEIC bug: test with realistic inputs, not the smallest thing that makes the test run fast. A tiny fixture that never hits a real limit isn't testing the limit at all, it's testing that the code runs, which is a much weaker claim than it sounds like.

The best one: every visitor on the internet, resolving to the same person

This is the bug I'd tell first if I could only tell one, because it's the one that most clearly could never have shown up anywhere except on the real, live site, behind the real network path, with real visitors hitting it.

steili.com runs behind a Cloudflare Tunnel, which is a normal, supported way to host something from behind a home connection with no port forwarding. The way that works is the request comes in through Cloudflare's edge, gets proxied through the tunnel, and arrives at the actual app having passed through more than one hop before it gets there. Each of those hops can append to the request's forwarded-for header chain, and the app has to know how many hops of that chain to trust in order to correctly extract the real, original client IP from it. Get that hop count wrong, and every request looks like it came from the same address: whichever internal hop the app mistakenly decided was "the client."

That's exactly what happened. The proxy-hop count was miscounted, so the app resolved every single visitor, no matter where they actually were, to the same internal IP address. Two things downstream of that quietly broke at the same time, and neither one announced itself with an error. The rate limiter, which exists to stop any one client from hammering the site, started treating all traffic, from everyone, as a single client, because as far as it could tell, it was a single client. And the analytics module's unique-visitor count, which depends on being able to tell visits apart, collapsed toward one, because from its point of view, one person had visited the site an enormous number of times and nobody else had visited at all.

Nothing about this was visible in local development, because local development doesn't have Cloudflare's edge and a tunnel hop in the request path at all; the app just sees a direct connection and never touches the hop-counting logic that broke in production. It wasn't visible in a staging environment run from my own machine, for the same reason. The only place this bug could exist was the actual production deployment, behind the actual tunnel, receiving actual traffic from more than one real visitor at the same time, which is the only condition under which "did the unique-visitor count go up" is even a meaningful question to ask.

The shape of all of it

None of these four bugs were on any spec, any design doc, or any pre-launch checklist. They share a common cause: each one only exists at the intersection of real content, real devices, and a real network, and every one of those three things is something a development environment approximates rather than reproduces. Sample fixtures approximate real photos. A laptop hitting localhost approximates a real network path. Neither approximation is wrong to build with day to day, but neither one will ever surface the bug that only exists in the gap between the approximation and the real thing.

That's the actual argument for dogfooding on your real site, as early as the software can hold real content, and it's stronger than "eat your own dog food, it's good practice." It's that some entire categories of bug are structurally invisible anywhere except your real, live, actually-used deployment, and the only choice you get to make is whether you're the one who finds them there, or your first real user is.

— end —