An approach to better quality performant front end code
If you are building a website of any decent size and with high traffic, there are a lot of things to consider when writing high quality frontend code for high traffic sites at scale. There’s a ton of things to consider for the rapidly evolving frontend stack before publishing a new a project, and my own practices have changed massively and improved throughout the years, so I thought I’d share some of the detail of my own approach. The following techniques, gotchas and dos and don’t are what I personally consider to be some of the best practice I’m using that delivers not only a better user experience, but more performant frontend code overall.
Use semantic HTML elements.
HTML5 brought with it many new elements that are designed to be more meaningful. For example
<nav> <section> <article> <footer>
and others are now much more appropriate to use to give semantic meaning to your page than using <div>
tags everywhere. These not only provide search engine robots with better understanding of your page architecture, but also make your code much more readable. In order to be backwardly compatible with IE 8, you’ll need to polyfill with HTML5 shiv. Yes, IE8. Because not every market has the latest and greatest browser and if you are building on high traffic websites you have to worry about that. 1% of 1,000,000 visitors, is 10,000 people you are potentially missing out on. Yes, it may be 2018, but those browsers are out there and good frontend code progressively enhances and caters for older browsers when it makes sense to do so.
Pick a CSS Framework.
Modular CSS provides you with a way to reuse blocks of HTML and CSS, so before you get started pick a modular framework convention like BEM, OOCSS or SMACSS. If you are building a site with more than a handful of pages, it doesn’t take long for CSS to become unmaintainable so sensible naming conventions and modularity are a must. Frameworks enforce those conventions that make you think deeper about the code you write.
Write CSS with reuse in mind.
Deeply nested overqualified selectors are also a code smell, not only do they suck for browser performance, (as the browser has to do several fetches over the DOM) but they add additional bytes to your load time and limit reusability. The best practise advise for your class selectors is to aim for two selectors deep at most.
.widget section table tr td h1.widget-title { padding: 10px 20px; font-weight: bold; font-size: 2rem; }.widget .widget-title { padding: 10px 20px; font-weight: bold; font-size: 2rem; }
Use a Module Loader
Large applications often need to load core javascript, then additional page specific javascript for particular pages. My approach for this is typically to use RequireJS multipage, although I’ve worked recently on a project that uses a modified version of Paul Irish’s code which is also pretty elegant. The key here is, don’t load JS you don’t need for large applications until you need it, and load it on demand.
Use Modular JS / AMD.
Using Asynchronous module definitions (AMD for short) — provide a great way to write reusable javascript code. Once you are using RequireJS, it makes sense to go down the AMD route. Why AMD? AMD implementations allow developers to define dependencies that must load before a module is executed, so wave goodbye to function undefined errors that can result from Javascript executing in a more linear fashion. AMD implementations also load smaller JavaScript files, and then only when they are needed. Good reading over here. and here on the official requireJS site. I prefer this approach rather than CommonJS, for many of the reasons detailed here. Essentially in my opinion AMD has much more flexibility on the lazy loading and dependency management front.
Minify CSS / JS to trim the fat.
JS and CSS should be optimised before being deployed to production, so setting your project up for that right from the get go with deploy scripts and well battle tested devops is a good way to ensure that the code you deploy is as trimmed of fat as possible. There are a few options for doing this on the server side. For Java, there’s WRO4J, JawR YUI + Ant. PHP you can use the PageSpeed module from Google. This is available on Apache and NGINX.
Lots of developers these days are jumping on Webpack as the defacto standard in frontend bundling and optimisation. However, with great power, comes great responsibility, it’s easy to end up bundling all your dependent modules into one file and spitting out a 1MB+ file even with production switches, particularly if you don’t know what you are doing with code splitting. Webpack configuration is something of an art to get right, so if you are using it to do your compression of your code for production you need to be careful what options you employ, and work out if a module loader with post deploy compression will better suit your needs than a module bundler. Veera Sundar has a nice hello world post that is easy to follow on how to use Webpack with code splitting that explains the process and how to get your configuration right.
Make it Validate.
There was a time in Internet land when everyone and their dog explained the merits of HTML validation. Browsers today do a pretty good job of understanding badly formed HTML and for the record, it’s of no benefit for SEO. However there is something about valid HTML code that pleases me. It indicates that you are dedicated to your craft, attention to detail focused, and care about the multitude of browsers that may be chewing over your HTML. After all, if your HTML is valid, then there’s little excuse for an existing decent browser making a mess of your page, and you’ve also future proofed your application. It also makes your code that bit easier to maintain, and picks up any potential bugs that you might have unwittingly introduced.
Only use JQuery when necessary.
Less is more, particularly when loading sites on mobile 3G / 4G connections so getting rid of Javascript that isn’t necessary is an important frontend task. Whilst jQuery is well recognised and provides a useful layer over native javascript, it’s not needed on every project. See if you can get rid of it altogether on greenfield projects and swap in native JS instead.
Don’t rely on JS to fix CSS problems.
There’s often instances where you can solve frontend problems more efficiently with just CSS, for example, equal heights can be solved with Flexbox. Slide menus for navigation, again, don’t necessarily need javascript and can be performant into the bargain. Need a slideshow? Don’t automatically reach for the Javascript. Although some of these techniques rely on modern browsers, you can always bring your JS in afterwards to support the older browsers and move the execution and loading of those scripts out of the modern browser pipeline.
Defer JS Loading when possible.
By default JavaScript blocks DOM construction and thus delays the time it takes for your browser to first render. Scripts which are not critical to how your page displays on first render should be made asynchronous or deferred until after the first render. How do you do that? A bit like this:
<script src="bundle.js" async></script>
Previously to move JS out of the critical rendering path in older browsers, we all used to script inject. However, that is no longer considered best practice. Moving your JS loading to the footer, adding an ASYNC attribute to your JS should be sufficient to letting the page get on with its thing prior to your JS executing.
Provide a Jank Free Experience.
According to the site dedicated to getting rid of it, jank is any stuttering, juddering or just plain halting that users see when a site or app isn’t keeping up with the refresh rate. Jank is the result of frames taking too long for a browser to make, and it negatively impacts your users and how they experience your site or app. It’s important that as front end developers we understand the causes, how to diagnose and how to resolve Jank issues when they occur to make the web as silky smooth as possible. 9 times out of 10, when I’ve experienced Janky behaviour, it’s typically been to do with direct binding to window scroll events without debouncing, inefficient fixed position CSS or non hardware accelerated CSS animations. Anyway, understanding how to diagnose this is something of an artform in itself, with much of the Chrome Developer tools providing lots of ‘under the hood’ diagnostics to assist with it. Some additional reading. [1] [2] [3]
Use CSS Sprites to cut requests.
Performance is important and every byte you can save downloading resources is money well spent, not only because of the bandwidth saving, but the overall performance of the site for end users. Sprites are a great way to cut down on HTTP requests, and speed up your site. All of the icons and smaller images on your site should be added to a single sprite, and then background positioning used with CSS to show the image. If you aren’t already familiar with it, the following CSS illustrates how that typically looks.
.icon{ background:url(sprite.svg); width:22px; height:22px; display:inline-block; } .no-svg .icon{ background:url(sprite.png); /\*fallback for rubbish browsers\*/ }.icon .twitter{background-position:0 0; } .icon .facebook{background-position:-22px 0;} .icon .pinterest{background-position:-45px 0;} .icon .linkedin{background-position:-67px 0;}
One resource I’m a big fan of which complements this technique is SpriteCow, which does all the hard work of working out the position of your sprites and spits out some CSS for you.
Use SVGs for scalable graphics.
Modernizr is a thing of beauty, and I use it on pretty much every build. This allows me to serve SVG’s to browsers that support it, along with PNG’s to browsers that don’t. It’s also a great way to work out if you’ve got flexbox support or not. If you aren’t already aware (and you should be) Modernizr is a feature detect library that provides additional classes on the HTML element of your page that gives you the flexibility to do this. A bit like this:
<html class="js no-inlinesvg video" />
That means you can write CSS code like the below. One sprite is served to the browser that supports SVG, and another for those older browsers that will only show a PNG.
.myclass { background-image:url('images/sprite.svg'); } .no-inlinesvg .myclass { background-image:url('images/sprite.png'); }
Provide double density PNGs for retina displays
Double Density devices are becoming more and more widespread now, so often your PNGs are often going to look a bit ropey and pixelated on those devices. You should be serving an image twice the size, and detecting the high density devices with a media query and serving that.
/\* for high resolution display \*/ @media only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) { .image { background: url(/path/to/my/highreslogo.png) no-repeat; background-size: 200px 400px; } }
Use REM or EM for sizing.
Great sites don’t dictate the font size for their viewers. Set a base font % on the HTML element in your CSS, then use REM units to set your typography to the correct size. If your design breaks when zoomed in the browser, then there’s px units used somewhere and the scaling won’t play nicely. Pixel density is one of the last things you can rely on with various user devices, so em-based units of measurement put the user back in control of legibility.
Don’t use Icon Fonts
Sorry all you fans of Font Awesome, but I’m not a fan. Required reading. Part 1. Required reading. Part 2. Oh and this:
- Font rendering is very inconsistent across platforms, so your icons can look pretty shit.
- The overhead of download a ton of icon resources to use one or two icons makes no sense.
- SVG support is sufficiently widespread, so the original rationale for using font icons no longer applies.
Don’t use them, Icon Fonts are performance cancer. Consider embedding whatever icons you need in SVG sprites as mentioned earlier.
Progressively Load Web Fonts.
We’ve come a long way from SIFR. Web Fonts now provide us with all kinds of of typographic joy, but they aren’t cheap from a performance perspective. Zach Leatherman is something of a web font pornstar and his article on font loading strategies is a must read for every front end developer. In fact, go read his entire blog on how you should be approaching font loading whilst remaining performant. Ebay also recently open sourced their font loading strategy and the linked article is also a good resource on how to improve performance.