 Hey, I'm Surma. I work as a developer advocate for the Chrome team in London. And I am here to talk about my most recent mini project that I did, which is that I built a proof of concept, PWA, but using WordPress as the back end. And so in my title, I have two words, WordPress and PWA. And I feel like I should probably justify both of them. So let's talk with the other one, why PWA. Because most people, I think, when they hear PWA, they think about installability, about getting into the home screen. And that's not something you necessarily want for your blog. Like you don't really care about your blog being an app on your home screen and triggering that banner. So maybe the name is a little misleading. It's not a progressive web app. In this case, it's more of a progressive website. And that's OK, because PWA doesn't really necessarily mean that you need to be app-like. And also, I think people never thought about this or approached this, because they were like, hey, my CMS has been around since way before the acronym PWA was a thing, so obviously it can do PWA. And that is wrong, because you can use all these new PWA technologies with a very old system that are already in place. And that's what I basically want to prove with this project, have an existing CMS, have an existing infrastructure, and still turn it into a PWA. And I think the bottom line, as Chris said earlier today, it is about drastically improving the user experience. And because we all know flaky networks are a pain, slower than websites are really awful, and this can help. And all decisions that you make during development should be made from the user's perspective. So the second word in my title is WordPress. And the question might be, why WordPress? Why not Umla, or Drupal, or any other kind of CMS that is out there? And maybe I should have used another acronym in my title and just called it CMS plus PWA, because that just rolls off the tongue right away. And it doesn't matter. This talk is not going to be WordPress specific. It's about having an existing CMS. And all techniques that I show will be WordPress agnostic, because I didn't need to know anything about WordPress, really, to make this happen. The reason why I did choose WordPress, though, because they are, by many standards, the most popular CMS out there. They have a 60% market share in the CMS world, so to speak. And they have a stat that says that 27% of websites, WordPress websites, just take that number in 27% of websites. But just to be clear, if you are logged in to your weirds, home, in-house, pearl-written, custom-built CMS, this talk is still useful to you. I have not known anything about WordPress at the start of this project, and I still don't know much, because I didn't need to know much to make this entire thing happen, and to have good performance, and have all these PWA features. So to talk a little bit more about what WordPress is currently doing in the PWA world, I would like to invite Dan Wormsley onto stage from Automatic. Hi, my name is Dan Wormsley, and I am a code wrangler at Automatic. I couldn't help but overhear, like, he sort of glossed over it, but he's like, a very old system, and I think it's only like 12, 13 years old. But, you know, we live in technology, and things move fast. So Automatic is the company behind WordPress.com and WordPress VIP, and WooCommerce and Jetpack, which is a plug-in for WordPress. And Automatic was founded about 12 years ago with the goal of accelerating WordPress adoption, and I think you could argue that we didn't do too badly. You'll notice that this is 28%, so it has not updated its slide. I'll forgive him, this number grows very quickly. But it's now 28%. So I think one of the reasons behind the growth of WordPress and the success of WordPress is that our mission is to democratize publishing. And this is sort of a mission that is deliberately very ambitious in scope, and it is underscored by a philosophy. And this philosophy has really stood the test of time. It says that we should always be making software that's incredibly easy to use, to install, that it should ship with sane defaults, and I think crucially, we should always be trying to reach out to people who may have been left behind, web users who for reasons of language or geography or the kind of browser they use or the kind of network they're on have not been able to use the web as effectively as everyone else. And so over the years, we've engaged in some really ambitious projects to try and live by that philosophy. So starting with the famous five-minute install, Backwind's content management systems took 15 steps in some arcane wizard to get installed. We just asked you a couple of questions and everything else was done for you. WordPress, every release comes out in 80 languages or more growing every year. We have a responsive and accessible UI, which is really important for the over one billion people in the world living with disabilities, and we scale from a USB stick to a massive installation like WordPress.com, which is one install of WordPress serving millions of websites from data centers all over the world. So every year brings new challenges, new ways to live by this philosophy, and 2017 is no different. So in 2017, users expect to be able to create rich websites with no programming required and with no sort of messing about with sort of text-based widgets or custom elements. And so we're creating the Gutenberg editor, which is coming out in WordPress 5 and it enables really rich block-based editing that's fully pluggable and usable by people. And I think equally important in 2017, it's the year of web fragmentation, which is a huge challenge you've been hearing about all day. But basically, people are accessing the web on more devices and more kinds of networks than ever before. And this is like a really important challenge for us as web developers, because most of us are using the web on fast computers, on fast and reliable networks, and it's not always clear to us who we're leaving behind. And of course, with WordPress, we're always asking who are we leaving behind, how can we help them access the web better? Now I want to pause just for a second and talk about Jetpack, because I mentioned it in passing at the start. So Jetpack is the plugin that powers WordPress.com and is also available to download on any WordPress website. And why Jetpack is important today is because it powers over five billion page views a month. This is part of the scale of WordPress. It's one of the more popular plugins and it powers WordPress.com and VIP and all of our other products. And so Jetpack has this incredible reach, which is both an amazing opportunity and a really big responsibility to make sure that the people who are using Jetpack are not left behind, are having a great experience. And I also want to talk about the work we've done with Google in the past. So automatic and Google were both pitching in on the HTTPS Everywhere initiative alongside Let's Encrypt, alongside the EFF. And this has borne incredible fruit. We now have, I think, 10x the number of websites in the top million versus two years ago, running HTTPS, which is important because it's a foundational building block of progressive web apps. And also, we collaborated with Google to create the AMP plugin for WordPress, which I think is probably by far the biggest way that AMP content is rendered on the web, powering billions of page views a month. So this partnership between automatic and WordPress has been really powerful. So we got together and decided to investigate, well, could progressive web apps provide a great experience, not just on a site by site basis, but in a cross-cutting basis? Could you actually put progressive web app technologies into Jetpack and get them out in front of people in a way that works with any theme with any other plugin out of the box so that we can get these benefits to people? And so we have some really exciting preliminary results. So what Serma has been talking about today has been how you might take an individual theme or an individual site and tailor it to use progressive web apps. What we're talking about is basically one click with a site running this experimental version of Jetpack and you get almost 100s across the board in Lighthouse. And what we've seen is on slow 3G, with an image-rich homepage, say a photo blog, you can get 10 or 15x the load performance on that site, sort of the time to your first content paint. Now, how do we do this? There's a lot of hackery going on under the hood, but a lot of it is sort of traditional web hackery about how you do deferred and async scripts and inlining scripts and these other things combined with progressive web apps, which let us kind of mangle the first page load to make it super fast. And then in the background, load all the remaining assets for the other pages so that you can have a traditional web experience that starts and loads quickly. So that's just some of the things that we've discovered that are possible with progressive web apps and WordPress and I would love to turn it back to Serma for some of the nuts and bolts. Cheers. All right, enough of the inspirational speak. Let's get down to the nitty gritty. So this is the blog that I built. It had a lot of features I set for myself. I wanted to be really fast on the first load. I wanted to have nice transitions. I wanted to have a really bandwidth conservative approach to loading all these things, work offline, full PWA. But what I also wanted to do is I wanted to target low-end devices because I wanted to make, it's a blog. It should not require a flagship phone to render a blog. So basically all my development that I did happened on a Moto G4, which is the phone that we always advocate for as a baseline for low-end devices. It's not very expensive. You can order it on Amazon. And basically my workstation just looked like this with always my development server running open on the phone so I could see that the animation would still be running smooth even on a constrained phone like this. Along the process, I basically documented everything. So whenever I did something, I wrote in a dog and as you can see, I ended up with like 11 pages of me just ranting really. And this document is really R-rated and not fit for public consumption at all. So I will not hand that out. What I will do though is I do a YouTube series called SuperCharge. And I'm working on the next installation where I will basically cover one feature of this blog per episode and just do a step-by-step thing on how you could implement this feature or how I implemented my features and maybe you could use those to use it on your blog. So definitely stay tuned on that one. Like and subscribe our YouTube channel, please. Either way, let's get started. So if you are a theme author or you have a theme running on your WordPress blog, it's important to me that you know that this is not an all-on-nothing thing. You can cherry-pick the features that you deem more important for your blog. Like you don't have to do it the same way that I did. This is a proof of concept. I wanted to show it is possible, but there's many different solutions that might work or might work better for you and you don't have to do everything the same way. Again, bottom line, improve your UX. So let's get started with the first thing, a feature that is very important to me is the time-to-first contentful paint, the TTFCP. It's a great acronym. It basically means how long does it take from the second I hit Enter or just navigate to a page until the content is on screen? It doesn't need to be fully styled. It doesn't need to be interactive yet, but the content needs to be there. And for blogs specifically, this is very important because the content is your content. It's the thing that you want to ship to your users. And I set myself the goal to get that under one second on 3G, which is fairly ambitious, but it can be done. And this is the feature that you really notice when you're on vacation and on hotel Wi-Fi, when you're roaming on 2G, when you're in a country where bandwidth is still scarce. So statistics have shown that when you make a user's weight, and we've heard this today a couple of times, if you make them weight, they're gonna bail on you. So your mission should be get on screen as fast as possible, get them busy, get them engaged with your site one way or the other. And I achieve this in this case by putting everything for the first contentful paint into my index HTML. That basically means if the user only gets the index HTML and then the network breaks down, the site will still be useful for the user. And basically, this is basically what it looks like. On the left side, you have the full load. On the right side, you only have the index HTML. And yes, it's a great experience. I mean, all my assets are missing, but the content is there. It's still useful to the user. They can start reading. And if the loading is still going on in the background, this visual will enhance into the full experience by the time the assets come in. And this is definitely better than staring at the blank screen for 20 seconds. So that's why I wanted to take this approach. And here is a screen recording from a Moto G4. It's the wrong device frame, I know, but I didn't have any other. So this is actually a proper screen recording from the phone in my pocket. And you can see once I tap on my bookmark, this is clear cache, no service record, over a 3G Wi-Fi that we have in the office. It simulates a 3G with packet loss and delay. And you can see I'm on screen really quickly. But the thing is, loading continues to happen in the background for more than 20 seconds. It doesn't matter, though, because the user doesn't notice. They're happy reading at this point. And this is the mindset that I want people to adopt more when developing websites. Get the user busy. You can be pretty big, actually, most of all. It can be a bigger website as long as you get the user busy and are on screen fairly quickly. Here's a video of a reload when the service record is in place. It's practically instantaneous. There's things I could do better here, but with service record, stuff like this gets really, really fast. So how did I do this? Basically, I started with a WordPress theme that is completely empty, and then just incrementally added things that I needed for the first paint and only add things that I needed for the first paint. And the nice thing about WordPress, or basically any kind of CMS that is PHP driven, for example, is you are basically doing service rendering by default. Because when you have these PHP tags that inject content into your markup, that's service rendering. And service rendering is crucial for the first contentful paint because you need to have the content in there already. And so it's really easy to get a good, or it's fairly easy to get a good first contentful paint with something like WordPress. For the styles, I separate my styles into two categories. I had the critical styles and what I called lazy styles. The critical styles get inlined into the documents, and the lazy styles get lazy loaded. And that's something that Jetpack does already. I think Dan mentioned that in his segment. So my lazy loader basically looks for elements that have the lazy load class and loads the elements that are inside those. The interesting bit about this is that for the styles, I use a no-script tag, and that means that if JavaScript is disabled or not running, that the linked tags inside it will still get loaded. It won't load as performantly as with my lazy loader, but it's basically a progressive enhancement for lazy loading. If you have JavaScript, you get the better loading performance. If you don't, you will still get the full visual experience just loaded a little bit slower, and I think that's a really nice trick. So just to separate what is a critical style and what is a lazy style, critical styles are just like the very basic coloring and visual space allocation. So you can see there's no embellishments, there's no real little things flying around or anything. Only the space is allocated. So you can see the header is not there, but the space for it has been taken up by the element, but the styles for the text and the background image are all in the lazy styles because they're not as important. Second feature I want to talk about is ES6 modules. I wanted to use ES6 modules through and through. Why? Because I think that's what the future is going to be like. We're just going to use ES6 modules everywhere, and because the import syntax just allows you to decouple your code in a much nicer way, and modules are deferred by default, which is also something that was really important to me to have a better loading experience. As you might know, not all browsers have ES6 module support, and even back then, even less had support for it. So I went for a pretty bold strategy. I just said, in development, I'm just going to use the browser that has support for it and use no bundling, no transpiling, and just native ES6 module loading. And then once I go to production, I'm just going to rely on Babel to transpile to system.js and use system.js and those browsers, and then I'm going to basically use the no-module tag to let the browser decide which version to use. So it is pretty ambitious. It's actually kind of ignorant, but it worked out. So I can say you all should be doing this because it worked for me, so it has to work for you. At the same time, my colleague Sergio wrote an article about where he did a really in-depth study how performant unbundled loading currently is, and basically his conclusion was we can't stop bundling because it will impact your loading performance. And I really urge you to look into the Bitly link and read the article because it's really good. But also, it kind of only is true when your module tree is decently big. And for me, that wasn't the case. I had like a dependency tree that was four-level steep, maybe, and a total of 15 modules. So I said to myself, I might be able to get away without the bundling, and so I continued down my path. So let's talk about how I do load my modules with my two-branch approach. Basically, I have a PHP array with all the modules that I need, and then I have a PHP for each loop to generate the module script tags, and then I have another for loop where I just turn it into system.js calls. The one has no module, the other one has type module, so a browser can decide which version to use depending on if they have module support or not. But also, as I said, I had a dependency tree depth of four, which, if you have high round trip times, can still end up being quite costly. So I had an idea how I can reduce that without having to resort to bundling. And there was a tweet that I sadly didn't find, but basically it says, whatever smart thing you think you did, game developers have been doing it for 10 years. And I think there is a lot of truth to that because game developers, in my opinion, are the pioneers of architecture. They usually have very, very big teams that have to work in parallel towards the same goal, and that requires the team to have a very clear image of the architecture. And I think there's lots of things that we as the web developers can learn because we were just starting to do big architecture on the web. And so the thing that I did, and it's not gonna blow anyone's mind in here, is a message bus. Woo! The concept basically is just that it allowed me to send messages. That's a message bus. But it turned out that this simple concept actually turned into a performance primitive and actually made my developer experience pretty great as well. Here's an example. So I have a pending comments module because the blog has support for writing comments while you're offline and using Background Sync to dispatch them once you're online. So with that, the pending comments has a dependency on my Background Sync Manager module that I wrote. It also has a dependency on to my router module because it needs to know when I switch from article A to article B to update the view of how many comments are still pending. And the router in turn has a dependency on the analytics because it needs to tell the analytics that we switched a page and we wanna update our view counter by one. So as you can see here, this by itself is a depth of three. With a message bus, which is a global thing, is just basically inline because it's a very small piece of code, we basically remove all these layers and just have independent modules. You basically end up writing microservices in the front end. And that turned out to be a really nice experience for me as a developer. Additionally, I actually extended the message bus to span all my tabs and the service worker. So it means if the service worker used the message bus and sent a message, it would go to all the tabs. And I'm gonna talk about later why this is really useful. But here's an example. My router has a navigate function and when that gets called, it does the fade out and switch and fade in and all these little things and push state. But in the end, it does a dispatch on my PupsUp the message bus that I wrote. In turn, the analytics module does all the typical analytics loading and in the end, it subscribes the navigation event and whenever it gets one, it just sets the page to that URL and increases and sends a page view event. So these two modules now are completely oblivious of each other's existence and yet it works and I think that's really cool. One thing that is worth noting is that no browser has support for ES6 modules and workers. So that means for every piece of code I wanted to share between my service worker and my main thread, I had to resort back to public, to globals, which is not nice but in the end, it will be fixed at some point and for now it works, I guess. The next big feature, bandwidth conservation. As I said, I want you to put everything in your index HTML for your first contentful paint. But if you then end up with an eight megabyte index HTML, you're not gonna be fast. So this is where I have adopted different mindsets of what I consider necessary and what I consider big because in emerging markets, or especially in emerging markets, people still often pay by the megabyte and it actually costs them money to look at your blog. So you have to be aware of that. So the first load of my blog is 230 kilobytes, which I personally actually think is still a little bit too big and it's mostly due to the fact that I have four different weights of my web font. So if I had variable fonts, I would be great but for now I actually have 140 kilobytes of fonts. And the rest is pretty small. And here is one of the major misconceptions and I hinted at it earlier. Your PWA doesn't need to be small. You can load more data in the background, your initial load needs to be small. And as I said, I have everything in my index HTML so I can get on screen very quickly and my index HTML is 10 kilobytes. So even over 2G, that would be pretty fast. My second load, once service worker is in place, is one kilobyte. And that's because I have caching headers in service worker and that's where you wanna be at. But this is not the thing, I have service worker and this is probably the biggest feature I wanna talk about and that's offline. It makes resistance against flaky networks. I in this case hand rolled my service worker because I didn't wanna use yet another thing so I didn't look at the work box. Also when I was doing this, work box was also so pretty new. In retrospect, it would have been a good fit, honestly. But in this case, I wrote it myself. But the thing is, I wrote a theme, as I said. So if other people start doing this, we're gonna end up in a space where we're gonna have the one service worker per theme problem, which might end up conflicting. So at some point, CMSs will have to start thinking about providing a core service worker from the CMS itself and allowing themes to tap into that rather than every theme writing their own. But as I said, for now, I wrote my own. It's part of the theme. And the first problem here is that the service worker file is part of the theme and therefore is in the folder of the theme and by default only gets control of requests to the theme folder. But I obviously want more than that. So my generic solution was to use the service worker allowed header that allowed the service worker file to take control of a broader scope than it was originally allowed. And then I just inlined my service worker file with PHP. With WordPress, you could actually use the mounting mechanism or redirect mechanism, but this would be a generic solution that works everywhere. My strategy was to just cache everything. But it turns out I really did get requests for everything because I'm now at the root and that means I would have to accept some stuff. And that would be things like the admin panel, which I really didn't want to cache, the theme preview inside the admin panel that I really didn't want to cache. And in general, some external resources that were kind of tripping me up. And that ends up with an on fetch handler that actually gets a little bit messy with early returns. So again, this would be an opportunity for Siem as to provide a function for you that figures out for you if you should be caching this or not. My strategy that I think Jeffy mentioned earlier is stale while revalidate. So that means I just send a request to the network and the cache. And if there's something in the cache, I use it straight away, no matter if it's stale or not, just to be quick. And I can update the cache in the background. One thing that is super important if you use the strategy is make sure your e-tags work because otherwise revalidate will become fairly expensive. So every request will also send a request to the network to check if it's a newer version at the server. And if it's not, it should be super cheap, but it will only be cheap if your e-tags work. So make sure that it actually, you see this in Chrome, lots of three or fours, lots of very cheap requests. But as I said, sometimes there is something newer. Sometimes your cache will be updated. And the question is what do you do then? And this is where the second opportunity for my message bus came into play. So this is my stale while revalidate implementation where I, as I said, I go to network and the cache. And then I wait for both the network response and the cache response and figure out if they are changed and I use the e-tags to figure that out. And if it's actually the case that there is a new resource, I once again send a message to my message bus. But this is running in the service worker and that means that this message will be broadcasted to all the open tabs. And that means that every tab independently now has one of these microservices again running that subscribes to this event and can check if the updated resource is either a core item like the header, the footer, the header image, whatever, or is the item currently on display. And if that's the case, it will fade in a green banner as you can see in the screenshot and asking the user to reload. And I think this is a, it's not the ideal experience in terms of user because they still have to reload, but it's something that's very easy and very safe to do. The last feature I want to talk about is background sync. The thing I did so you can comment while you're offline and because navigating online is a lie. Don't rely on it. If it's false, you know that you're offline. If it's true, you don't really know if you're online. So you should always have a source worker in place to capture these kind of requests. The recipe I used is pretty simple. I basically catch my post requests for the comment in the source worker and I synthesize a successful response. So I try it up what WordPress does if you actually send a comment to an online version and I just redirect you back to the blog post, which kind of makes sense. And so the source worker just synthesizes that response and I store the actual request in a queue in IDB, trigger a background sync, whenever the background sync comes back to me and says, I think I'm online. We can try to actually send these things. I just go to the queue in IDB and send off the requests again. Here's, so if you remember this, my early returns, there is one early return for comments. And the post comments, as I said, is just it doesn't do anything if you don't have background sync support and it just goes straight to the networks and fails or it doesn't. And otherwise I just generate a 302, a redirect back to the blog post and put everything in my queue in the source worker. Now here's the thing that frightens me a bit. First of all, it's open source. That's all it's scary. Like I would love for you to look at this and if you're a theme author, maybe rip out them features, put it into your theme. But I'm not gonna give you the hosted URL, which is just one of my tiny servers hosting this thing. So I'm assuming it will go down, but go nuts, give it a try. Let me know what you think. I would love to hear it. And now for the last couple of minutes, I would like to invite Dan back on stage so we can talk about the future of WordPress and PWAs. Okay, so you've heard like a lot of exciting information here today and I'm sure because it's late afternoon, your brains were super receptive and it wasn't just all flowing out your ears. But I do wanna just say that what we've discovered through this process was actually even more exciting than what I thought we were gonna find. Like I thought we would find one or two things that are like, that's a cool hack, but really like the whole surface area of this whole PWA space is amazingly exciting for content-rich websites. And so what I would love for everyone to do is if you are interested in broad adoption of this technology, think about WordPress as a platform for getting that adoption and check out the Jetpack branch that we linked earlier in the slides. And if you are a WordPress developer interested in this technology, do exactly that same thing. Go check out that branch and see if you can learn a bit about progressive web apps and what they could mean for the users around the world. And I just wanna thank Google for the amazing opportunity to get up here and explore this technology. And in 2018, we're gonna be rolling these features as they're ready into Jetpack and across the web. Thanks very much. Thank you.