Getting The Progressive Web Apps Edge
Checking session availability…
Hang tight while we load the latest updates.
- Improving performance and removing baggage
- Building with 2G networks in mind
- App like UX built on the we
- Challenges of being ahead of the curve
- Embedding in your current architecture - Angular, Ember, React or plain JS.
- What networks do and do not do
- What about Native Apps
Getting The Progressive Web Apps Edge
Amar Nagaram at UXDX EMEA. Video: https://youtu.be/5oo15T-lYYE
Readable transcript: edited from the recording's captions for readability (fillers and false starts removed, punctuation and section headings added). Wording is the speaker's own. Timestamps are positions in the video. Names marked [?] could not be verified against the audio.
Shutting down the mobile site
[00:00:00] We like to call ourselves the Amazon of India. In fact, we would like to call Amazon the Flipkart of the United States. Anyway, I was going through these slides last night, and I was told I only have 20 minutes, so I tried to squeeze in as much as I could about the journey we've had in the last two years. I have to skip over some of the slides, so please feel free to catch me off the stage if I didn't cover any of the details that you would like to know.
[00:00:24] How many of you still do this on your mobile sites? How many of you throw these badges on your mobile sites: "Please install our app, please install our app"? You guys do that, right? Yeah. We used to do this in 2015. In fact, we went to the other extreme of shutting down our mobile site and just putting up this one page saying, "Please go install the app." We thought that just because users can come onto the app, which obviously performs better than our m-site, we would throw our numbers in the other direction. Guess what: it didn't work for us. And the blessing in disguise was that it actually led us on a journey that we are all proud of today.
What an app means to the user
[00:01:06] Before we get there: what does an app actually mean to the user? Not to us, to the user. Users have this perception that apps work offline. Not every app is actually built to work offline, but they do have this perception that even offline, "I open the app, I could see something," which does not work in the web properties. The ability to have its own dedicated space or focus on the user's screen: you don't go to one utility app to launch this thing, you have your own space for the app, which also means the user gets it top of mind when they see the app.
[00:01:47] And most importantly, push notifications. We just had the Big Billion Days. It's like the Black Friday of India. We started this construct four years ago, and it's the biggest thing India goes through in a year. Some of the numbers we've just been told about Flipkart, those were the numbers we used to do in a year; we just did those numbers in five days. That's how big this whole construct has become for us. You know how much we actually spent marketing that event this time, to reach a country of a billion people? Okay, forget about a billion people: around 300 million people, or 300 million devices, online, 100 million active shoppers in the country. We spent less than 2 million dollars.
[00:02:31] You know why? Because of the push notifications. We were able to engage users very contextually, right in the moment, about the offers we were enabling for them. And it was very personalized, to the extent that if you were a customer who was looking to buy a new phone, we targeted you with the right kind of push notification, and the conversions were like never before. We would not have met these numbers if it wasn't for these contextual and more relevant push notifications. Again, an advantage that you get with native apps.
[00:03:06] But let's see this in the context of a web property. Offline behavior: no. Whereas the web property actually gives you no wait, no baggage of installing an app. You just go to a browser, type in a URL, and you're good to go. While you don't get the home screen thing, you have incredible URL reach: you can give a URL to every single page in your website, and you can take users to wherever you want in your property. Push notifications: no. But the tabbed experience that users were always used to, especially in the e-commerce space, where people are used to comparing prices, SLAs, all of that, is gone in an app. You're in a walled garden, in your own container. So we thought about it: how about bringing the best of both worlds together?
How progressive web apps happened
[00:04:00] This is actually a story I was just sharing with some of the folks from Google, about how progressive web apps happened. In 2015, when we shut down the mobile site, do you know who were the first people to call us? Any guesses? Google called us: "Why the hell did you guys shut down your mobile site?" Because, fortunately or unfortunately, we are so big in the country that whatever we do makes news. So it became such big news that Flipkart shut down their web properties. And if you just put the dots together, web, SEO, ad money, Google: Google was worried.
[00:04:37] So Google called us up and asked why, and we said, "Guys, it's not working as well as our own Android native app, so it doesn't make any sense for us to invest in this m-site, knowing that the customers are not buying on our m-site. So we don't want to do it." Then Google asked us, "What do you mean? What do you want from your mobile site? Are there any web technologies that need to be changed?" We said, "No, it's just the browser. At the end of the day, no matter what we are writing as code, it has to run in the container called a browser. If the browser cannot do things which would only be possible in a native app, there's no point in doing anything." Then they asked, "What should this browser do?" We said the same thing: "If it can do all of this, then yes, we will launch our mobile site on it." That's when this happened: progressive web apps.
[00:05:29] So this talk is about a very high-level journey of what really changed between traditional web development and progressive web apps. This is pretty much true of almost every website today, the standard model. There's a browser, there's a web server, there's a request going to the web server, which goes all the way to our back-end APIs, and then the response comes back, and then you have a CDN, obviously, for the static content, unless you have edge side includes. A pretty standard one, right?
[00:06:10] It actually worked for no-JS browsers as well. In fact, one of the data points from India: you know the most used mobile browser in India? Not Chrome. UC Browser. You know why it's most used in India? They offer a feature called data compression, which means a no-JS browser. Users have this perception that less data will be used if they use UC Browser. So we actually had to do a no-JS version of our property just to work on that browser. Again, it was not working for us. UC Browser was not meant for transactions; it was mainly for viewing, more than anything. And this is what happened with the server-side rendering to your page load time. How many of you agree with that? You guys don't agree with that. So we had to reject it.
Service workers and the single page app
[00:07:11] So what did we change? It's pretty much the same thing. The first page load is still the same; we didn't change anything in the first page load. I will talk about how we addressed the first page, or first paint, time. But what we introduced was something called a service worker, and this is what the service worker did after the first page load. The service worker reaches out to all the content. It acts as a proxy to go and fetch the content in a more intelligent way. Your application context and your user context come into play, and based on that we were able to fetch the data in a way that made more sense to the user and was perceived as performant.
[00:08:03] So what does the service worker actually mean? It's as simple as a script run by the browser in the background. It's like the shared worker. I think this is one fundamental difference between the web and the native space. In the web space, you have just one main thread doing everything, right from fetching data to laying out screens to literally mixing all of this, by one main thread, whereas in native apps you have worker threads. The service worker brought that functionality to the web, and you now have the ability to run JavaScript before the page even loads.
[00:08:38] Okay, now I have to really move fast; I'm being told from behind. What did we do on the client side? We went to a single page app. There are challenges with a single page app, like SEO and all of this, but we solved it with the right selection of technologies. What we did in the end was a very lightweight implementation of the Flux pattern. We open sourced it; if you guys are interested, you can look at it. It's called front-end JS [?]. It's a single page app where we're using React [?], and of course React Router, just to keep the URL mapping to the content in the site. So we were able to do push notifications, I'll show what offline-first means, and we did background sync and more.
The loading state and HTML page shells
[00:09:39] Here is a challenge: the first paint still suffered, because, if you remember from the diagram, we were still getting the first page load in the conventional way. So what did we do? We looked at our application and divided the application state into two states: the loading state and the loaded state. What does loading state mean? It's the state your property is in before anything reaches your browser. What does this mean? This is what happens when you type in flipkart.com. You're typing in flipkart.com. Did you see a blank flash? It's almost not there.
[00:10:24] That entity that loaded the moment you hit the URL is called a shell. We introduced something called an HTML page shell. It's actually very simple to do. I'm sure you guys must be using webpack for bundling. We send all our core components through webpack, we create bundles, and we run a render cycle on them to create page shells. The page shells are pretty much the skeleton of your page, giving a structure to the kind of data your page will load. So this is what it does: on the first page load we are not waiting for the entire page. Now we are only waiting for the shell, which is very lightweight compared to the entire page contents, or the first fold of the content. It's much less than even the first fold contents. And the other HTML shells will again be prefetched by the service worker.
[00:11:33] The most important advantage we have with page shells is that they can be cached, and they can be rendered in the loading state. Once the data is fetched, if you have to re-render, you can re-render them. And if you're using something like React, you can render partial areas of your shell. You don't have to re-render the entire thing, which is one good thing about something like React. On top of it, sw-toolbox: we use that to fetch the data. sw-toolbox is a network proxy library built on top of service workers. We were the first ones to use it, in our Flipkart Lite, when we launched the progressive web app in 2015.
[00:12:13] Now here is a challenge. You've solved this problem, but now you have too many shells. Every page technically has a shell, because every page could have a completely different structure altogether, unless you have only one shell for everything. Then what happens? You have too many page shells to handle, which again defeats the whole purpose. Sorry, I went too fast. So if you had to make a choice between one big monolithic single page app versus multiple smaller single page apps: we went down the path of multiple single page apps, which gave us the flexibility of building and deploying each of them separately. It wasn't that trivial. We had to share the common libraries and utilities. In fact, our front-end JS [?], the Flux implementation we did, takes this into account very intelligently.
Results, splash screen and offline
[00:13:10] Sorry, I'm running out of time; let me get through the best parts of it. One more thing is that sw-toolbox supports the route-based fetching model. You can see the difference: on 3G, these are the response times you're looking at, because of the bundling. We were able to split JS into multiple chunks, we were able to split it into app shells and all of it, so your page loading is no longer dependent on one big chunk or one big file that has to be downloaded. On 2G it pretty much remained the same, because the content is so small now for the first meaningful paint to happen. So the network technically went out of the picture here.
[00:13:56] Again, add to home screen was one thing we were able to do with the manifest file. The splash screen is one more important thing. You can see this is a programmatically rendered splash screen. The splash screen also turned out to be one of the very important features for us. In fact, this version of the splash screen is from when we launched progressive web apps. If you look at our splash screen today, we're using it to drive new features, new product launches, all of it, while we are loading the page shells and the content behind it. So we were able to capture the attention of the user while we're doing something in the back end to load the home page.
[00:14:33] Offline experience: the network proxy that I was talking about, where we were able to prefetch the data, is actually sitting on your device. So we thought we could use that to give an offline experience, because we had already fetched all the content that you browsed and looked at. So this is what we did with the offline thing. It's offline mode, and it's visually clear that you're in offline mode, and this is completely working in offline mode, because it's the data we fetched anyway when you were browsing through these things. In fact, the network proxy gives you the flexibility of fetching data that the user didn't even browse. If your business logic expects users to do certain parts of your app offline, you can go fetch that data when the user is online and give them a completely relevant offline experience.
[00:15:27] And then, thanks to Chrome and all the other browser vendors, browsers now also give us access to a lot of hardware APIs. Geolocation is very important for us as a marketplace. We cannot sell everything in every part of the country, so it's important for us to know where a user is coming from, and very important for us to tell the customer, "In your location we cannot sell this TV." It was a pain point for us to ask the user to please type in their PIN code and all of it on an m-site. Now it's all geolocation, and it's precise, comparable to a native app. The accelerometer: in fact, we could not figure out a use case for this in e-commerce, so we introduced an Easter egg where if you move the app in certain directions, it unlocks an offer for you. So it's something users keep playing with.
Performance and what the user feels
[00:16:13] This was the part I really wanted to cover; I don't know how much time I have. Performance. This is the only feature the engineering team in Flipkart brings to the table. While our product team is busy bringing all the features that users would like to see from Flipkart, this is the only feature the engineering team is working on. Performance is something we do not compromise on, because the whole premise of building progressive web apps, and for that matter our Android app, which happens to be the fastest Android app in the country, and the fastest e-commerce app in the space across the globe, is that we take performance very seriously.
[00:16:49] But here is the question: what is performance? What does this thing actually mean? People have their own perception of what slow means. You must have heard tons of advice: DOM manipulation or DOM changing is slow, CSS animations, all kinds of stuff that you keep hearing about what is best and what is not good for the performance of the app. There are tons of things around. People throw out first paint, DOM content loaded, frames. People use different metrics to measure the performance of their properties, be it an e-commerce property, be it a gaming property or whatever. But nobody ever asked the question: what does the user feel?
[00:17:44] So we started looking at our own user studies, only to discover that somebody had done this research way back in 1993. Dr. Jakob Nielsen wrote this amazing book called Usability Engineering in 1993, where he called out three important time limits for a user property. The first one: your reaction time should be less than one tenth of a second. When the user does something on your glass screen, the pixels that need to change under the glass need to change in less than one tenth of a second. Your flow change, your progression based on the user action, needs to happen in less than a second. And most importantly, your app or your site should run at 60 frames per second, so each frame should be rendered in 16 milliseconds, if you just do the math.
[00:18:41] So it was said in 1993. Why is it that most properties are still not as performant as they should be? Because nobody ever mapped what these three timings correspond to in real-time scenarios. That is where this model came into existence. This model explains how those three numbers map to your real-time scenarios. In fact, they did a very clear grouping of user activities on your property into four things: response, animation, idle and load.
[00:19:20] Response: input feedback in less than a hundred milliseconds. Your feedback should be fast enough. If it is more than one tenth of a second, the action-reaction thing breaks for the user; the user actually thinks it is not working properly. The second thing, and I'm almost done: animation. You don't do animation just because it's cool. You do animation because you want to give a seamless experience to the user, seamless transitions. But if you don't do it right... can you see the difference? The first one is moving at 60 frames per second, the second at 30 frames per second, the last one at 15 frames per second. It could be a problem for the customer. In fact, you're making it worse.
[00:20:05] And not everything in your application needs to happen in real time. A very good example: we collect data when the user is on our property. We do not send the data back to our service in real time; we batch it. Because if you have to send it in real time, you're using I/O, you're using threads, which could be used for the operations the user is expecting to see from the property. We do it offline, we do it asynchronously. So you have to make a call between what the user-critical operations are and what operations could be done asynchronously.
[00:20:39] And last but not least is the first meaningful paint. It's not the first paint, it's the first meaningful paint. If you look at the way we render our property, the first thing you see is a search bar. 67% of our traffic interacts with Flipkart using search, so the first thing we load is a search bar. So we measure our performance by the time it takes to load the search bar, not just when the first file was downloaded to the browser.
[00:21:09] And yeah, this is how it looked. This is the very first version of Flipkart Lite. In fact, if you see Flipkart Lite today, it's much faster and much more performant. But I wanted you guys to go back in time to when we launched it in 2015. This is how it was when we launched it. Look at all the animations, look at the fluidity of the animations. And last but not least: I was just talking about the Big Billion Days. In 2015, when we started Flipkart Lite, it was an experiment with our friends from Google. Today it's the second most visited channel for us, bringing in 30 percent of our revenue. So the performance of your mobile web properties can change the game for you, and we are telling you that from our experience. Thank you, guys.

