 So hi, I'm Jeff Posnick from the Chrome Developer Relations team. We've heard a lot about the benefits of progressive web apps, and we've also heard a lot about service workers. And we'll be hearing more about those in sessions to come. But I want to take the time to talk about one approach to structuring your progressive web app using what we're calling the App Shell. The App Shell architecture works really well with service worker caching as progressive enhancement. So during the session, we'll talk in detail about the SW pre-cache and SW toolbox libraries, which make it easy for developers to add service workers to their web apps. Web apps that use the App Shell plus service worker model effectively can feel like they load instantly. And before we go any further, let me demonstrate what I mean with a real world example. So I built a web app to browse iFixit repair guides using iFixit's excellent public API. The iFixit API returns a mix of text and images, like you'd find in many content browsing applications. And in the screencast, we're using the iFixit API to load in a list of featured guides, and then browsing through a few of them. So you can imagine each of these pages being something a user discovers on the web as a search result or finds a link to from some other site. It's a web, so there's nothing special to install in advance. So loading obviously looks fast, but let's put some hard numbers behind a few different loading scenarios. So all of these examples were run on a simulated 2G mobile network against the App Engine instance we've deployed on. And we're showing off top level navigations here. So what you see on the screen is the experience loading the web app for the first time, with nothing in the cache and no service worker in place yet. We're able to get all the above-the-fold content on the screen in a very respectable 650 milliseconds. So let's compare that first visit to what your users will experience the next time they visit the same page. With the service worker in place and your cache populated, we're able to get the above-the-fold content on the screen in around 200 milliseconds. As a point of comparison, the same page loaded with a populated cache, but without a service worker, maybe because your browser doesn't support it, takes around 560 milliseconds. And let's take a look at the learning experience for the page that we haven't previously visited. So because a web app service worker is already installed and the app shell is cached, new pages load instantly as well. So we could get above-the-fold content on the screen in 225 milliseconds. With the same cache content but without a service worker, we're looking at about 650 milliseconds to get the content on the screen. You can see that even on a slower mobile network, great load times are possible across the board. So regardless of how you structure your web app, that initial request for the HTML landing page could take time. On the left is our network traffic requesting our iFixit demo app for the first time with no service worker and nothing in our cache. It takes 423 milliseconds before we get our full response back. And this includes the time it takes the server to make the iFixit API request for us. So on the right, we're serving the same page directly from our cache and only takes five milliseconds once the service worker gets involved. So with the service worker and with the right caching strategy, the network no longer stands in the way of getting your app on your user's screens. So we strongly feel that adopting the model we'll be talking about today is the surest way to build web apps that consistently load in under a second, which is a target we recommend in the Rail Performance Model. You'll hear more about Rail during tomorrow's sessions, but keep that in mind. And as my colleague, Tal, pointed out, loading instantly can make a big difference in your web app success. Studies have shown that an additional one second delay in page load times can lead to 11% overall fewer page views as well as a 16% decrease in customer satisfaction. So instant loading isn't just nice to have. There's a direct impact on your bottom line. So hopefully everyone out there is ready to go and adopt this model in your own web apps. But before you can do that, let's run through an explanation of the key concepts you'll need to understand. So first, what's an app shell? So your shell is the HTML, JavaScript, CSS, and any other static resources that provide the structure for your page, minus the actual content specific to the page. So the shell should be cached, obviously, and should load quickly. Link to your shell is kind of similar to the native code that would get uploaded to an app store if you're writing a native application. But you don't have to sacrifice the linkability and discoverability of the web. And unlike the native apps, you get to play updates to your app shell in a second. The concept of an app shell is going to come up again and again throughout this presentation. Anytime you see something called green, know that we're talking about the app shell. So once the shell content loads, we fetch dynamic content, in our case, from the iFixit API to populate the view. The app shell plus our dynamic content equals the complete rendered page. Anytime you see something called blue, know that we're talking about the dynamic content. So now that we've talked about the app shell, we can turn to the next question. What's a service worker? Technically speaking, a service worker, it's a network proxy written in JavaScript, it intercepts HTTP requests made from your web pages. But an analogy that I like to use is to think of a service worker as being an air traffic controller. So let's see how this analogy plays out. You can think of your web app's HTTP requests as planes taking off. With the service worker, you now have an air traffic controller, making sure that your requests load quickly and efficiently. So the request plans might land using the network runway, or they might land using the cache runway, in which case the request can bypass the network completely. So it's up to the service worker to decide how to handle each request. And here's a quick visual walkthrough of the life of a service worker. We start out with a page that isn't yet controlled by a service worker, but contains code to kick off the registration. So that creates a new service worker instance, and fires the install event that the service worker will respond to. And now is our chance to add our app shell's content to the cache. Let's dig in deeper. So while every setup may be different, in our iFixit sample, we're serving the HTML for our app shell from the slash shell URL. Keep that in mind throughout this presentation, when you'll see slash shell pop up again. So to populate our cache, the service worker makes an HTTP GET request for slash shell during the install event. And let's walk through the contents of our shell. So we're including some site-wide inline styles. They'll be shared by all of our pages. And next, we have a template insertion point within our body element. So this doesn't contain any real content, but it serves as a placeholder for the client-side templating that will take place when our content's loaded. And finally, in this example, we have a reference to an external file, app.js, that contains all the site-wide JavaScript that our page needs. So back to the life of the service worker. The next event you'll get a chance to handle is Activate. So here, you normally perform cache cleanup of any out of date resources you no longer need in your shell. And once those handlers complete, your service worker enters into an idle state. So it's running, but it's not doing anything until there's a network request that fires off a new event. And in response to network request, a fetched event handler will get a chance to intercept the request and respond as you see fit. So that happens after a period of idleness. Your service worker script is stopped automatically. So when the next network request is made, when your page is loaded again, it started back up and can immediately respond to fetch events. So the air traffic controller analogy breaks down a bit here because you probably wouldn't want to tear down your air traffic control tower every time a plane lands and wait for another one, but bear with me here. OK, so let's take a look at how our fetch handler serves up our app shell. Our service worker can intercept any requests for any URL under its scope, including pages that haven't been previously visited. So in our case, we want to respond to any navigation requests with the slash shell response that we've previously cached. And this will bypass the network completely. So this is a model that you might already be familiar with. It's what happens with server-side rendering when there's a URL pattern that's routed to a matching handler, which is then responsible for returning the full page to the browser. And this is a service worker equivalent of that routing. And it's code that runs client-side in the app shell that ends up populating the content in this scenario. And if you're using a universal JavaScript framework, it might end up being the same templating code that runs in both the server and the client, but that's not a requirement for using this model. So at this point, I hope you see the advantages of structuring your web app using the app shell plus service worker model. And while you could write your own service worker code, we have two libraries that will take care of many of the details for you, while following the best practices and avoiding common gotchas. So going back to our analogy for a second, you may be an awesome web developer, but you might not feel comfortable writing the code responsible for landing an airplane. And that's fine. These libraries will handle that code for you. So the first library that we're going to talk about is SW Precache. So it takes care of caching and maintaining all the resources in your app shell. The second library we'll talk about is SW Toolbox. It provides a canonical implementation of all the runtime caching strategies that you'd need for your dynamic content. So we built these libraries to go together hand-in-hand, and using them together gives you a service worker that can load your app shell instantly, while giving you fine-grained control over how your dynamic content is cached. So both of these libraries are battle-tested and ready for production. They powered the service worker for this year's Google I.O. web app. So chances are you've already visited a page that uses them. I was a member of the team that built the I.O. web app, and we specifically designed these libraries to handle the real-world needs that came up while building the site. I wrote up a case study about our experiences, which you could read at the URL on the slide. And we're also really excited to announce that Flipkart is using the SW Toolbox library directly as part of their new progressive web app. You'll hear more about that tomorrow. All right. So let's dive into the first library, SW Precache. So most web apps already have some sort of build process in place. And SW Precache drops right into your existing build process and uses wildcards to identify all the resources that make up your app shell. And we're using Gulp in the iFixit demo app, but SW Precache works equally well with Grunt, and we have a command line version as well. All right. So let's take a look at what happens when your build process includes SW Precache. So here we have a set of local files in our build directory that make up our app shell. We perform a Gulp build, which includes a step that runs SW Precache for us. SW Precache is configured with some wildcards to match all of those local files. And what it does is calculate a hash of each of the files that it finds that matches the pattern and generates a service worker code for you that contains a mapping of the URL and the current hash. So this service worker JS file that it generates contains all the code that you need to maintain the cache of those files. You don't have to write a thing. You can just use it as is. And the initial visit to a web app that uses a service worker triggers a pre-caching of all the URLs that were identified. And you can think of this as kind of being equivalent to a native app store install process. So your app shell changes over time. And SW Precache will make sure that your cache changes along with it. So let's assume you make some updates to two files, and you're ready to push those changes out to your web server. When you run Gulp as part of your deployment, it'll regenerate the service worker JS file. Since two of the files now have new contents, the associated hash will change as well. And there's logic in the generated service worker file that will detect those changes for you and automatically update the cache entries just for the files that have changed. When users return to your app, only new or modified resources are downloaded. You can kind of think of this as a native app store's incremental updates. So those who prefer a command line build process to Gulp or Grunt can install the SW Precache module globally from NPM and then run it directly. So remember to rerun the command prior to deploying an update to your site to ensure that the generated service worker picks up on all the local changes. All right, let's get our hands dirty a bit and take a look at how I'm using SW Precache in the iFixit web app demo that I showed earlier. So this snippet is part of the Gulp build strip for the project, which gets run through Babel, so you can use all sorts of ES2015 goodness. And first and most importantly, we have the dynamic URL to dependencies option. And this is how we keep an always up-to-date copy of the application shell cached. So we use the URL slash shell as a key. And the value here is the array of all the files that uniquely define the inline contents of slash shell. Next, we set the navigate fallback option to be slash shell again. Our new service worker file will automatically serve the contents of slash shell whenever there's a navigation to any URL on our site. We'll serve up the application shell, even if the actual navigation request is something like slash guide one, two, three. And client-side URL routing will handle populating the content. Next, static file globs is also really important. This is where we list all the individual app shell resources that might be requested directly, as opposed to those that are included inline within the shell. And finally, we have the import scripts option. And here's where we can extend the generated service worker to include additional logic that we might need. In this case, we're pulling in a configuration file for SW toolbox, which is the next library we'll talk about. All right, so SW toolbox is the answer for caching your web apps dynamic content. So everything that we've colored blue in this presentation, basically. And while SW pre-cache integrates with your build process, SW toolbox is loaded by your service worker at runtime. And there are a number of very common caching strategies. SW toolbox provides a canonical implementation of each. So let's take a look at some of those caching strategies. And one approach is to have the service worker check the cache for a response. If it's there, great, return it to the page. If it's not there in the cache already, then return response from the network instead. So this is a good strategy to use when you're dealing with remote resources that are very unlikely to change, like static images or something like that. And it corresponds to SW toolbox's cache-first handler. The converse of that approach is check the network first for a response. And if that's successful, return it to the page. If the network request fails, you could fall back to a previously cached entry instead. And this is a good strategy to use when you're dealing with data that needs to be as fresh as possible, like, say, a real-time API response. But you still want to display something as a fallback when the network is unavailable. And this corresponds to SW toolbox's network-first handler. So finally, here's an interesting strategy. The service worker fires off both a network request and a request that goes against the cache at the same time. Cache response will normally come back first, and that will get returned directly to the page. In the meantime, the response from the network is used to update the previously cached entry. And that keeps things relatively fresh. This updates them in the background without blocking rendering of the cache content. And you could get this for free just by using the toolbox.fastest strategy. It's pretty powerful. All right. So you do need to think a bit strategically here. You need to take time and think about what strategy is most appropriate for the dynamic resources that populate your app shop. But you don't have to choose just one. SW toolbox's routing syntax lets you apply different strategies to different URL patterns. But you might be saying you could implement those caching strategies yourself. So why use SW toolbox? SW toolbox has a couple of additional options that help solve problems that arise while fetching your content. These options make service worker caching much more useful in real-world scenarios. So first, a phenomenon that I'm sure everyone here is encountered, LIFI. So it's one of your devices network connection, but it's one that's extremely unreliable or slow. While your app shell should always be fetched to cache first, you might request dynamic content used to populate your shell using a network first strategy, in some cases. And LIFI can be deadly in those cases. If there were no network at all, your request would fall back to the cache right away. But when there is a network request that just drags on and on and on before eventually failing, you end up wasting precious seconds just waiting for the inevitable. So using SW toolbox, you can set an explicit network timeout. And here, we're sending it to three seconds when fetching an image network first. And after those three seconds have passed, we don't have a response for network to automatically fall back to the cache content for us. All right, so here's another real world problem you might bump up against. What happens as users go from page to page on your site? So you're probably caching those page specific contents, like images associated with each page to use your visits at runtime, to make sure that if they ever return, the full page will load instantly, not just the app shell. But if you keep adding to your dynamic caches indefinitely, you'll end up using an ever-increasing amount of storage. So SW toolbox will actually take care of cache expiration for you, saving you the trouble of implementing it yourself. Here, we have SW toolbox configured to use a dedicated cache for images with a maximum cache size of six. So once the cache is full, as it is now, new images will cause the least recently used images to be evicted. In addition to the least recently used expiration option, SW toolbox also gives you a time-based expiration option, where you could say, automatically expire everything once it reaches a certain age. So Flipkart was looking for a solution to this exact problem, and we're really happy that they turned to SW toolbox for that. All right, so now let's take a look at the SW toolbox configuration used in our iFixit demo app that we showed earlier. So this file gets included with our service worker at runtime by using the import scripts option in SW pre-cache. All right, so first we define a route for our iFixit API requests. And we're using the toolbox.fastest strategy here, which strikes a nice balance between automatically returning stale content immediately and automatically keeping our caches up to date in the background. So next, we explicitly add an image to our cache that will act as a placeholder missing image if the request for a real image fails. That, in turn, uses part of our image handler function, which takes a request, attempts to get a response using a cache-first strategy, and if that fails, falls back to that missing image placeholder. And finally, you can see how we set up a route that uses our image handler function to handle requests for images served from cloudfront.net. We're also making use of a maximum size for this dedicated cache that we're using for images. So after 50 images have loaded in the cache, the least recently used ones will automatically be expired. All right, so I'm sure that throughout this talk there's been something in the back of your mind. This app shell model is great, but how does it work in browsers that don't support service workers? Well, service workers are, in general, progressive enhancement, and that applies when they're used in the app shell model as well. So going back to our analogy, it's possible to land planes at an airport without an air traffic controller, but you're probably going to want to do that very slowly and very deliberately, and I wouldn't really want to be a passenger on that plane. So putting our analogy aside, how do you go about treating service workers as a progressive enhancement? Well, remember that the first time you visit a web app, it won't yet be a service worker in place. So you need to make sure that your web app renders properly without service workers anyway to handle that scenario. On browsers that support service workers, the next visit to your web app can take full advantage of everything that service workers have to offer. But for browsers that don't support service workers, the next visit to your site ends up looking very much like that first visit. There won't be a service worker. So you just continue to serve things via network requests. So your architecture shouldn't have to change in order to accommodate browsers that lack service worker support. All right, so whether there's a service worker in your browser or not, HTTP caching best practices still apply. So you should continue to follow patterns like adding in hash fingerprints into your file names and using far future HTTP cache expiration headers. This doesn't change any of that. And SW pre-cache will happily work with that set up. But if you are in a browser that supports service workers, you now have a key to unlock additional performance wins. So you can serve even your initial HTML landing page directly from the cache when using a service worker, which is difficult to do safely with just HTTP cache headers. If you remember from the start of the talk, that means the difference between 423 milliseconds to retrieve your landing page and 5 milliseconds. And you can use that same cache first strategy even for pages that are dynamically rendered on the server. Because SW pre-cache will keep those pages fresh for you when one of the underlying partials or inline content changes. All right, so where does this talk leave you as a developer who's interested in using this architecture? Well, we have a number of demos and jumping off points that you can choose from to get started. So first of all, you should realize that if you're building a modern single page app, you're probably already using something similar to an app shell already, whether you call out that or not. The details might vary a bit depending upon which libraries or frameworks you're using. But the concept itself is definitely framework agnostic. So your next step could be just to add in a service worker to your existing web app using SW pre-cache and SW toolbox libraries. But if you're starting from scratch and you want some inspiration or just want to see a finished real world example, here's the source code for the iFixit API client I demonstrated earlier. So it uses universal JavaScript, both server and client-side rendering, and can be adapted to go against a different API or source of dynamic content if you want. Next, for those who prefer a step-by-step hands-on learning experience, we put together a code lab that walks you through adding SW pre-cache to an existing web apps build process. Those of you who are attending in person, you can do it in the code lab section that's outside. I also want to mention a Polymer-based demo that my colleague Rob Doxon put together, which makes use of an actual service worker model in a blog web app, which I think is called Super Cool Blog. Rob will be talking more about that project, including the correct pronunciation a little bit later today in his session. Next, the most recent release of the Web Starter Kit project includes SW pre-cache and SW toolbox. And projects that use Web Starter Kit as a starting point will take advantage of them by default. So this is super powerful in terms of getting this technology out there to folks who are getting started on web development. And finally, my colleagues Adi Asmani and Matt Gaunt spent a great deal of time putting together this vanilla JavaScript starting point. It gives you an actual plus service worker set up with minimal opinions about frameworks or libraries. I highly recommend that folks check this project out. And it gives you a great place to start with all the best practices already baked in. OK, so we strongly believe that the AppShell plus service worker model is the best way to structure your web apps if you want reliable, instant load times without having to worry about the network getting in your way. We hope you're inspired to go out there and adopt this model today. There's no reason to wait. And we know your users will appreciate not having to wait either. Thank you.