Build in public

One Origin From Blank: The Dependency I Didn't Know I Had

By Arun Jalota · · ~5 min read

I blocked one hostname in a browser and my own site rendered zero characters of text. Not a broken layout. Not a missing image. A white page, no error, nothing for a screen reader to read and nothing for a recruiter to look at.

The hostname was a public CDN. The site is a single-page app that had no build step, which sounds like a virtue until you follow it through: with no build step, the JSX has to be compiled somewhere, and "somewhere" was the visitor's browser. So every page load pulled down a JavaScript compiler — three megabytes of it — plus the framework, plus a 3D library, all from the same third party. About three quarters of what I was shipping to a visitor was a compiler whose only job was to undo a decision I'd made for my own convenience.

I want to be precise about the failure, because the interesting part isn't that a dependency can go down. Everybody knows that. The interesting part is what the page did when it did.

Convenience dependencies become availability dependencies

Nothing about the original decision was reckless. Skipping a build step is a completely reasonable trade for a personal site: edit a file, refresh, done. The CDN is fast and widely used. The site worked, every time I checked, for months.

But "no build step" wasn't really the choice I made. The choice I made was move the compile step from my laptop to the visitor's browser, and that quietly moved something else too: my site's availability now depended on a third party's availability, on every page load, for every visitor. Not once at deploy time — continuously, forever, once per view.

That dependency doesn't only fail when the CDN has an outage. It fails on a corporate network that blocks unknown script origins. It fails behind a strict content-security policy. It fails on a flaky hotel connection where a three-megabyte download times out. Each of those is a real person, and the version of me that shipped this had no idea what any of them saw.

I hadn't chosen a convenience. I'd chosen an availability ceiling, and set it somewhere I couldn't see.

Blank is the worst possible failure

Here's the part I'd defend to anyone: the outage isn't the bug. Blank is the bug.

A page that fails and says so is fine. A page that renders the text and loses the animation is fine — good, even. A page that renders nothing has thrown away information it already had. All the words were in my repository. They were three hundred milliseconds away on the same origin the visitor had already connected to successfully. The HTML shipped, parsed, and then sat there empty because the only copy of the content that mattered was behind a compiler that never arrived.

I had actually written a fallback — a full text version of the bio, career, and essay list. I'd put it inside a <noscript> tag. That tag means "show this if JavaScript is disabled." It does not mean "show this if JavaScript is enabled and then fails," which is the failure that actually happens to people. JavaScript was enabled. The browser dutifully ignored the fallback and rendered the empty container instead.

That's a small, specific mistake, and I think it's a common one. We reach for <noscript> and feel covered. It covers the case that basically never happens and misses the case that does.

What I changed

Three things, in order of how much they mattered.

The content is in the HTML now. The static bio, career history, and full essay index live inside the app's root element as real markup. When the app mounts, the framework replaces it — that's just what rendering into a container does. When the app doesn't mount, it stays. An inline first-party script hides it immediately so there's no flash on the normal path, and a three-second watchdog puts it back if the app hasn't taken over. Nothing in that degradation path touches the network. A fallback whose delivery depends on the thing that failed isn't a fallback.

The compiler doesn't ship anymore. The JSX is compiled ahead of time by a script in the repository, and the compiled output is committed. There's still no build step on the host — it's static files — but the compiler is now a tool I run instead of a payload the visitor downloads. The home page went from about four megabytes to under one hundred kilobytes.

The remaining libraries are served from my own origin. Same bytes, one fewer party. If my site is up, its dependencies are up, and that sentence is now true by construction rather than by luck.

The test I should have had from the start

The fix is a day. The lesson is the test, and the test is four lines: open the site in a real browser, abort every request that isn't first-party, and assert that the page still contains more than a thousand characters of text.

That check now runs on every build, alongside one that disables JavaScript entirely and one that blocks all scripts and confirms the watchdog restores the static page. They are the cheapest tests in the suite and they cover the failure that would have embarrassed me most — a hiring manager opening my site from a locked-down corporate network and seeing a white rectangle with my name in the tab.

I've written before about designing AI features that fail soft, and about operating code you didn't write. This is the same argument pointed at myself, which is the version that stings. It's easy to hold a team to graceful degradation on the product and never point the question at the thing with your own name on it.

The uncomfortable bit is how long it lasted. The site never broke while I was watching. It worked on my laptop, on my phone, on every network I happened to use. The failure mode existed the entire time and was invisible because I only ever tested the happy path — which is the exact thing I tell other people not to do.

You don't find this class of bug by using your product. You find it by attacking it.

So: pick the thing you own that matters most, open it in a browser, and block one origin. Not a hypothetical origin — the real one, the one in your script tags. Then look at what a visitor gets. If the answer is "nothing," you don't have a performance problem or a dependency problem. You have a content problem, and the content was sitting right there the whole time.