F8 2019: Building the New Facebook.com with React, GraphQL and Relay

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
good afternoon everyone my name is Ashley Watkins and today Juan Benoit and I are excited to be here to tell you more about the new facebook.com/ during the keynote presentation mark and VG shows the upcoming visual redesign what they didn't tell you is that this was more than just a redesign under the hood the technology stack has changed as well let's take another peek at what the new site looks like [Music] [Music] [Music] I cannot wait for all of you to try this for yourselves so what were we trying to accomplish with us we found that every time we make our services faster and simpler people communicate more this is really at the heart of the new facebook.com/ we've found oh sorry about that it's to be easy to do the things you want to do and user research showed us this new design is simpler and more intuitive but let's talk about fast what does fast mean speed matters and two places time to start up and UI responsiveness fast startup is measured by how quickly the page is rendered and interactive after someone navigates to facebook.com for the first time UI responsiveness means that as you click around the site we respond quickly to your interactions and transitions are seamless simple and fast combined to create the app like field that we've all come to know and love on our mobile devices and we wanted to bring that to the web but it doesn't mean anything if we can't keep it that way we have a lot of Engineers at Facebook contributing code to maintain consistency and quality these needs to be baked into the entire system sustainability means it needs to be easy to design iterate on and maintain the user experience and the thing is our existing site couldn't need all of these our old tech stack wasn't going to be able to deliver a performance experience with an app like feel changing the styles and layout wasn't going to cut it by starting over we freed ourselves from the constraints of our legacy infra and we were best positioned to accomplish our goals so the new facebook.com/ is a brand new single page web app powered by react graph QL and relay how are we doing this today Wan will talk about how we're using relay to simplify and streamline our data fetching Benoit will talk about how improving the delivery and efficiency of our Java Script code and I'll talk about how our trying to make the new facebook.com/ be a great user experience for everyone please join me in welcoming wante hada from the relay team to the stage hey everyone so as we've just mentioned we're using graphical and relay to do data fetching in our app in case you're not familiar with graph QL graph QL is a declarative query language used for requesting data from a server graph QL is agnostic to how you store your data in the backend and provides a unified typesafe layer on top of all your server data so that it can be easily inquiry by multiple clients over time in fact we use graph kill to power our mobile applications and we're using it to power the new face book on facebook.com - now with that said the main focus of this section of the talk is going to be relay which is a JavaScript client we use in the browser to fetch graph QL data really is a JavaScript framework we developed here at Facebook for managing and fetching data in react applications in this section of the talk I'm going to tell you about how we're using relay to scale data fetching across a large complex application like the new facebook.com and what important opportunities this unlocks for us in order to ensure that our application can both startup fast and have instant responsive UI interactions so you might be wondering why do we need a whole framework just to fetch some data well let's go over an example to answer this question let's consider this post let's say this post is a single react component which has a few different data requirements to render it we can easily describe the data that this post needs with a graph Kuehl query and fetch it from the server this will work but this post might not always be rendered by itself this post could be part of a newsfeed which in turn could be part of the homepage itself each of these sections in the page will have their own distinct data requirements so how do we fetch them well one simple approach could be for each section in this page to independently fetch the data that they need this means though we'd be sending mul network requests during initial render and this can end up hurting user experience and performance that being said we can improve the situation just using graph QL with graph QL we can accumulate the data requirements for each of these sections and describe the data that the whole page needs in a single top-level query this will be better because we can save the work of starting multiple Network requests and get all the data we need in a single round trip but if we do this we introduce a new problem for instance what if we want to reuse this post across different pages as we know this post has some data requirements so what this means is that each of these pages need to include the data that this component needs in their queries but what happens when the data that this post needs changes well now we have to make sure that each of these queries are updated to include the new requirements if we forget to do that we can end up in a situation where we're fetching the wrong data or not enough data for this post but let's say that we managed to update all of our queries well what happens when a page stops rendering the posts again if we forget to remove this query from that paid this data from that page we'll end up with the opposite problem where we're fetching more data than we actually need to use and even if we manage to get this right even with a small number of queries and components this can become very error-prone very quickly as the application grows and more engineers start adding more features components each with their own distinct data requirements this will very quickly become unsustainable for us to maintain errors we'll start creeping up data will be over fetch or under fetched and engineers will need to have an understanding of almost the entire system even to make small changes so how do we make this sustainable how do we make it so the app and its data requirements can grow and change while keeping all this complexity manageable well that's where relay comes in with relay ponents and their data requirements are no longer separate instead the data requirements for the component are declared inside the component and become part of the component itself this means that each component declares the data that it needs right next to where it uses it this is really helpful because it ensures that we can continue reasoning locally about our components and write them without worrying about how their data is being going to be fetched or how that affects other parts of the system and focus only on describing exactly the data that they need instead relay will fetch this data for us and make sure that each component has a data that it needs whenever it renders as we build our components using relay really will build up global knowledge of the data requirements for the entire system and statically know exactly what data is needed for any given page even as data requirements change over time all right so we're using relay Everywhere to build our app and to scale our data fetching so this unlocks a few optimization opportunities that we can capture first let's talk about parallelizing work the way we would traditionally fetch data in a client rendered app would probably look something like this first we download the code for app then we start rendering and as we do we send a network request to fetch the data that we need this means that we'll need to render a loading screen and wait for a while eventually when the data comes back from the server we can render the final content to our users in this situation if we could start fetching this data earlier we might be able to show content sooner and even entirely skip a loading screen the problem is how could we possibly know what data a page is going to need before we even start rendering it well as I mentioned earlier really has all of this information and not only that this information is available ahead of time what this means is that we don't need to run the application in the browser to know what data a page is eventually going to need to request what this allows us to do is as soon as we get that initial request for a page our server can start delivering the data down to the client in parallel with the code by doing this we can save some time and hopefully make our applications start up faster so we can paralyze a work of downloading the code and fetching the data but sometimes they're even more improvements we can do by adjusting how and when we deliver this data to the client for instance let's say we're already fetching our data as soon as we can however even if we do this we might still run into cases where the data that we need just takes too long to fetch when this happens we're back in a case where we need to show a loading screen and wait for a while before we can render our content if you take a look at our queries maybe the reason it takes so long is because we're fetching too much data or data that's too expensive to compute and the question is do we need all of this data just to show that initial paint on the screen maybe some of that data is initially hidden or render it outside the viewport for instance we usually don't need to show more than one newsfeed post for the initial render of the page so we shouldn't need to wait to fetch all of the posts before we can render anything so what this means is that we can trick treat some of our data as in most critical data and this doesn't need that we won't need the rest of the data only that we can afford to wait just a little longer for it if that means we can get the critical data delivered sooner with graph QL and relay we can actually mark which parts of our query are the most critical and which parts are less critical by doing so the server can deliver the critical data as soon as possible in a separate payload without having to wait for the whole query to be completed this means that hopefully we'll be able to show that initial content sooner and then the rest of the data will be progressively delivered in separate payloads each of which may cause less critical content to be rendered note here that all of this data is still delivered in the single request so we're not incurring in extra costs for new network requests so we can split up our query to deliver content sooner but there are even more improvements that we can do as it turns out sometimes the way we structure our apps can make it so we bet download both code and data that we never end up using this can commonly happen when we need to render two variations of a UI depending on some data or some user conditions for instance is Facebook a single post could maybe be a photo or it could be a video and when we render a post we can know ahead of time what type of post is going to be this money depend on what type of page you're visiting what type of user you are what this implies is that our component needs to know how to render both variations of this posts that is it will need to include both the code and data for the photo variation and the code and data for the video variation but what if a user visits a page and never ends up seeing a video well then we unnecessarily downloaded a bunch of resources that we didn't end up using and wasted some time and you might think this isn't a big deal it's just a little bit of wasted resources but at Facebook this is a huge deal a photo isn't just a video or a post a photo can post can actually be a myriad different things like an event a song an album or even a fundraiser so we downloaded all of these resources upfront we actually end up wasting a ton of resources if we don't want to download all of these resources upfront one thing we could do is split up the work and only download enough until we can tell exactly what extra resources we're going to need and then when we know that we need to fetch those extra resources we can fetch them in separate requests but as I mentioned before starting multiple Network requests during initial render can dramatically hurt user / user experience for example if a user happens to need a video as their first post a news feed they're going to have a bad experience because they're going to need to wait for these extra requests before they can see anything on the screen so how do we download exactly the amount of resources that we need as early as possible well again we can use graph kill and relay to do this with graph QL we can model the different variations of our UI and describe the types of posts a post can be when we write our query we can also describe the data that we want back when a post matches a specific type that is if the post ends up being a photo we want the photo data back but if it's a video we want the video data back and if it's a song we want the song data back when the server executes and returns the data for this Pope for this query as soon as as soon as it knows that we're querying for a video for example it will then will only download the video data in this request this is better because now we can download this data earlier as part of the initial graph QL request without having to start new request for data however at this point we still need to wait until we start rendering before we can rent fetch of that extra code well that's where relay comes in with relay we can also express which component code we're going to need in order to render the data that matches a specific type essentially it's like we're treating code as data itself and here the same conditions continue to apply if the post ends up being a video we know that we only need to download the video component code in practice we can't just deliver this code as part of the graph QL requests though we still need to deliver this code separately but what this does allow us to do is as the server is resolving this query when it knows that it's going to return a video posts it can immediately start sending this video component code down to the client a lot earlier which again means that we'll hopefully be able to show the final content to our users a lot sooner so I've talked about a few strategies we use to make sure that our application can start up fast but to close off I briefly want to tell you about how we're using cache data to ensure that our application can have instant and responsive UI interactions apart from fetching data from the server really also keeps the local in memory cache of the data that it has fetched so far this means that when we need to fetch a query from the server it really has cached any of that data locally we can reuse it to instantly give feedback to our users when they perform an action for example if we were to navigate to the profile page here we can immediately show the data that we've already cached even if it's not the whole data for the page in this example for example the name and the profile picture then as more data is fetched from the server we can progressively show the rest of the content once we visited a page we can return to it instantly since all that data for that page will have already been cached by relay so I've talked about a few ways in which we can leverage graph kill and relay to ensure that we provide a great experience in our application but data is really only half the picture up next I'd like to invite Benoit up to the stage to tell us about how we simplify and deliver JavaScript code in our application thanks Juan so make the new facebook.com/ fast we had to rethink how we deliver code to the user we found on funding old facebook.com/ we found that as we continue to grow we started facing more and more problems if you're like me a few years ago you're probably wondering why is this even difficult it's easy right you take your code you put it in a few packages you add it to your page and then you're done well that works well when you're small but eventually as you grow you're gonna find like us that your page slows down and you encounter more problems why is that this is what a typical boat might look like as you grow you'll notice that a lot of the code that you're sending isn't used for the initial display of the page the unused code might be for interactions that can only be performed after you've started up the page or it might be for an a/v experiment that the user is not a part of or like Juan mention there's a lot of possible Story types but only one or two will be used for the initial display of the page so this means even more on use code by removing and deferring unused code we can improve start-up performance this is called code splitting on the old facebook.com site we solve this by adding api's to make code splitting easier we can code split based on which a be experiment a users on or we can wait until data is about to be used to fetch it or we can defer the loading of modules until after startup users that don't need any of this code will do Co dramatically slower load however users that hit code splitting on the startup path will see you dramatically slower load this is because if we're in the middle of rendering a page and we hit one of these code split points we have to interrupt the rendering of that page and download the missing code before we can continue to render for example if you see a video post there's a good chance of the video player code will be split out of the initial payload which means you have to wait for that video player code to download and the problem is that we might have more than one of these code split points on any given startup every time you hit one of these code split points your your download take or your startup takes even longer because you're waiting on more of these requests to maintain good loading performance on the old facebook.com site we have to constantly monitor all our codes play points and make sure that they're behaving properly in production at our scale with so many engineers we found that this was simply unsustainable but how do we make this sustainable how do we send down exactly the right code without ever making performance worse so daniel facebook.com/ we decided to reimagine how we did code splitting in the previous slides we talked about two classes of problems one is delivering too much code that isn't used to it's not delivering important code but really both of these are the same problem we don't know what code we need at the beginning of the request the reason for this is their code split ap is like the video player we only discover what code we need in the middle of the request while we're executing on client if we could somehow determine exactly the code that we need at the beginning of the request then we can make sure that we do exactly the code that the user needs let's look at how we fix their code splitting API for the new facebook.com first let's talk about a/b experiments here we have two variations of the composer if the user is part of this experiment they will receive additional functionality we want to make sure that only users that are part of this experiment receive the additional code before on the old site the code was imported in the middle of the startup sequence for the new facebook.com/ we decided to move to a new declarative API that's discovered via build stat at the beginning of the request we can determine if the user needs the additional code for this a/b experiment this means shipping the right code right away to the user by doing a quick check now that we've improve a B experiments what about on render on the old site we found that when we code split on render it's because the decision to run the code is based off data we receive from the server like one mention this is fixed by implementing data-driven dependencies using relay if the server returns a video post then we know right away we need to send down the video player now this leaves us with more more code split api to improve this is the one that I'm the most excited about we want to load the minimum amount of code and we want to defer any code that isn't required for startup until later Sydney new facebook.com/ we decided to reimagine how we deferred code first we want a design to drive the loading experience of the user and not the coastal point themselves therefore we've designer page load into three phases the first phase is responsible for showing an initial loading screen to the user the second phase is responsible for doing a full display and the third phase is responsible for making the page interactive each phase has a very clear purpose to the user to see how this works let's take a look at the home page dependency tree the page is composed of a root which is responsible for rendering the page structure in our case we have the left navigation news feed and the right column while we're waiting for code and data to download we want each of these components to be able to put up a loading screen and so you have the implementation of the components themselves which they'll also include which often brings in a lot more code and then your components will have many interactions such as menus buttons and editors and we want to make sure that we pulled in this last because it's not required for the initial display of the page so to shore a loading screen we want we don't need the majority of this code here we could load we could use lazy loading on startup but if we import that code lazily it means that we have to we have to render we have to wait until we're rendering the page to start fetching the remainder of the code and so this means another round-trip as we've seen this can make the page load dramatically slower instead if we just annotate the import using a new declarative API imports for this plane by creating a new declarative edges in our code dependency graph we're able to easily identify which resources are required for the loading screen and the display of the page and we can do this without having to run any of this code on the client we can figure this out right away now it's the same for a third phase we introduce a new declarative API call imports for interaction this lets us easily separate the interaction code from the display code with this we're able to take our code dependency graph and split it in two or three phases at Build time without running any of this code on the client this means we can take our single blocking download we can split it up into three phases and then we can do the rendering in three phases the loading screen the full page and the interactions separately each of these phase will render exactly what we expect according to how the site was designed but this leaves us with a problem how do we render quickly on slower devices on the new facebook.com/ we're building a strong foundation for server rendering by using server rendering we can show a fully rendered page while we're still waiting for code to download once we're confident about our implementation for server rendering we plan on exploring how server rendering allows new optimizations that aren't otherwise possible however the key to being fast on the web is to be as small as possible splitting into three phases isn't useful if you can make the code smaller to ensure that we're as small as possible we've decided to introduce code size budgets budgeting code size for us starts in the design process our startup goal is not driven by technical details but it's designed based on how long the user expects to wait Vanille facebook.com/ performance was one of the most important features we wanted to deliver what a start-up goal in mind we picked how much code could we load in that amount of time and this became our code size budgets but once we have a code size targets how do we track it for example let's take a look at our home page we break down a page by the features and teams that build on it here we have the top navigation the left navigation stories the right column and news feed with this breakdown every team that's contributing to this page is invested in keeping it efficient but we have hundreds of pages like this we created a tool where we can list the size of each of each page on our site and then we can calculate the code size by each of these phases then we add code size targets for each of these phase now at a glance we can see if any of these pages are too large and then we subdivide complex pages by the teams and features that build on it like we've just seen for the home page what the new site we understand where every bite is going on every page but we need more we need state-of-the-art tools that let us zoom into our code graph we need to get better at finding opportunities for size improvements we've built tools like graphics for that highlight all of the new declarative edges that we've all the new declarative code split api's that we've added the common ancestors of share code and how to remove modules with these improvements and tools we've been able to make code deliveries sustainable again but we need to do more than just mastering data in code I'd like to invite Ashley walking back to the stage I want to tell you more about how our technology stack enables us to deliver a great user experience for everyone by default we have over two billion monthly active users this means a huge range of network conditions device classes abilities people have and languages people speak and we need to provide a great experience across all of these situations and someone's experience really starts from the moment they type in facebook.com and hit enter juan and benoit have already talked about how we've made our data and java script fast but there's one more piece to the puzzle on our old site we were sending too much CSS this amount has grown over time if you load the homepage today you might download up to 450 kilobytes of compressed CSS with an uncompressed size of 2.3 megabytes this is just for the homepage alone here's how our styles are structured today we have a class there are few rules defined within it and another class and some more rules this is pretty standard but what's wrong with this rules are duplicated throughout the stylesheet and that just means wasted bytes so instead we generate an atomic stylesheet that means that we take these rules and we merge them together set each rules to find only once each rule is given its own class and components can then pick multiple classes in order to get the same effect before every new feature meant more lines of CSS added to our stylesheet now new JavaScript doesn't have to mean new CSS as we add new components they can use the existing styles meaning the style sheet will plateau at a low byte size and that single atomic style sheet can be used across the entire site but no matter how fast our data javascript and CSS are the page still needs to load how can make loading a delightful experience if we get this wrong the content is jumping around and the text images and icons are coming in at different times let's look at that again this doesn't feel good what's the right way to do this instead we want to load the content in the order that we read so in English we want to load top down and left to right first we load the top bar then the left column and so on but in order to do this the different pieces need to be coordinating any of the components on this page might be waiting on data code or images historically centralizing the experience with react would have required a lot of engineering overhead but now the new react suspense component can help us here we have a post if we wrap it in a suspense boundary then react can coordinate whether to show a loading state or the full content the suspense Pommery works like a try-catch first react will try to render the content but any of the images can be weight or any of the pieces can be waiting on code data or images let's say the body is waiting for the image it can let react now it's not ready and the suspense boundary responds to that and renders the loading state instead then once the image is ready we can replace the loading state with the final content react suspense simplifies the code necessary to make this happen and these boundaries are Nesta bull when nested this way we can create the top-down and left-to-right loading experience that we're looking for and here's what this looks like all together this gives us a small number of paints images come in at the same time as the content and the content doesn't move once it displays so we focused on the initial page load so far but what about subsequent navigations as we're using the site we continue to use the same optimizations for client side routing that wanna benoit already talked about for the initial page load we still incrementally load the code in data which lets us show the loading States early and get the final content on the screen as quickly as possible both client-side routing there's even more that we can do here we're on our newsfeed and we get a notification how likely do you think it is that we're going to want to click on that notification probably pretty likely so as soon as we show it we can begin to fetch that first download so that we have a head start we're immediately ready with the loading state and we have less code and data to fetch for the final render and if we have a high enough confidence and the code is small enough we might even prefetch the full experience letting us jump straight to the final render but can we do more think about using a native app on your phone you can move back and forth between pages without losing your place this lets you stay focused on what you're trying to do right now without worrying about whether or not you can get back to what you're doing before the new facebook.com/ supports this you can start here on your newsfeed scroll down click on someone's profile effortlessly go back to where you were respond to a notification and then go through your notifications list inline everything comes together to give us that app like feel with seamless navigations what about the experience within a page our research shows us that people come to use Facebook on all kinds of different screen sizes and many of them are quite small so we redesign the site with a three column layout to better serve our users allowing people to focus on the content that matters we also need to respond quickly to user input this gets tricky because of JavaScript execution let's say we're here in the loading stages and someone clicks on one of the already visible buttons we want to respond to the input but we're in the middle of executing a lot of JavaScript in order to render the rest of the page this is a typical JavaScript execution block and if there's a user interaction we'd have to wait until the current block ends in order to respond to it instead what if we could be periodically checking to see if we needed to respond to input then when we reach the next check after the interaction we could deep prioritize or cancel the current work in favor of respond to that input and this is now possible because of a new browser API called is input pending it provides a way to quickly check the event queue without adding a lot of overhead to the current execution block this is the first browser feature implemented by Facebook and the API is out in origin trial and we hope to see it fully launched soon this is just the beginning of our collaboration with the chrome team and other browsers as part of a broader effort for javascript scheduling on the web so we also need to be mindful and responsive to different device capabilities take this loading state for a post it's kind of nice right but we found that when we put a couple of these on a device with limited CPU and add in some other background activity we were actually maxing out the device and the entire experience degraded to handle this we check whether the device can stabili render these animations and if it can't we freeze the animation the experience is still complete but tailored to the user we've also done more to give the user direct control over their experience first let's talk about theming theming is important for a few reasons we have a different primary design for pages like the watch tab where a darker background experience is more comfortable for watching videos our styles and components need to be usable here as well and we need to swap the card definitions dynamically on a page navigation to support this we switched using native CSS variables for colors the colors change without downloading any extra CSS or refreshing the page this also enables something else dark mode low light situations are more comfortable with a dark screen and for some people it reduces things like eye strain and migraines in addition to theming we're also giving users control over their font sizes some people may want or need a larger or smaller font size in many websites today you have to use the zoom functionality to make the text larger but this also increases the size of things that you may not need to have increased like images which makes the website even more difficult to use in smaller screen sizes so we've changed to our CSS is built in order to support this let's look at three ways we can to find font sizes pixels MS and REMS pixels are static the font will always be that size regardless of browser settings or element positioning MS are relative to the parent size so you can set it to be a time-and-a-half larger than the parent font size but ms compound which means if you set all divs to have a font size of one-and-a-half ms and then you have nested divs the font will get larger and larger rems are similar to ms the relative but their relative to the root of the page this avoids confusion around nesting but also provides the flexibility to adjust the font sizes across the site so this is what we want to use but for engineers the hard part is that we tend to get our designs in pixels so to support this we automatically convert pixel font sizes to REMS and our build step let's see what it looks like focus here on the words themselves by using REMS we respect users browser settings for their preferred font size by default and we're able to give them a way to customize their font size directly on our website itself much of this so far has been about the visual experience of using the new facebook.com/ but there's even more to it than that this was an overhaul for assistive technologies as well I want to cover just a few of the things we're doing in this space first our new component library has been designed with accessibility in mind from the start a great example of this is what we call contextual headings on the notifications tab for a screen reader we want notifications to be the main level one heading for the page and then the event name could be a second level heading however when we're looking at the event page directly we want the event name to be the main level 1 heading to adjust this we've created what we call contextual headings that respond to the context therein to adjust these varying conditions all without any extra work from the engineer but solutions like this aren't quite enough when components are used in certain ways or in certain combinations it's still possible for accessibility issues to occur and traditionally we've been used things like education and lynching to try to prevent these but we took it a step further in our development environment if elements are inaccessible then we also visually and functionally block them out we also provide context about what's wrong so the engineer can quickly and efficiently solve the problem it's easy to forget about experiences that are not our own but we want Facebook to support a wide range of abilities and what we found time and time again is that a great user experience starts with a great developer experience the right thing should happen by default and when this isn't possible the right thing should be far easier to do than the wrong one by taking this approach we're able to do our jobs better and faster so that we can provide quality experiences to people that come to our website today Juan showed us how we can write and maintain intricate data dependencies seamlessly while delivering the pieces we need when we need them benoit showed us how with some changes to our api's and resource loading strategies we can deliver minimal efficient code and I've covered some of the many changes we've made to our user experience this is just the beginning we're continuing to learn and iterate and as we find solutions that work for us we'll be sure to share them out with you the community so that we can all improve the web together we'll be in the open-source section of the developer resource area if you have any questions or just want to chat and we'd love to hear any feedback you have about this session today so please reach out thank you [Applause]
Info
Channel: Facebook Developers
Views: 23,362
Rating: 4.9488115 out of 5
Keywords: f8, f8 2019, facebook f8, facebook conference, conference, f8 conference, facebook, family of apps, whatsapp, oculus, instagram, messenger, tech, developers, developer, mark zuckerberg, keynote, f8 keynote, keynote 2019, open source, graphql, react.js
Id: WxPtYJRjLL0
Channel Id: undefined
Length: 40min 46sec (2446 seconds)
Published: Thu May 30 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.