 So as Adam said, my name is Asa Kasuma, I'm from LinkedIn. And today I just want to talk about a few ways you can become a controller again in a good way with a couple of tools, including promises and controllers. Yeah, so I said I worked at LinkedIn. I'm a mobile web developer outside of web development. So I'm at about like a quarter. So yeah, I've been at LinkedIn for about almost a year now. I graduated from school a little over a year ago. So these are probably my favorite photo of me writing JavaScript since I've been doing it for quite some time now. Yeah, let's talk about mobile web and LinkedIn. So in 2011, we launched a, it wasn't our first mobile web experience. It was the first back bone app that LinkedIn had in production. And then about a year later in 2012, we decided how we want to give our back bone app a face lift. So we just want to change the views and change how they feel. So that should be easy, right? Unfortunately, that was not exactly the case. And so today I want to talk a little bit about the learnings we had from refactoring our mobile web experience. So after we launched this new mobile web experience, we kind of got in a room together and said, OK, what can we do to improve the infrastructure of our development environment and how we can improve writing code and how can we make our site faster? So we wanted to just improve our site, not thinking anything about products, but just about improving the technology. So we formed this team. We called it Team Austin. And our slogan was the Wild West Refined. So what we kind of liked to characterize our situation was we had a Wild West in our hands. So we expanded really fast. We launched some really cool products. But we needed to step back and kind of bring some infrastructure to the website that we had built. So a couple of things that characterized what we called the Squad West. So Brian did a really awesome job of talking, I think, yesterday about controllers and routers. And so one of the things he talked about, which I wanted to echo, was this concept of routers being super bloated if you don't kind of properly funnel code around. And so this is a picture of what one of our routers looked like. This is actually after a major refactor. As you can see, we had actually implemented AMD at this point. And there's a big AMD plus at the beginning. But it was just like this super huge, unwieldy thing. And it wasn't very fun to work with. Another thing we found was that our control flow was less than ideal. So we would often have two asynchronous operations that had nothing to do with each other, besides the fact that after they were both done, we needed to do something else. And so instead of doing this in parallel, we would often do this in series, which is not good. And then one other thing we found we had was we didn't really have that much abstraction. And so we would have views where we were doing die manipulation and also caching. So if you wanted to change some visual components, you also had to know about our caching later, which was also not a good thing. So two things. One, we're actively taming our Wild West. So we're not done yet. We're in the process. And so today, I just want to share with you a few of the things that we're actively working on, some of which are in production and some of which are not. The other thing is that we are actively optimizing for our mobile web. So we have some other backbone stacks on our site. I'm not going to be talking about those. I'll be mostly focusing on mobile. So we have a feeling that we aren't the only ones for both of these things, with respect to both developing for mobile and with respect to actively improving our backbone experience. So promises, that the heart of promises is we're talking mostly about ways of simplifying and abstracting asynchronous backbone. And so the main concept here is that we want to create some kind of object that we can use to, given an operation that promises a future result, have a handle that operation. So like I said, the heart of this is abstraction. So abstractions are easier to control. And this is kind of the theme of this song, way easier to wheel. So promises basically allow us to reason and think and manipulate asynchronous operations in a synchronous manner. This is huge for readability and maintainability. So a couple of details about promises. So there are lots of ways you can implement promises. I'll be talking today mostly about the A plus spec, which is a set API for interacting with promises. There are lots of libraries that implement the A plus spec. I mean, you're talking mostly about Q today, because that's what we use. But you can use any promise library like when it uses. And then the main API to a promise is the event function. So the event function allows you to attach a handler to your promise object. So the cool thing about our promises is that backbone and jQuery already use them. So any or most backbone asynchronous operations return a promise that you can use as do many jQuery asynchronous operations. So they return a JQXHR, which is the promise object that typically represents an XHR operation. So why are promises cool? Promises are cool because if you have, let's say, a callback chain, you can easily string them together in a more readable fashion. So here we have, say, three operations we want to do in series. Typically, we'd have a callback chain. But instead, we can flatten that into one last bottom. So it's pretty much to work with. So as I mentioned earlier, we had this issue where we would have two operations that really could be done in parallel and instead were done in series. So typically, if you want to make your code more efficient, a lot of times you'd have to add more complexity to your code. And so we had both complex code and inefficiency, which was definitely a lot of good. So this is an actual chunk of code from our repo about probably six months ago. So I changed some of the names, but this is actual code as a production. So if you see, there's three callbacks. So first, we do a page animation. Slides the page over. Then we go fetch some data. And then we actually display that data. So we can actually do this page or data fetching and animation at the same time. And so this is kind of what this looks like after. So we use Q, but you can use any promise library. Most promise libraries have an all function, which takes two promises, and it kind of merges them. So it returns a promise that will be completed when both promises resolve. So we modify the show page function to return a promise. And then, like I said earlier, fetching, back when fetching by default returns a promise. So you pipe those two in, and you get out a new promise, which you can attach a handler callback to. So notice, right now I'm using anonymous function as a callback. That's for readability sake, the presentation. In real life, you would want to use a member function. So it's way more readable, and you can actually test it. So that last slide, we discovered it makes it a lot easier if you put that in a controller. Like Brian described earlier, controllers are pretty awesome. And we kind of went back to this idea of paying the C back in NBC and using controllers. So backbone is not a framework. We've talked a lot about that. And it's really a library for building a framework. So the cool thing about backbone is it gives you the developer lots of power. And with that power comes responsibility. And that responsibility is to build the framework that you want to use. And we found out that if you don't really build that framework, that each developer kind of builds his own framework, which can be a problem when you have lots of developers working on the same backbone app. So one of my favorite concepts that Nick Zekis talks about is this concept that everything should have a specific spot to belong. Kind of a concept of if you're building something and you don't know where it belongs, that probably means you either need a new component or you need better documentation. So this issue of, oh, where does this belong? Really it should not pop up. So why the controller? First, NBC is pretty well known. Lots of people have used Ribbon Rails or old PHP frameworks. First learned NBC on a PHP framework. Kind of one distinction that Tina at least tripped up myself was when you learn NBC on the server side, controllers are a little bit different because the controllers are pretty much dead once the HTTP request is served up. So they don't really have an extended lifecycle or else in the client, you really have to worry about that. As Brian talked about, and a lot of people talked about, controllers are really useful for draining your routers. So if your routers are bloated like ours were, it's really nice to glue a lot of that code into the controller. And then it also reduces points of contact. So your router has less dependencies, and then your controller has less dependencies because you split them out more. That also helps with testing a lot because our router tests were ginormous before because we would have to pull in so many dependencies. The other thing we did was we built this somewhat specialized concept called a page controller, which does exactly what you think it does. It represents basically a screen on a mobile app, and we found that concept was super helpful. So one thing to talk about is controllers are finite. So there's another guy I work with, LinkedIn, who's really big on calling controllers operations and not controllers. It's a lot of times he feels like people don't remember that it's a finite process. So controllers start up, and they can be killed in the future. And I'll talk a little bit this more later, but it's really important on a mobile device to conserve memory. And so we definitely had to make a lot of use of stopping a controller. So if we had some polling going on and within the controller, and we wanted to hide that controller, we needed to make sure that we could pause it so we could stop any polling that was going on since the ACP professor showed us that. And like use, you can unbind them and destroy them, which just like you would clean up a view, you could clean up. So another important concept for controllers is this idea that your components should be monogamous. And what I mean by that is you should try to have each component only interact with one layer at a time, one app layer. So kind of the same principle as separation of concerns. So kind of an extension that is kind of a layer principle. So this is a diagram of what the flow that you might use in an app. So if you have a view that had the form, user clicks on the form to submit it, that event gets bubbled up to the controller. We also sometimes use event delegation. So instead of a bubbling event, we actually have a reference to the parent controller and we call it parent controller directly. You can do that as well depending on what you prefer. And then the controller we do have a generic method that handles actions from the view. So I think someone talked a bit earlier about data binding being from hell. We actually experienced some data binding or model binding issues where we would have a model that was passed around to a bunch of different views from controllers. And some controller view would trigger an event and an unexpected side effect would happen in another component. And so one way to get around this is we have kind of proxy an event to one object. So there would only be one event, only one object is listening. And then we use this like create record function kind of as a hook. So if you want to add anything else to what happens when on save click has happened, you put that functionality in create record. So it's a little more readable and you don't have to go looking around for where a model is bound. And then the controller would call save record on the model and then the model has some data that is bound to function or any other data functions you might have. Then we start the process back when we go back up or down this diagram. So we have a promise resolves, a asynchronous operation to fetch the model. The controller has a handler to the promise and calls process new record. And a process new record is that kind of generic method from coming back up the chain that you can use to write any hooks for. And then the controller then calls a view and notice the view does then does the domineculation. So one important thing to note is that each layer has its own distinct vernacular. And we want to use that vernacular whenever writing or naming functions. So we don't want to be talking about the DOM as much in the controller. That way it's kind of easier to read components as you're going through. The other thing that controllers really helps us with is we call sane routing. So you're not pulling your hair out trying to get your app to route the way you want. And then one other thing we did was some of our routers, instead of just calling a simple callback function, you can set up a hook function. So this example we have two routes. We have a post reading route and you can also write to that post. And so to write, anybody can go read a post but not everybody can write a post. So you have to be the owner of that post. And so we wanted to make authentication really easy. So you can write a hook that returns a promise. So the promise resolves, the router allows the app to continue on to that page. And if the router is, excuse me, if the promise is rejected, you're not allowed to go through that page. So that way it's super easy to set up authentication. We actually have this issue where we can really set a set way to do authentication. And so we had like four or five different ways to authenticate a page, which is really bad because anytime you wanted to add code to this. So this is how we actually implemented what I just talked about. So here's again a route hash and we had this contract have an application which a lot of people also talked about earlier. Same deal, you have one overarching object to control the application. So when you create your application, you create a router. And one thing you can do is pass in, we call it a proxy router function to the router. And what this is is it's a single point of entry to a route change. So anytime a route is changed, and if there's an authentication hook, if the promise is resolved, you'll call this router proxy function. So the app knows anytime you change page. So you pass in the action. So we went to read, read would be action variable. And then it would pass in the first parameter. So in this case, ID, which will write. And then it would find the page controller and fire up that controller and show the page. Oh, I forgot to mention, one other cool thing this does is if you want to have your router available on the back end, like I know the process, it's way easier to like say, email that on the server if your routers are small and only care about routing. So we talked about controllers, but really the main point we want to get across is that you really need to expand your component toolbox to what fits you. So we've also added several other components like a view model, which I'll talk about in a second. We also have a router map, which essentially just the router hash. So that way it's just a JSON object and it's super easy to include that in your node instance. We also have a data store, which is a lot like what Bob Holt talked about, kind of a combination of a repository and a factory. So it's a model factory. All the models reference it. And it's kind of what we use to override backcode.sync. And it has our caching layer, all that jazz. So it's kind of a separate data layer. And we also have page controllers, which I talked about earlier. And kind of the cool thing about page controllers is if you have, if you're using AMD or any other modular system and you're using page controllers, you immediately have a dependency graph of every page. So if you want to build a bundle per page, that's super easy to do. So let's talk about a little bit of models. We mentioned them a few times and this is how we implemented them. So we firstly have a data store, which has a model factory function to get user, so get the current user. And then the callback to that is on data. So on data is kind of a generic term for whenever we have the data that we need to get the user model and we take the output of the two JSONs function and shove that into the user view model. And then we pass that view model to the view. Now in the view, this is how we use it, but in the model we have this idea of an exports array. And the exports array is what gets passed to the view. So someone earlier talked about essentially extracting your logic from your template and into your model, and this is super helpful. One, because it's way easier to test your models than it is the output or DOM. And two, because it gives you a layer of abstraction and like I talked about earlier, if you have a model that's passed into a view, if that model is also passed other places in your bonding to that model, any component that has that model suddenly has an API to your view. And so you run these encapsulation problems where views can change other views unexpectedly. And so one thing we don't do is there are no events on view models. It's just a facade to the model and it's really dumb, which is a good thing. So if we have an exports array, we call this example, it's called full name. So when the view model causes two exports function and builds an export object, what it does is it goes to the exports array and says, okay, is full name an attribute on my model? If it's not, it looks for a function called full name. If it does, it just returns whatever that returns. So this is a good example of how you might put your template logic into a model. So if the language is Japanese, you wanna put the last name first. And so we put that object for that logic in the model and that way if you have one model that represents a user, you might have views that don't necessarily need to do this and so you decouple your view logic from the pure data logic. So I talked a little bit about mobile constraints, kind of some pretty standard ones that we had to think about when building our component toolbox. Memory and CPU power was a big one. We ran into lots of issues with that. We had some crashing issues, that's kind of big. You also have less green space. And this can be a good thing or a bad thing depending on how you look at it. So it's nice because you don't have to have too many DOM objects available at once, but it also has some product concerns. And the other big thing is a request are way more expensive. So be very careful about how many requests you make and how big those requests are. We actually have started doing this thing where in order to speed up our page, what we'll do is we'll create, we'll talk about this earlier, we'll create a page bundle which is based on the page controller. And so what that is, is that's all the JavaScript you need for that one page. And where this comes in handy is, as I'm sure you all know, LinkedIn sends a lot of email. So, send out email, people will get the email and on their mobile device and they click the email. Right now, in a lot of cases, when you click the email, you have to load the entire site to view that one invitation or see that one profile. You have to download over half a meg of CSS and even more JavaScript just to see one little page. So what we're doing is we, on the first request, you just load just the JavaScript you need and just the CSS will sort of, we're actually working on the CSS, fancy problem. If you have any ideas on that, I can talk to you afterwards. But yeah, so that feeds up the site a lot. It means your requests are a lot smaller. Super helpful and it's really helping our balance rate because if you just want to see a connection, you don't have to wait like five seconds to see. So I want to talk about just the general concepts to be learned while refacting our app. Be a little more general here. Kind of like the overarching, one of the overarching principles we learned was that if you want your code base to be unified, you really have to be a control free. One small thing we did was we know our code base was it wasn't hinted. So we implemented JSON. Now, if you already have a humongous JavaScript code base, this was across the company, this is not just. You have a humongous code base, this can be super problematic. Like some did not go over all some people. I mean, you don't want to refactor like all your code for a hint, but the output was awesome. And that's all our code looked the same. And so it's not always going to go over well with everyone, it's usually worth it in the long run. And another way is really good for kind of keeping control over your code base is to set up extension. So people are gonna extend your code whether you like it or not. And if you don't have a set way for people to extend your code, they will extend it in whatever way they see fit, which can be problematic when you have a lot of developers. So there are lots of great ways to do the same thing. And we definitely want to encourage people to always have a conversation about the best way to do something, but we want that conversation to happen at the beginning before we actually build the app. So like I said earlier, we had like five different ways of authenticating a page, which is problematic. So a couple ways you can do that. We talked a lot about using mixins. I could go too much into that, but basically it's way better than multiple or having a huge inheritance tree. Really easy way to do multiple inheritance. We also had this issue where we had lots of member functions that really should have been just helpers, like static functions. So I'm not like a super big fan of the way back when it does static functions. So we just have these little AMD modules which are just object literals that have functions on them. And instead of attaching or mixing them into the class, we just add them as an AMD dependency and use them on the fly. So we have a lot of like little view helpers that just do basic DOM manipulation stuff in the same way over and over. Do be animation that's helpful. You know there are times of backbone extensions are on the web and like pretty much all of them that interact with views have like a pre and post render function and there's a reason for that because lots of people want to do things before or after you render. So simple functions like that are good to add wherever you think someone might extend your function or extend an operation. Well yeah, routing, rendering, two things everyone does, good to have those around. Another thing we learn is that big things are hard to control. So if you have monolithic anything, it's really hard to get that to do what you want from both a runtime perspective and from how you want things to develop perspective. So if you have something big, it's often hard to get the code the way you want it. We have people manipulating that huge monolithic thing so whatever it is. So one practical thing we have been doing is we focused on making small, unintrusive changes. And so I have some engineering friends that aren't in software engineering and they're in mechanical or civil engineering and they always talk about how software engineers can cheat because they can just merge and not have to worry about the consequences of what they're changing. Whereas if you're building a bridge, you can't necessarily just shut the bridge down to do any modifications. And so we kind of try to embrace this concept of trying not to shut down anything or merge or branch. So we're on a single branch system at LinkedIn so we don't do branches or feature branches or merges. We found that merging was super painful for huge projects and also like really slow. So if you're working on a code base where hundreds of people are looking at us, it's just too hard to keep track of all the branches. So one thing we had to do was modifying plates. So we have small feature flags. We try to make changes as incremental as possible and this is super helpful for making sure our goal doesn't go down. The other thing we do is we have, we call or we didn't come up with this but it's called a train release model. So your release schedule is like a train. So the train leaves the station every day and if you're ready, you hop on the train, you make, you push or commit. If you're not ready, you just wait for the next train. And so that way we know we're iterating every day, we're pushing out code every day. We can actually do it like four times a day. So it's really nice. Typically we don't do that, but if we need to, we can. And it also forces us to make sure we commit changes that don't print the bill because you probably have to push that day. So you don't wanna be that guy that holds up everyone else from leaving the train that day. Another thing we learned is the importance of context. So as an analogy, I worked at LinkedIn, if someone tried to describe me and said, oh hey, Aisa, he's the short Asian guy with black hair, that would not be very descriptive and they wouldn't be at a point, yeah. But if someone was like, oh, he's the guy on the mobile team, you know, he eats a lot, he sits in this area, you suddenly like shrunken down the context and it's way easier to describe something. So in the same way, we try to make our component context really small so that our function names have some meaning. So if you say this.u and you're in a huge component, that doesn't really say anything. Whereas if you're in a much smaller component, if you say this.u and they don't have only a few views, that's a lot easier to kind of figure out. The other big thing we learned is that your front end really has a back end and a front end. So in a sense, like our data store, we're doing all the caching and saving the models, and that's really like our back back end. And then our mid-tier is kind of our actual models which have lots of business logic in them. And then kind of our front end is like our view models and any of our views we have. And so it's very important to kind of realize that what you have showing you the user might not be the source of truth at that time. So you make some assumptions thinking everything is always just one flattened object to get run into problems. The other thing is that code is really a form of communication. This is super important. We've improved our documentation a lot, but we've come a long way. So when you don't have a lot of documentation, like written in a separate brief or not just code documentation, it's very important that your code is very concise and direct. So I think we talked a little bit earlier about the problems that you can have from data binding. And would you use data bonding in some areas? So we, you know, we bond models around and do some cool things. We found that's really useful in small contexts, but once you blow that up to several components, you're running into problems. So it's really hard to be concise and explicit when you have view bindings or model bindings floating around. We found that a delegation and actually directly calling functions is really helpful for readability sake. So I think some of you talked earlier about dependency injection earlier, the same it was. So we do a lot of work and passing in the parent object so that we can call the parent object directly instead of bumping it. Another really helpful thing was that we used a module pattern. Some people it's this kind of obvious, but you know, it took us a long time to actually get to the point where we could do this. You know, everybody agrees that module patterns are good. It's whether or not you wanna put the effort to refactor a system if you don't have it already. And so we actually converted our code base to AMD in a step-by-step process. So at one point we had only half of our modules in AMD and the other half were just global variables. And so it was a really interesting process to build a shim and a setup where you could do that. And it was really helpful because the process wasn't super stressful. So we just kind of, we built the shim and then we kind of converted as we went along. We weren't in a huge rush. We were able to do things slowly. One motto we have is think slowly. So we try to kind of keep the pace down and try to go things in a very small step-by-step way. Yeah, definitely worth it. Like I've already talked about, it's really just for testing. It's really good for decoupling. It makes you really think about your couplings. So we had a lot of circular dependencies for modules that we didn't really know about and a lot of really nasty, we had a really nasty dependency graph. And we had no way of knowing this until we actually used AMD. So it's super cool to see your code base on a dependency graph. I think your visual dependency graph is the first time. So I'll highly encourage that if you have not done that already. So I just want to kind of go over a few of Backo and friends that we use. So like I talked about earlier, we use Q for our promises. We also use Fiber for our inheritance. So Fiber does is if you're extending a class and you have to reference your parent object, you usually have to say base, model, or backo.model.prototype.ply. So that's not a big deal, but it'd be really nice to be able to say base.apply. So what Fiber does is it modifies your extended functions. So you actually pass it a function instead of an object literal. And that function has an argument, which is the base. So you can just say base.function.apply. And it's kind of, it's actually sugar, but it's super helpful. And it mixes in with Backo in really well. So I really recommend using it and also has some pretty nice mixing functions as well. So check that out. And we also use FastClick for a touch event. So if on a mobile device you tap, it'll actually wait 300 milliseconds before finding that event to make sure you are not doing a double click or a double tap rather. So that's cool if you're using double tap, but if you're not using double tap, well, we weren't, it just slows up your app and makes it feel kind of laggy. So we want to make our mobile web experience feels much like the native as we could. So FastClick is a cool way, it's a high-fill to do that. You also use Venus as our test runner. So Venus has an open source project, LinkedIn started, has some cool abilities so you can easily run your tests and different browsers on Selenium. Check that out, it's pretty cool. And like I said earlier, we use AMD for our module pattern. And we don't actually, AMD is really just a build step for us. We don't actually asynchronously load code except for one final bundle. And so we just use that to like build our dependency tree. The kind of basic gist of this is that you really have to pick your toolkit carefully and you definitely want to pick a single tool to do something. So no need to reinvent the wheel. We're really big on taking, finding open source projects that can solve problems that have already been solved. So just kind of to start to wrap up concepts that we learned are really helpful for being taking, taking control of your backbone repo. Like I said, have tools, single tools for common problems. Make sure your component toolbox as well as your library toolbox is, is what fits you. So again, you shouldn't have a problem of not knowing where something belongs. And then just the general concept that small is good and big is bad. So you want to try to keep things as small as possible as incremental as possible and move slowly. Then as far as keeping your code clean and doing things the same way, make sure that you have a set plan for how you want to extend your code. So make sure there's one way to extend something and make extension easy and documented that everybody knows how to expand on code. And use a module pattern. So pick something that works best for you. And actually enjoy carrying out the X-M6 modules. It seems really cool. Some people use require. We actually have a project called Inject we used to do in the episode that up. Yeah, and before I did, I want to give a special thanks to Ryan Blundin. He's here right now. Ryan, just stand up for a second. If you have other questions after the presentation, feel free to come talk to me or Ryan. So Ryan did a lot of help to help me put this together. He's been a big mentor of mine. And also a Jacob Huger, another LinkedIn guy. He's the name maintainer of Inject, super cool guy, definitely. He's been a talk to me if you have other questions as well. And yeah, so my Twitter handle, my GitHub, this presentation will be on GitHub later. This deck is actually a mini back bone app that kind of shows the concepts and the concepts I talked about. And so you can feel free to check that out and play around with it from a code standpoint as well as checking out with it. Yeah, so that's it. Use a queue. What does queue give you over just using jQuery to fit objects for example? So one of the reasons you use queue is at some points, this might be fixed by now, but jQuery had a couple of tasks failing and IE. So I don't know what the deal with that is. It might not be an issue anymore. For either reason, we picked queue was because we really liked how you handle debugging. So you can turn on long stack traces. And jQuery might have that right now. I'm not too familiar with it at the time. I don't think it did. So it's really helpful for debugging. And then also, we just kind of liked all the different helper methods queue has. So queue has a really cool method for, if you have a synchronous function and you want to easily return a promise, you don't have to like go through and like trade a fur. It's like one function you can return. That's basically just helper methods and the test for passing. Like yeah, I mean, we use, we actually use jQuery to fur as first in some of the other products and we like it. So it's definitely, I'm a good option. Oh, of course, does queue support pipe and all the other stuff in that part of this, this back, take plus five, promise me. Pipe. I'm not sure about what that does. If somebody's more familiar with that. Blake's not this back. Okay. That's not the same. Oh, one thing I forgot, man. I got some swags that you answered. Or if you asked a question, you get a t-shirt. I've got, if you want it, do you want a shirt? We got a large, extra large and a medium. So you can come back in by after and you've got your shirt. First guy answering the question, you too. I got four, so yeah. How do you test request limitation on mobile web? How do you know when there's too many requests? Too many requests. So I guess like too many is kind of objective. We try to make as basically as few requests as possibly possible. So we have one CSS request. Actually, I said two, it's not good, but. Okay, one very small number CSS request. We have two JavaScript requests. We have the initial JavaScript request for that page and then asynchronously load one more. So there's only one small place where I think where we do any kind of repeated requests. And I can't remember what the, the amount that like the frequency of that is. We didn't do any like super scientific thing besides just kind of look at the, like DevTools and saw what the response was. Somebody posted a really, some really good blog post that we talked about in the presentations about like why mobile is so slow. And there's that one I think is most more focused on CPU, but another one I can't remember where they were just talking about the disparity between a wireless network on a 4G network or on like a really slow network. And so I don't only have any good answer besides just as few as possible. And we generally found that we actually made like some slight feature changes to like rain in the amount of requests being made. So I think it's a good idea to try to push back on any product changes that might require that because the user experience is super important. And I think most users care that they can use the core features well and not have a huge set of features that are super slow. So that's the answer I have. Hope that helps. Next question gets the last t-shirt. Except all questions include how good will I look in this t-shirt. You mentioned earlier that you don't do any branching and merging. I was wondering if you could elaborate on that. So actually, yeah, so I lied. We have, we generally have two branches. So we have like when we were, when we went from like the 2011 version to the 2013 version, we had, you said two branches. We had like the old branch. We had like the new, what we were developing on. So I should say we only have one branch under active development at any one time. And so we don't do like feature branches. So we don't have like two guys go up and work on this branch and two guys go up and work on this branch. We just have one branch. So when you commit, and when you commit and push, you are changing whatever what else is changing. So, and we still have to merge sometime as far as, I should say we do not have to merge branches. You still have to merge sometime if you happen to like, when you're commit, happens to conflict. But we don't branch off the new features. Does that answer your question? Yeah. One more. Are you using Git internally? So the mobile team uses Git. We have a lot of legacy code that's still on SVN. But yeah, everyone wants Git. Well, how do you manage analytics in mobile space? Oh yeah, good question. So we have our own analytics setup. So I was talking a little about page controllers and this page controller would help us. So we fire metrics, we're gonna have you click a button to go to page and we fire another metric when you actually hit that page. You know, big brother is definitely watching when you use our app. So every tap, swipe, all that, you know, about. So yeah, we have a, we actually, we did a lot of refactoring for this. So it was like metrics is probably the biggest pain in the rear for us right now as far as developing. So we have a really custom setup and we handle that multiple ways, which, you know, the thing I've talked about is really good to do that one way. And so we have some situations where it's in the markup. So you have a button you tap. There's a, in the markup, you specify some kind of metric and sometimes that's handled. We really extended the crap out of view. So all the view handlers actually automatically handle that for you or most of them do. So any view call back, it'll, under the hood, it'll fire a metric for that. So yeah, you have your own custom setup. If you make your own custom setup, definitely work a lot on the API and make sure there's like one way to do things. And I think it also helps if you have, you build your metric system, at least when you have an idea of how your app will work. Because we added some features after, we added some like, some different control flow mechanisms after we did our metric system. And so we kind of had to hack on stuff after. And let's do it one more time for you, Sarah.