Skip to content

Progressive Enhancement

A few weeks ago I gave a talk about Progressive Enhancement for the Meteor JS Cape Town meetup group. I was quite nervous because I was expecting a tough crowd: Meteor’s beliefs and decisions run kind of contrary to my own. As it turned out, the crowd were lovely (and perhaps I was able to be a little more pragmatic than usual, rather than the fire and JavaScript brimstone that I often spout). Here’s a write-up of the talk.

I think that we should not trust our JavaScript. Have you ever clicked on a button and nothing has happened? That’s probably JavaScript, messing with you.

Here’s a fancy To Do app, built using Meteor, a client-side MVC framework. If everything is ok, this is what our users see. If one of many things went wrong with our JavaScript, this is what they see:

Not even the title or an error message. Just a blank screen. Why? If we View the Source we can see: there’s basically no HTML, no content. It’s just a script tag that loads everything in.

So am I saying don’t use Meteor? Not exactly. Well, maybe a little bit.

So, am I saying don’t no client-side JavaScript MVstar frameworks at all? Not exactly. Well, maybe a little bit.

What am I definitely saying is that we need to be mindful of the cost of our choices.

Some links about the content of this talk are up at naga.co.za/pe

I’ll cover two big things: JavaScript fails and face palms, and Progressive Enhancement. I’ll talk about the theory of PE, the practical implementation of it, and some reasons to love it.

To be clear, I love JavaScript. I think it’s amazehorse. But I certainly don’t trust it. Some things can only be done with JavaScript.

You can use JavaScript to deliver a rich, fast (once your JS is loaded) experience that’s not possible with just HTML and CSS. You can use Ajax to avoid pull page refreshes, and you can access cool device APIs like geolocation.

And if you use Service Worker, you can even enable our stuff to work offline. That is seriously amazing. Although it’s still quite bleeding edge.

When I use that JavaScript, though, I’m making assumptions. Assumptions about my users network, their technology, and more. I’m assuming that everything is like my dev environment: it works on my machine! I’m assuming that everything is okay.

So, I want to ask: why would my JavaScript fail?

I think there are three broad categories as to why this could happen.

  • Is the file there?
  • Did the user get it?
  • Did it work?

Let’s look at each category in a bit more detail.

Is the file there?

What if the browser gets a File Not Found error? What if the file has the wrong name? What if the upload of the file to the server didn’t complete?

What if the CDN is down? Sure, if might not happen that often, but it does. If you’re using jQuery or Bootstrap, chances are you’re running it from a CDN.

Did the user get it?

I really like this quote from Jake Archibald on Twitter:

”We don’t have any non-JavaScript users"
No, all your users are non-JS while they’re downloading your JS

This is especially true for client-side MV* frameworks as their JS payload tends to be on the large side, which means it will take a while to download.

The user has JS turned off

Yes, they probably don’t. It’s a tiny percentage of people who have JS turned. But if you’re serving thousands of people, or ten of thousands, or more: that tiny percentage adds up to a lot of people.

Personal Firewall

What if a user’s personal firewall is being a little over-zealous? What if you set up a friend’s PC over the holiday, installed some anti-virus software for them and cranked the settings up to the maximum?

ISP “Helping”

ISPs like to fiddle with the data they gets sent to you. Dropping in ads, fiddling with images, and sometimes flagging things as suspicious that aren’t. Like large JavaScript files. Like jQuery.

Company Firewall

What if you have a systems administrator where you work who errs on the side of paranoid. They will protect your network at all costs: that means none of that dangerous JavaScript is coming into this office, no ways.

:poop: connection

What if your user is on a crappy connection? What if they’re on a crappy cell connection not a good cabled connection? What if the speed is slow, the latency is big, or the signal is weak? Any of these might mean the user doesn’t end up getting your JS file(s). This is maybe our biggest problem at the moment: mobile web use is huge.

Did it work?

Wonky deploy

What is the file was only partially uploaded? What if the build breaks, but the code goes out? What about the npm fiasco of a few weeks ago (with left-pad) where one repository was removed and many builds broke?

Tpyo in your JavaScritp

I know what you’re thinking: “I don’t make typos!” Your time will come ( ͡° ͜ʖ ͡°). Uncaught errors happen, even though we try very hard to avoid them.

Browser extensions

Browser extensions are things specifically designed to mess with our web pages. Usually in a good way, but still. It shouldn’t come as a surprise if one of them stops your JavaScript from working properly.

Ad blockers

Are you running an Ad Blocker right now? Lots and lots of people are. Or do you have an iPhone and you’ve switched on the iOS JavaScript blocker?

Earlier I asked “Why would my JS fail?“ Given everything it’s up against, maybe a better question is why would my JS succeed?

I’d like to introduce you to the spirit animal of Progressive Enhancement.

I would, rather cheekily, like to suggest that Progressive Enhancement is a better way to build web apps. (It’s cheeky because “a better way to build web apps” is the Meteor Cape Town meetup group tag line :D ).

PE says be prepared, be paranoid. It says use feature-test driven development.

I’m going to run through some theory, then some practical.

PE: The Theory

Don’t depend on JS for core functionality. The emphasis here is important. It doesn’t say don’t use JavaScript. And it doesn’t say don’t depend on JS for some of your functionality. It just says don’t depend on JS for core functionality. Like buying a ticket. Or posting a picture.

PE says make it work, then make it work better.

Build the simplest version of your thing first. Then provide an enhanced experience for people with better tech. And add it in add layers. If you already work work in an agile software development environment this might sound familiar. PE is an iterative process, like you work already.

Mobile First Responsive Web Design is especially interesting. It’s Progressive Enhancement for layout. Make it work (build the small screen version), then make it work better (add Media Queries for larger screen sizes).

PE says support all browsers, and optimise for some. We should support pretty much every browser out there. Why shouldn’t we when we’re providing the simplest version of our experience as the baseline? Of course you won’t spend weeks making your site work the same in older browsers: you’ll optimise for the most popular ones for your audience. That means that different users will get different experiences, but that’s okay.

Web sites don’t have to look exactly the same in every browser.

Web sites don’t have to work exactly the same in every browser.

In fact, I would go so far as to say that web sites don’t have to smell exactly the same in every browser.

Web sites certainly don’t have to be experienced exactly the same in every browser.

With the huge variety of screen sizes, operating systems and browsers, we’re providing layers of experiences to our users whether we like it or not. If we don’t do anything it’s an on / off switch. Wouldn’t it be better to provide more than that?

I’m a big believer in the One Web. Maybe because I’m a hippy at heart. I love the things that Sir Tim (Berners-Lee) says about the web. Things like “The power of the Web is in its universality” and that everyone should be able to access all of the web, regardless of the device they’re using, the type of connection they’re on, or any disabilities they might have.

PE: The Practical

Now that the hippy stuff is out of the way, let’s look at the practical side of things.

Here’s a triangle, yesterday. It shows what every page is made of: an HTML base; a CSS topping; JS sprinkles on the top.

Something important to note is that HTML and CSS are fault tolerant. This is by design. If you’re writing HTML and CSS, you’re doing PE without realising it!

<img src="cat.jpg" alt="A cute cat" />

If you include an alt attribute on an img tag, what happens if img doesn’t load? Say, if the file’s not there, or the user doesn’t get it? The alt text displays.

<input type="email" />

If you use the new and shiny email type of attribute, what happens if the browser doesn’t support type = email? It renders as a regular old input type of text.

What about some CSS examples?

background-color: purple;
background: linear-gradient (135deg, red, blue);

If these styles are being applied to an element, what happens if the browser displaying the page doesn’t support gradients? The browser ignores the second line and the (already applied) first line takes over: the background colour will be purple.

By comparison, JavaScript is not fault tolerant. It stops with one bug. This is also by design.

if ('querySelector' in document) { ... }

This feature test in JavaScript tests our assumption. Before using querySelector, we ask “hey, browser, do you know about querySelector?” and only if the browsers answers yes, do we execute the code that uses querySelector.

if ('querySelector' in document &&& 'localStorage' in window && 'addEventListener' in window) { ... }

Here’s a bigger chunk. This is the BBC News team’s “cutting the mustard.” They send everyone the base experience of HTML and CSS. “Modern” browsers, ones that pass the test, get a JS-enhanced, fancier, experience. They add more layers inside this test. A more layered approach can often be better. Split up the places that you use features, and block those off.

if ('querySelector' in document) { ... }

if ('localStorage' in window) { ... } 

if ('addEventListener' in window) { ... }

An important part of this is that we’re using feature detection, and not device detection. That means we’re grading components, not browsers. In fact, we don’t care what browser a user has: we only care what features their browser supports.

Why I love PE

It’s a security blanket. Building with PE means we don’t need to test so much, especially in older browsers. That’s because we have a baseline experience. If all our JS fails, our users still get a useful and usable experience.

Another reason I love it is that it gives a faster initial load time. You’re just sending HTML and CSS down the pipe, and that renders fast. And fast pages means happy users.

Pages built with PE are also easier to archive and index. Search engines and spiders are getting better at indexing Js-heavy pages, but still.

It also means that we can reach more people. Instead of drawing a yes / no line in the sand and providing a bunch of people with a blank screen, we’re giving people a range of experiences.

I also believe that building with PE is the right thing to do for majority world (like here!). It means removing some of the barriers to entry. No requiring a fancy phone or a bleeding edge browser.

You might be thinking “But everyone has a smartphone now!” Well, this is only kind of true, and not all smartphones are created equal. The new, cheap, Android phones don’t have much RAM or a fast CPU. That means that their JS performance (in terms of parsing and executing the cide) is, to use a technical term, kak slow. About 10 times slower compared to a laptop browser.

On top of that, Opera Mini is a very popular browser locally. Depending on where you get your stats, Opera Mini accounts for about 30% of SA’s web traffic. An interesting feature of this browser is that it doesn’t do ajax: every JS action means a new request. Now, Chrome also account for about 30% of the traffic, but the split between mobile and desktop isn’t clear. Anyway, back to PE lovefest.

A little organisation know as the W3C has this to say (their Priority of Constituencies):

In case of conflict, consider users over authors over implementers over specifiers over theoretical purity.

Users first. And we, developers, aren’t even second: we’re third! PE says put our users first. Our users convenience is more important than ours.

If you’re familiar with User-Centered Design, all this might sound familiar. Progressive Enhancement is UCD-flavoured Front-end development.

So, to recap: don’t trust your JavaScript. We need to ask:

  • Is the file there?
  • Did the user get it?
  • Did it work?

I believe that we need to be mindful of the cost of our choices.