Four sentences somebody could check
I've started writing progressive enhancement down as four claims that can actually fail, plus two counters that change what the argument is even about.
A client asked what progressive enhancement meant in practice. The 4 of us on the call managed about 90 seconds of gesturing.
Nobody was trying to trip us up. They’d read the line in our proposal (it sits between a line about accessibility and a line about performance) and they were curious. We said the site sort of works without JavaScript, and that it degrades gracefully, and that we build from the content out.
None of that is wrong. None of it is checkable either. They couldn’t have taken any of those sentences away and used them to figure out whether we’d done what we said. Neither could we, and that’s the part that stuck with me.
That was in the spring. I left in June and I’ve spent the couple months since in-house. The company still works with an agency and the proposals are the agency’s. The parts of the site I look after come with no proposal, no signature and nobody who’s ever going to ask.
It turns out that’s a good test of whether a thing you say you believe was doing any work.
A belief you can’t fail
I think this is why the phrase has had such a strange decade. People hold it as a philosophy and a philosophy can’t be falsified. So it survives contact with a deadline by quietly turning into a preference.
And honestly, that’s a comfortable place for it to live. When the schedule gets tight, progressive enhancement isn’t a requirement getting dropped. It’s a value getting deprioritized. Nobody has to write that down anywhere or tell anyone it happened.
I’ve done exactly that. Nobody has ever come to me at the end of a project and pointed out that the thing we promised at the start didn’t happen, because nothing we promised at the start was specific enough to not happen.
So I’ve been writing it the way I write a browser support matrix instead. It’s a small number of statements about what works under what conditions, each of them either true or false about a given build. I’m not doing it because a claim is morally better than a value. I’m doing it because a claim can come back later and tell me I was wrong.
Four sentences
Every page’s main content is in the HTML the server returns, instead of being assembled afterward.
Every form can be completed and submitted without JavaScript, and gives the person a sensible response when it is.
Every navigation is a link with a real href that still works if you open it in a new tab, copy it or bookmark it.
Enhancements go on top of all that, and their absence leaves something usable instead of something broken.
Each of those takes about a minute to check. Turn JavaScript off in DevTools and walk the two or three journeys that make the store money. If the content is there and the form submits and the links go places, it’s true. If it isn’t, it isn’t, and now it’s a bug with a ticket on it instead of a value somebody deprioritized in a meeting nobody took notes in.
You can assert the first one in CI too. That’s what made it feel real to me instead of tidy. A check for it is a handful of lines that fetch the deployed page and assert that the product name and the price appear in the raw HTML response. It catches the regression that would otherwise slip in quietly with somebody’s refactor a year from now.
But nobody turns it off
True, but it’s answering a question nobody asked. The realistic failure cases have nothing to do with anybody having an opinion about JavaScript.
The bundle didn’t finish downloading because a train went into a tunnel, or a corporate proxy mangled it on the way through. Or a blocker took out one script and the error cascaded through everything queued behind it. Or the CDN had a bad few minutes, or an older browser hit a syntax it couldn’t parse and gave up on the whole file. Most often of all, one of those third-party scripts I keep going on about threw early and nothing after it in the queue ever ran.
None of those is a person with a principle. They’re all just a normal day.
The published figures I’ve seen for how often the JavaScript doesn’t run on a big site land somewhere around 1 in 100 sessions. That sounds like nothing until you multiply it by a month of traffic. Then it’s a few thousand people looking at a spinner, or at a blank div where the thing they came for is supposed to be.
Not one of them is going to contact you about it. They leave because leaving is easier.
Go and count them
If you keep one thing from this post, make it this one. It takes an afternoon.
Put a counter in the HTML that the server increments when it renders the page. Put a second counter that only fires from JavaScript, after your bundle has parsed and run. Then leave both alone for two weeks.
<!-- rendered server-side, always present -->
<img src="/beacon?stage=html" width="1" height="1" alt="">
<!-- fires only if the bundle parsed and ran -->
<script>window.addEventListener('load', () => {
navigator.sendBeacon('/beacon?stage=js');
});</script>
The ratio between those two numbers is your JavaScript failure rate. Nobody has to trust a figure from an article, or weigh my opinion against the marketing agency’s, or agree with a position first. It’s yours, measured on your own site for the people who actually come to it.
Every time I’ve done this, the number has come in higher than anyone in the room guessed. That includes me, which I mention so it’s clear I’m not grading my own homework.
The conversation afterward is a different one entirely. You’ve stopped arguing about whether progressive enhancement is good and started figuring out what should happen to the few thousand people a month for whom the enhancement never arrived. Those are not the same meeting.
It’s the same move as putting a number on performance instead of asking everyone to care about it a lot. People will argue with an opinion forever. They mostly don’t argue with a counter.
Where I’d lose the room
I’d lose credibility fast if I pretended this scales to everything. So here’s the half I’d concede before anybody makes me.
Some things really can’t be done without JavaScript: a rich text editor, a drag-and-drop page builder, a live dashboard, an interactive map. Insisting on a no-JavaScript version of those is obstruction, not principle. I’ve watched somebody argue for it and lose a room they deserved to lose.
In those cases the four sentences don’t ask for a full alternative, only a graceful failure and a route out of it. A map degrades to an address, a zip code and a link that opens a mapping app. A drag-and-drop reorder degrades to a number field and a save button, which takes an hour and covers the keyboard case while you’re in there. A dashboard degrades to a page that says what’s wrong and offers a refresh, instead of an empty gray rectangle that looks exactly like a broken site (on account of being one).
And I’d concede the other half too. “The base layer is free if you start with it” is a thing developers say, me very much included. It’s mostly true, just not always.
On genuinely application-shaped products, that base layer is real work. It belongs in the estimate as real work instead of getting smuggled in as a principle by whoever is writing the estimate. I’ve been that person. It’s not a great way to make friends with whoever inherits the number.
Nobody to promise them to
You write this down at the start instead of the end because it’s one of very few things in this job you can’t retrofit at a sensible price. Adding a server-rendered base layer to a finished client-side app is a rebuild with a rebuild’s number on it. Starting with one and layering on top costs some discipline and almost no money.
That was easy to say at the agency, where there was a document with a signature at the bottom of it. On my parts of the site the four sentences are exactly as true. But there’s nobody on the other side to hold me to them.
So they live in a README in my corner of the repo, which nobody reads. Both counters have been running on those pages since the end of July. I’ve sent the agency the first two weeks of numbers and the first sentence, in case they want it for their next proposal.
I still don’t know whether a claim needs somebody on the other side of it to stay a claim. A support matrix works partly because a client signs it. A budget works partly because somebody has to defend the number out loud.
Four sentences in a README with nobody attached to them might just be a belief again in better clothes. I won’t be able to tell for a while.
When I went looking, I found remarkably little written about this as a practice instead of as a position. There are a lot of people in favor of it who’ve never published what they actually promise. If you’ve kept something like this alive in-house for more than a year, I’d really like to know how, because I’m two months in and I have no idea yet.