 Today, I want to talk about making great apps, both mobile apps and web apps, some of the problems that make that hard, and a few of the patterns and solutions that I think can really help us not only do that faster but actually make more robust apps that use less complex code and that are actually easier to understand. A little bit about me, my name is Lee Byron. I work at Facebook on a team called Product Infrastructure. So Product Infrastructure on this team, we build the technology and the tools that all of our products use to be the best that they can be. And in part of that, I work on a lot of our open source technology. So I'm a core contributor to React, I'm one of the co-creators of GraphQL, and I'm the creator and maintainer of Immutable.js. And I may mention these tools and a couple others just because they're relevant to how we're doing this at Facebook, but this talk is not about any specific library or framework. It's not actually about any specific platform or language either. What I want to talk about today is the architecture underneath the apps that we're all building. What I'm going to talk about applies just as much to an iOS app as it does to a JavaScript app. And these ideas can actually be implemented in a huge amount of ways. What I'm going to show you is kind of one way to do this idea, but there's a lot of different forms that this can take. And in order to do this, I think it's really important that we first get a shared understanding of what it is we mean when we talk about architecture. When we're talking about software, architecture is a metaphor. And what we're actually doing is we're borrowing from the idea of building actual buildings, and there's some really clear parallels that we can draw between these ideas. Architecture itself originates from studying one of the most important technological advances in building things, and that's the arch. This very simple shape has proved to be nearly infinitely composable, which allows us to build larger and more complex buildings than ever before. This is Vitruvius. He was a Roman scholar and architect, and 2,000 years ago, he wrote about the principles for satisfying good architecture. Firmitas, utilitas, and venostas. Firmitas means durability. A building should be robust and have longevity. And when we build apps, we look for the architectural choices that help us reduce bugs and improve performance. Utilitas means utility. A building should be relevant to how it's going to be used. The materials, the technologies, and the aesthetics that you would choose to build your home are going to be very different from building an office building or a place of worship or an auditorium. It's because each of these places is used in a very different way. Similarly, the architectural choices that we make for our apps can differ wildly depending on the complication of the app, how it's going to be used, if it's more static or dynamic, simple or complex, or if it's going to rely on more networked or live data. And venostas means beauty. Building should be both usable and desirable by using proportion, repetition, and scale. And obviously, we want our apps to be desirable and usable, but what I believe the parallel here is not to user experience, but to developer experience. The architectural choices that we make, they impact not only how easy or hard it's going to be for our teams and ourselves to improve our apps, but to also iterate on those over time. So architecture is about making fundamental structural choices about how we're going to build things with the qualities that Vitruvius set out for us 2,000 years ago. In software architecture, we make the same fundamental structural choices based on our understanding of what our app needs to do and the challenges that it's going to face. We choose our few elements, our abstractions, to create variation and robustness to support the role of our app, solve its primary challenges, and create a productive space for ourselves and our other developers and engineers. And for the information-rich user-facing apps like the web and mobile apps that a lot of us in the room have been building, there's been a really dominant architecture over the last decade or more, and that's MVC and REST. And for these kinds of apps, there's one single biggest challenge that we need to solve, and it's this. What changed? How can I tell when something has changed so that my models, my views, and my servers stay up to date with each other? And I believe that MVC has really just left us with a poor solution to this problem, because it attempts to solve this by adding even more things that can change. You've got models sending change events, views that are easy to get out of sync, and race conditions when you're doing multiple things interacting with the server. When models and views both need to listen to each other for changes, it's easy for what seems like what would be a small change to have these cascading effects that create unpredictable effects on your entire application. And there's also a second big challenge that these information-rich apps face, and that's synchronizing data with our servers. You know, modern mobile networks, they're just really crappy. Okay? First, there's latency. We can measure latency often in large parts of an entire second. So minimizing requests to our servers is actually really critical for performance, and then there's intermittency. So we often talk about supporting offline mode, but as we all know, no one goes offline. Instead what happens is, you know, you go underground on the metro or you're driving in the car between cell towers. So what we're actually talking about when we say offline mode is intermittency mode. So handling these kinds of network failures is really critical. And while REST helped us model our data in terms of a network service, it can really be crippling to performance when our app needs to load lots of data that's dependent on loading lots of other data. So let's take an example, okay? Say we want to build a social calendar app. We're going to need to get our friends. We're going to need to get, you know, their names and their profile pictures, their avatars. We want to know the next events that they're all attending and the names and the details of those events. And so we're going to have to load a lot of stuff from our REST endpoint. And since REST APIs typically return URLs when one resource references another resource, then we're going to end up loading lots of these things dependent on others. We're going to have to wait for previous URLs to load before we can even start loading the other ones. And that means that even the new stuff that we're getting, like HTTP2 pipelining, it just really can't help us solve this kind of problem. And when network latency on a mobile connection is measured in big chunks of a second, this stuff adds up really fast. You're talking about for even moderately complicated apps, three, four, five second slowdowns just on the latency, not to mention all of the other things that have to happen all over the network. So needing to deal with this problem of handling both a flaky network and a slow network, it puts us in a really sticky situation. We might want to show a loading indicator if someone performs an action in our apps, but networks can be slow. So we might not want to have them sit and stare at that loading indicator. We might just want to guess what the server is going to do and show that to them right away. But networks can be flaky. So what if that doesn't work? Are we going to roll it back? What happens if they've done multiple actions at the same time and we've got to figure out what order to do these things in? It's a really big mess. And not only is this challenging, but it's also unfortunately a very common scenario that we need to solve in our apps. And so I actually think that there's a really powerful technique that we have to get a better control on these problems. And that's by tightly controlling what can change in the first place by embracing immutability. What's really exciting about this to me is that immutability is not more complicated than what we've already been doing. It's actually less complicated because we're removing a feature. We're removing the ability to mutate data. And when we give up the ability to mutate data, we're giving up some power. We're giving up the power and the abilities that come with that power, but in exchange for that we get new principles. And when we have principles, we have new guarantees about what can and can't happen in our programs, and we can leverage those principles, those guarantees, to do performance optimizations and other whole program optimizations to get a better, you know, app speed overall. And I really think, you know, this is the crux of what we do as software engineers. It's all about balancing between power and principles to get the power that we need to build the apps that we need to do, but also get the principles that we need in order to build the best things. And if we only cared about having our software have the most power possible, then we would have stopped at assembly or go-to, right? Like, all languages are turned complete. So, you know, we really have the same power everywhere. And go-tos are incredibly powerful. You can do a lot with them. But instead, we were always looking for more principled ways to apply the power that we already have. You know, so of course most modern languages, all modern languages, have forgone go-to in favor of for loops. And, you know, you can't do everything with a for loop that you can do with go-to. But the principles and the constraints that it provides, let you guarantee some things about what can and can't happen in your app. And many of the, you know, more modern languages, even for go-for loops in favor of map, filter, reduce, and other higher order functions that happen on things that you can loop over. So it's really not the power of your software. It's about the principles that you can leverage. And a mutable data brings with it these principles, this guarantee about what can change and when so that we can use techniques that previously we couldn't rely upon, like memoization and time travel. We'll talk about those a little bit later. So, you know, I'm done with MVC and REST. I really think it's time to rethink the core architecture for how we build all the stuff that we build all the time. And as I've been observing the apps, both that we're building at Facebook and that I'm seeing built by the broader development community, I'm seeing a new kind of app architecture emerge to better solve these problems that I like to call the immutable app architecture. And it's heavily inspired by similar architectures that we've seen and may have seen in functional, reactive programming environments, which has been the result of many years of research. So, of course, these aren't all my ideas at all. And it's actually heavily inspired by the programming language Elm, which you'll hear about right after me when Jack presents. So, immutable UI doesn't mean that our screen is frozen and you can't do anything with it. It's not like we're going back to 1998 web pages. Instead, what it means is that we're just going to embrace the principle of immutability at each point in the architecture of our application. That means that we're going to be able to control exactly where changes occurred in order to apply some techniques like memoization and time travel to build faster, more robust apps. So, let's start with first principles, views. Every platform that we care about building apps for already has a concept of a view. The web has DOM elements. iOS apps have UI views. Android has Android.view. But all of these have a similar API that helps you build this view tree, right? We want to use these because they can be used on each platform. But these APIs, they're not great. You create elements. You alter their properties. You add them to parent elements. All of these are mutative operations. We're talking about immutability. And after this, you got to keep track of all these references. You do things like looking stuff up by ID and keeping things around as members in your classes. It's really hard to do this. There's just a lot of stuff that you can do. So what we really want is an abstraction on top of views that lets us declare what views should be displayed at any point in time. And we call these components. And a component, it just defines this peer function. Kind of the state of things, the state of the world goes in and what you want to see on the screen comes out. And what a good component library should do for you is provide all the hard work of managing this mutable view API that all of the platforms that we care about actually expose. And it needs to do that in a way that leverages all the performance techniques that we can get when we apply this limited power in favor of the principle of having this one function to define things. So let's take a closer look at this. Just an example. In my example I'm going to call this MyView. And it's going to have some data. And when we render this component, it's going to have some visual views. So virtual views are a representation of what we ultimately want to see. They have the same structure, elements, properties, children. But they're really just in memory objects. They're not on screen yet. And what that lets us do is the first time we render these components and we get these views, our component library can take the virtual views and then create the actual underlying native views and then our component function is called a second time, we provide some new data, then we're going to get a new set of virtual views that's the output of calling that rendering function a second time. And so what our component library is going to do is compare virtual views from when it was previously called to now when it was just called and it's going to compute a diff. And then computing a diff, what we're going to get is a translation into the set of mutable components into the state that we want them to be in. So good component libraries can do this very, very efficiently. Running really complicated apps at 60 frames per second, even on some mediocre mobile devices. So components are really important aspect of this architecture and of course React is an excellent library that implements this concept. React lets you write pure JavaScript functions and then manages the mutable DOM elements that you want to use. So there's React Native. React Native lets you write these component functions actually using the same React API in JavaScript but actually will bridge down to the native UI framework. So it will use UI kit on iOS, Android views on Android, it will even use Windows UI views if you're building for Windows. But then there's also component kit. Component kit is this exact same idea but it's entirely implemented in JavaScript. That's fine too. You can dive in here and you can write pure functions in Objective C++ and manage the underlying UI views for you. And by the way, if you've ever used Facebook, hopefully you've used it once or twice and you've used the news feed on an iOS app, you've used component kit in action. That's how all of our news feed product is built. So these component functions, they need some data as input and that's where models are going to be built. Data management libraries before these are immutable models which means they're just pure data, they're very simple, accessors only, no setters, no events, nothing like that. And I really recommend building these in JavaScript, just plain old objects or whatever the equivalent of your plain old pojos in Java. Or you can use persistent immutable data structures if you need to do something more complicated like the ones that I mentioned before. And then of course we need to get those models from our server. But I want to first take a closer look at these models and components together. So these two concepts are actually really tightly linked. They're coupled. When a component wants to render something new, you're building your app, you're adding a new feature, then you want to make sure that the corresponding model also is knowledgeable about whatever data you need for that new feature. That means that the server is also going to send that new data as well. So we're going to add a new feature. And then we're going to make sure that it's represented in our models as well. And since one model can appear in multiple views, and it's easy for these to end up in kind of different parts of your code base, it's really accidentally possible to kind of do something in your view and forget to put it in your model, and then you got broken UI. Or what I found is actually more common is that you're refactoring and you're kind of deleting some experiment that didn't work or something like that, and you forget to remove it from your model. And now you have not just the function that you need to take the state of the world and give you your views, but also all the things that it will need from the model, then you have both of these things within the same place that define both what data you need and how to render that data onto the screen. Because they're described right next to each other, it's really easy to avoid these problems of overfetching, underfetching broken UIs. And we call this co-located data dependencies. And at Facebook we do this using a tool called GraphQL, which if you're here for the back and you might have already heard a little bit about. But if you haven't, GraphQL is an API query language that we've developed at Facebook as a replacement for REST, and it powers all of our mobile apps at Facebook. And if you haven't seen GraphQL, I'll just give you a brief overview of what it feels like to use GraphQL. So this is kind of the hello world of GraphQL queries. I just want to get my name. And the first thing you'll notice is that this looks a little bit like JSON. We've got these curly braces. They look a little bit like objects. We've got these things here that look a little bit like object keys. We call those fields. And if we send this query to our GraphQL server, it's going to resolve and come back as a JSON body. And one thing you'll probably notice here is that this JSON body and the query have very similar structure. And this is an important part what makes GraphQL easy to learn and use. And it also provides some principles that lets us do some really interesting optimizations with GraphQL and sophisticated systems. And we can also request fields of complicated data. So here there's a profile picture and avatar. And it's got, you know, not just a URL to go load, but also the width and height so we can make sure, you know, we get the right region of the screen before that URL comes back. And I bet you can already guess what this looks like if we run this, right? It's going to look exactly like the query. But more importantly, GraphQL is designed not just to query stuff about one resource. It's designed to smoothly navigate between resources in a single query. So for example, I can get not just my name, but the names of all of my friends and do that in a single query. And notice that here's where a REST API would have given us a list of URLs to go load a second time. Or maybe, you know, you've got a sophisticated REST library that lets you pass some additional fields that you want to do and, you know, get these names instead of URLs. But it's a little bit harder to figure out how that multiple levels down. And GraphQL makes that really easy. So remember our example from before where we were making a social calendar? Well, this is all the data that we need to render that social calendar. We've got our name, we've got our friends, we've got our events, right? And we can do that in a single roundtrip. Huge difference. Dramatically improving our network performance. And in order to match how developers, how they think about views and to allow for portions of queries to be recycled, GraphQL has this language feature called fragments. And what fragments let us do is describe co-located data dependencies right next to our components. And so here we have a component that describes both the data that we need and the rendering function to do a profile picture on Facebook. So we need a profile picture of our user, we need the width, height, and URI, and then we're going to render an image tag. And what we can do is we can use that component within another component. So here we're rendering, you know, maybe a row and a list but we want to know the name of the user, we want to know if they're friends with them not so we can render this button and then we want to render this profile pic. So of course we're going to need the name and the are you friends but for profile pic it's a little bit cumbersome to repeat that data so instead what we do is we just reference this other fragment and then we get all the data that we need, no more no less. And query fragments, they let us define not just the requirements next to one component and compose them one time but they compose all the way for your entire app so you can have a really complicated application which tons of nested views and then you can describe all those things and roll them up into a single query that we can run in one round trip to the network and get all the data exactly as you need it no more no less to render your app which is huge for network performance also for understandability. Okay so what we have so far is great for like a read-only app like maybe a news reader or something like that but what about apps where we have actions we have stuff where users are going to interact with our views. We need to perform actions we have these models that are immutable so we're going to need one place where we can apply those changes we're going to call that state it's really just like a bucket where we can hold on to all of our models. So we also have the initial query to our server that's going to give us kind of our initial state and of course we want state to also be immutable so we can't change that thing so what exactly is happening here? Well let's look at an action an action is just a function it takes the old state of the world in and it returns the new state of the world so you might write an action that takes in the old state of the world and then makes some changes to some models applies whatever change you want and returns a new version of the state with that change applied and once this action returns a new state with some new models then we can provide those to your components which are going to go back to your views which are going to update the underlying native views those then you can act on and we can then get back to our state again so it's a loop and so you might be thinking okay that's fine but like these functions that change your whole state you're going to do that for every time the user does anything that's going to be really slow right? Well actually not really and not really because we can leverage the principle of immutability to do this really cool thing called structural sharing so what structural sharing lets us do is take a previous version of some immutable thing and some change we want to apply and then recycle as much of the old version that thing as possible literally all the underlying memory we want to recycle if we can so let's we can do this for plain objects but this actually enables an entire class of really interesting data structures called persistent immutable data structures I'll just illustrate a little bit how this is going to work and by the way if this is interesting to you and you're a JavaScript person you can check out immutable.js which is just an implementation of these data structures so let's take a look at this example here there's some structure of data and we want to make this change so in order to make this change and we can't do it in place we kind of have to copy this node with a change applied and they can reference other nodes that's fine but anything that points to it also needs to change in order to have that new pointer again no mutations so we copy that thing and we do that all the way until we get to kind of the top of our data structure and once we do that then we have a completely new structure with our change applied and importantly we've recycled a lot of this you know this is super contrived I just got it to fit on the screen but it still recycles more than 50% of what's happening here and in practice it's more like in the 90 plus percentile most importantly we haven't changed anything about the existing data structure we can use this continuously that's totally fine we can actually use both of these at the same time if we want to that'll continue to work and as soon as we don't want the old one anymore we can just let go of it and the garbage collector will handle that for us and any modern JavaScript VM or Java VM or anything like that with a good garbage collector this stuff is super performant it's not going to be a problem and so by doing this we can really leverage immutability while still having very good performance for CPU and memory and a prime example the kinds of things that we can do when we have this is memoization so memoization is when you take a function and you know if you call it a second time with the same arguments you want to kind of skip running the function and just return whatever it returned last time so this is a cool performance technique that you can do for expensive functions like rendering a UI and so I just want to show like a really basic example this is going to be the sum function so we're going to take a list of numbers we're going to add them all up and I know I'm going to give this like a really big list of numbers so I want to memoize it so I'm going to give a list from one to a million and then I'm going to add them all up and then I'm going to push on to that array and then you know call this again so this is what I want to see right so I went looking for this function I literally went on stack overflow and it's like JavaScript memoize and it was like here's the most uprated version of memoize I simplified it a little bit just to get it to fit on the screen but this is largely it and I don't know if you guys see the problem with this but it's this JSON Stringify of course we're doing memoization to get better performance but JSON Stringify is really expensive especially if the input is big which is exactly why we're doing this and so as you might expect running the memoized version of this is actually much slower than running the not memoized version totally not what we want to see happen so I rewrote the function and instead of using JSON Stringify I just want to see if these two things are equal that's all so I did this so I ran it and at first it looked really good right I called it a second time and it looked like it was basically free and then I pushed my next number and I called this again but this time it didn't do what I expected it looks like it memoized and it didn't return the right answer of course this is why an array is mutable when you push into it you don't change the identity of that array it's still equal to itself so this check passes and got it now I understand why JSON Stringify was there but let's try that same thing and use a mutable list from a mutable JS so same exact implementation of memoized I haven't changed anything I've just replaced arrays with lists immutable lists so far so good this basically works the same and if you're paying really close attention you might notice that calling some on a list rather than array is a little bit slower that's to be expected immutable data structures are a little bit slower than their mutable cousins but what we want to do is reap that performance benefit back by applying these global optimizations and now I'm going to push into it what will happen with the immutable data structures when you push into it rather than changing that thing it's just going to return a new version of itself so we're going to make sure that we keep track of that and then we get the answer that we want so this is great so back to our diagram we're missing a big important piece and that's how our actions are going to communicate with our server doing that synchronization so let's go back and look at what an action is an action is this function that takes the old world and returns the new world but if we're going to go to our servers this isn't going to cut it we need asynchronousity we need to be able to wait for the server to get back to us so what we really want is a promise or a task that will eventually give us back our new state but like we talked about this could be slow we don't want to be slow we want to show people updated UI as quickly as possible and even on a really fast network connection even if you're on a desktop computer and you've got a hard line people are really good at noticing delays we notice delays in hundreds of a second so even on a fast network connection it's noticeable but of course on a more typical slow mobile connection it's going to be just really painful so here's actually what we want to do we want to return immediately our guess at what we think the new version of the state is going to be which we can do locally but we also want to return a promise for the real version of the new version of the world that the server is going to help us fulfill and that brings me to one last problem and that's what do we do when there's multiple actions happening at the same time and for this we need a queue so this is the last important part of this architecture and it's going to make sure that things happen in the right order and we don't get race conditions so a queue, what it's going to do is going to keep track of the most recent version of the true state what our server thinks is true and then it's going to keep a list of the options or the actions that are currently in flight so say a new action comes in what we're going to do is immediately compute what we think the state is going to be we call that our optimistic state and then we send this to the server and if a new action comes in, that's fine we just add it to the queue and we compute a new optimistic state and then when the first one is done that's great we can just remove it from the queue we're going to get a new version of our true state from our server from having done that and we'll just recompute the optimistic state if there's anything left in our actions queue and then when that second one comes back then we're dealing with true data again nothing new to apply we just have true state, optimistic state, the same thing so this is really interesting because it lets us model apps that let us first create a thing like a to-do item wouldn't be complete without a to-do app example here and then lets us make changes to that thing even before the server knows that it exists and this is usually something that's hard to do so first we're going to create a to-do and then we're going to change some stuff about that and at this point we don't really know how to tell the server hey set the due date of this to-do item that we don't know anything about yet so we've got to wait for the server to come back and say well here's the unique ID for that to do and then we can replay that action to get a new optimistic state and once that has been done then we can actually have the information we need to go to the server and give it the right info to do that thing and then we have the state of the world that we want so this kind of behavior it's typically really hard to do in traditional apps you may have bumped into this before I do kind of all the time and it's really hard to do without creating race conditions and so this technique lets us avoid the majority of the race conditions that we end up running into alright let's take one last look at this whole architecture our components describe the data that they need which informs the shape of our plain old object immutable models which are first created as the result of a structured query that we send to our servers which creates our app's initial state from there these models are provided to our components function which is going to describe our views which is actually created in the underlying UI view framework and then we're going to interact with those views which is going to create actions those actions are going to optimistically update our states but they're also going to be added to that queue and asynchronously sent to the server to let them know about that change and when the server responds we're going to get the action removed from the queue applied to get an update version of the state which is going to contain new models that again to our component rendering function which is going to describe new views which will then take the underlying native views and update them as efficiently as possible and then we're going to repeat that forever and ever and ever and ever until the user leaves your app you know kind of depending on how good your growth team is so this I think makes some dramatic progress on solving the challenges that we have in the modern apps that we're building today let's just keep track of what's changed keeping our UI up to date synchronizing with the server that more and more recently is at the end of a slow spotty network connection and we can do all this precisely because everything is immutable if part of our codebase, any part of our codebase could edit our models or reviews at any point in time which is super common in NVC frameworks then we couldn't use any of these techniques anymore we would have given up principles in favor of power we couldn't apply actions in order we couldn't easily roll back state we couldn't do these optimized view recycling techniques and we couldn't do all this on multi-threaded platforms like iOS and Android where a mutation makes jumping between threads very difficult and also this architecture it's simpler than NVC and REST because there's no direct mutation so I know I've covered a lot of pieces here and it feels complicated and of course component libraries are very sophisticated and they can be that way and they can be very useful principles, pure functions immutability and composition and what makes me really excited about this is that this powers a lot of our apps at Facebook where most of our mobile apps actually follow this pattern I mentioned earlier that if you use newsfeed you're using some of this stuff newsfeed was actually one of the inspirations for putting this talk together so I really think that traditional NVC and languages that have mutation by default are going to slowly be added to the pile of old ideas but I'll leave you with this NVC and REST they did not solve all of the problems that we face in app development but what I've presented today also won't these ideas are going to be far from flawless and they might not be right for the app that you're building and I want all of us to continue to challenge the notion that there's like one right way to do things that's not true and also just recognize how quickly and continuously our ideas about what good architecture is and good technology ideas are just every time I finish building something I just have this urge to go back to the beginning and start over because of course by the very nature of having built it I've seen all the things that went wrong and I want to do it again with all the ideas that I have to make it better but of course if I did that as soon as I was done I'd want to do it again and there are other ways to do it better and so for just this reason I think it's the process of this architectural improvement it's just never ending it'll never end and we're never going to end up at like an architecture in Ravana where we have all the answers it's just not going to happen it's an important realization and what fuels me is this process of exploration and trying new things it's the process of improvement that's exciting rather than some mythical perfection in our apps so I hope that you found this these ideas of immutable app architecture interesting hopefully they're useful for some of the things that you're building today but more importantly I hope that it inspires you to continue to improve on these ideas so that we can all build even better apps tomorrow so thank you is there like a a monitoring that you do at Facebook to make sure that things are performant how do you take off yes I mean I would say that almost every engineer is charged with some performance metric and we have tons of engineers that are literally dedicated to performance by their building tools doing that performance monitoring it's a super important part of why we're doing what we're doing and actually performance drives a lot of the architecture choices we make so both react and components in general what it lets us do is measure the time it takes to render very small pieces of the screen so if something gets slow we know exactly where it is in the app that it got slow and same can be said about GraphQL rather than having one endpoint that does a bunch of stuff and then returns you JSON and if it gets like a little bit slower you might not notice and GraphQL we measure the performance of every single field so if one field slows down maybe like 10x and it went from one millisecond to 10 milliseconds your whole query that went from 500 milliseconds to 510 you might not have noticed but seeing one to 10 milliseconds in one field you'll definitely notice and so just making sure we're making choices that help us measure that stuff and then optimize that performance is just super critical Excellent I know I spend lots of time in Southeast Asia and obviously people's experience of Moa networks is significantly different from here given all the JavaScript that you use what do you do for people with like our old feature phones in Burma and Bangladesh and places like that Yeah we actually build totally different versions of Facebook we have a product that's called Facebook Light that actually lights a little bit of a misnomer because it can do everything that the main Facebook app can do but it's really optimized for lower lower quality versions of Android and then lower quality networks so the common networking technology in India is a 2G connection so I don't know when the last time you guys used a 2G connection here was 2006 maybe 2005 before it was replaced basically worldwide or country wide with 3G dealing with 2G connections is more of a latency problem actually than a bandwidth problem and so we spent a lot of time thinking about how we can do all the work that we need and as few trips to the server as possible which has also driven some of the architecture we've So it's reducing round trips to the server Absolutely that's always number one Excellent a question from the crowd Why you no use semicolons in your JavaScript I'm actually really inconsistent with it so kind of per project I try to be consistent within each project but you know if you're curious about this you can look up some stuff about automatic semicolon insertion and how sometimes it can bite you and sometimes it can and I would put that as one of the great debates of the JavaScript community along with like should we pick 80 columns or 100 columns at the end of the day it doesn't matter as long as you're consistent with the code base Talking of great debates, GIF or GIF I know that it is supposed to be called GIF but I'm sorry that's something that you eat and GIF is how I've pronounced it since I was on the internet and you'll have to kill me to make me change and that's the right answer Give it up for Lee