Rebuilding Tumblr as a Single Page App
Griffiths, Oli
Following a hack day project, work began on building prototypes of Tumblr using Ember and React. Come and find out what we learned along the way and why we chose Ember to ship all our memes.
 Hello, everyone. Hopefully you've had time with a pee. Do whatever you need to do, and we're good to go for the next session. Hi, my name is Oli Griffiths. I don't know how to use this remote, so let's do that. There we go. Yes, this is me. I am a software engineer, and I don't do JavaScript. I'm actually a PHP engineer. I work at Tumblr. Shout out to my PHP folks over there. Yeah, there's not many in here. Yeah, I work at Tumblr. I've been there for a couple of years now, and moved to the US four years ago. I'm obviously not American or Australian, as I usually get. Yes, so I work at Tumblr. I work on the backend team there. And what is Tumblr? Who here has used Tumblr or knows what it is? All right, that's pretty good. Usually I don't get that. Most people are like, yeah, what is it, like a blogging thing? I don't really know. So this is Tumblr. It's a community-driven application full of all sorts of weird and wonderful things that exist on the Internet. It's really centered around communities. It's there to fill the gap between the personalness of Facebook, the public presence of Twitter, and the take a photo of everything I eat land of Instagram. So this is some of the stuff that you can get on there. Whether you're into comics, computers, home improvements, supercars, sports, even food porn, there's pretty much everything on Tumblr for you. Go check it out. So Tumblr is a place to be yourself. So cool, that's that spiel out of the way. Let's get on to the cool stuff. Engineering. So, Tumblr, we deal on a massive scale. We've got roughly 147 billion blog posts, 341 million blogs. It quits about 35 million posts a day, and about 3 million cash hits a second. This is like, it's pretty big data. But along with big data and big infrastructure comes a set of pitfalls. We have a fairly high latency outside of the US. We are hosted in the US. It's a US brand born out of New York. We have multiple code bases to support. It's a 10-year-old application, and with 10 years comes a fair amount of legacy code base. And coming with that is an inability to adopt new technologies. It makes it difficult to be agile, and we kind of struggle with our web app to provide native app-like functionality. It becomes difficult. So this is our stack right now. We are primarily in the front end, backbone and jQuery-based. We have a custom MVC stack that uses backbone. We have a three to four-minute build time, which is super fun if you're trying to build any of our legacy assets. You can literally go and get a cup of tea while that's happening. And I'll do that now. And we have some custom data bootstrapping. So, what am I here to talk to you about? Well, at Tumblr, we have a thing called Hack Days. And if your company doesn't do Hack Days, you should absolutely give them a try. They're freaking awesome. We love them at Tumblr. They drive innovation, and you can basically build whatever you like. You have 24 hours to build anything. One year, somebody built an interactive mirror. Nothing to do with Tumblr, but it was cool, and it worked. Yeah, so you have 24 hours, you can build anything. We have prizes and raffles and games. At Tumblr, we love them. But this is me. My first Hack Day. All bright-eyed and bushy-tailed. I just started at Tumblr, and I thought, okay, what am I going to do? Tumblr has an API. We have a website. We have apps that are powered off the API. We have a different website that's not powered off the API. And it's a completely different code path. So why don't I try building Tumblr as a single-page app? So I decided to have a go with EmberJS. I'd had a tiny little bit of experience with it before, but it was a good, well, it says on the website, it's a framework for ambitious web apps. Tumblr is a web app, so why not give it a go? And this is me. 24 hours in. Burning in copied and pasted HTML and CSS directly into this brand-flashy new application. And it turns out that just copying and pasting your HTML from your current app into a new one is a bad idea. Don't do that. But luckily, Tumblr has a facility for us to be able to take Hack Days beyond a single 24-hour period. We call them labs. So labs allow us a half a day a week to work on a side project outside of our daily roles at Tumblr. And it's encouraged to bootstrap projects that are outside of the, you know, regular product roadmap. So I decided, let's start a single-page app lab. And this started in October 2015. So half a day a week, not very long. It started October 2015. And I'm going to preface this by saying, the only thing that you're about to see is still a lab. It's still a complete prototype, but it's proving a point. So this is the Ragtag bunch of guys that started this project with me. They are sitting somewhere over there, fellow Tumblers. And we decided the best thing that we should do is to see if we could build a native comparable mobile web app. And what exactly is required in order to be able to do so? So the key point here is making sure that the application that we produce is as close to a native application experience as possible. And you shouldn't leave your web users behind. Yes, provide them with a native app if you so wish to, but don't neglect your web experience just for the sake of it. So we decided we wanted to make a first-class web experience. So this was October 2015. There were really two major candidates at the time. We had Ember, we had React. And we put them head-to-head against one another. We decided these are the two major frameworks that are going to be able to solve this problem for us. And we decided to build a simple prototype in both. The basic idea of the prototype was to fetch data from the API and render a list of posts. Nothing fancy, just like title, ID, that was it. And what would we need to go through in order to get to that stage? Like from step one, nothing like npm install to that point, what do we have to go through? Can anybody name these two characters, by the way? Yes, and? Yes, excellent. Now, I don't know if anybody noticed, but Tumblr has this really weird obsession with Guy Fury. And you'll notice he's right there. Can't get away. So we started. We started down this road, and this is where it started. We jump in head-first into the world of single-page apps. And this means everything from understanding Ember's build system to understanding how to do the same thing in React. We wanted to build a similar prototype in both. So it kind of felt a little bit like Mr. Krabs here, with the fast pace of JavaScript. It's kind of overwhelming. There's a lot of things to learn. But we decided, let's do it, feet-first, jump straight in. We noticed that with Ember, we were able to get up and running and be productive from day one. React was a little more difficult. If anybody here has tried to configure Webpack to build React, pre-react create app, which is a godsend, but it didn't exist in October 2015. And this literally took us days to get just to the point at which you could do, like, Ember new, and have a full set up build environment, tests, and everything. So it was difficult. And because of that, we also looked into the backing data stores that were available for React. And at the time, Redux was still in fairly early stages of adoption. It's kind of difficult to get started with. And Ember for us just worked out of the box. It was simple, Ember new, off we go. And in the first day, we had our list of posts being rendered straightaway. So eventually, we ended up here. We're aboard the Nebuchadnezzar with immense power beneath our feet. We knew where we were going, we knew what we wanted to do, and we knew that Ember was the tool to do that for us. Ember also has one major, major advantage over React, and I cannot stress this enough. And that'd be add-ons. Add-ons provide so much extra for the React ecosystem that the React, unfortunately, doesn't have right now. NPM is not a replacement for add-ons. It allows you to add into your application through build pipelines, through providing, essentially, full applications as an add-on. So add-ons really, really allow you to turbocharge your Ember application. Yeah, right? Portons cool, by the way. I've got another cool slide later. So creating a native comparable mobile web app. What is actually involved in that? What things do you actually have to do if you want to build something that is as good as a native app? Well, first off, you have offline support, and that was touched on earlier. For a native app experience, you need to have the app boot immediately. It needs to load off the device. You cannot be waiting for round trip times to your data center to go and fetch your HTML, assets, and so on. So you really should have the app being bootable off the device. No HTTP requests. Offline is absolutely paramount. Animations. Users have become accustomed to smooth 60 frames a second animations, and they provide this sort of skeumorphic experience to the user where you've got, like, bounces and easing and so on that feels good when you use them. And I remember when iOS 7 was released, and they added all the gesture support and animations and all that kind of stuff, and it made all the apps feel good. The web, lots of good. There are some solutions to these, but out of the box, it has the tools, but there's no out-of-the-box SDKs that are going to make it nice and buttery smooth for you. Multirin reusable. This is something that native apps kind of get out of the box. You have a UI kit in iOS where you're able to build reusable views that you can drop in anywhere throughout your application. So if you render a post in a list of posts on one screen, that you use that same view somewhere else in your application, and it looks the same, users, like, they expect consistency throughout your application, and for it being modular and reusable within the application itself, so we're talking about components and things here, that's really, really important. Push notifications. We finally got support for this a few years ago, but prior to that, it was like, you should have a look, I'm afraid. Push notifications were something only the native apps had. We can do this now, and users are expecting this. They expect to be able to have push notifications. Popovers are a really, really interesting thing. Popovers provide in-band context information to your application. So if anybody here has ever used an iPad, which I'm sure everybody has, you'll notice that on the iPad, you can tap on a button and you can see a popover in line, because you have much more screen real estate. Well, on a mobile device, you don't have that screen real estate, you're transitioning to another page. The difference with web is that you're really not limited to the screen size, the user could start out small and then it could get big. So popovers actually become really, really challenging. Touch and gestures. Another really big thing that was introduced with iOS 7, and that everybody has become used to now, is you can swipe to go back and you can do native scrolling. Touch and gesture support is something that the native frameworks provide, and they provide pretty good SDKs for them. The web, again, is slightly more difficult. It's all possible, but none of this is easy. And the last thing is home screen apps. Users expect to be able to have your icon for your app on your home screen. I mean, why wouldn't they? They want to be able to quickly access your app, and you want users to be able to quickly access your app, and use it as if it were a native app. It's interesting because studies show that users don't know all care the difference between a native and a web app. Realistically, what is the difference? From the user's point of view, they're clicking on an icon, on their home screen, and up pops your app. Whether they get that through a web URL, whether they get that through an app store, it doesn't make any difference. And fake news, I made that up. But the sentiment is true. It really doesn't matter where your application comes from. Users expect the same level of functionality from a web app versus a native app, but they honestly don't know or care the difference. If I ask my mom, for example, and sorry, mom, I'm going to throw you under a bus a little bit here, to describe the difference between a web app and a native app, she'll say that, well, the web app, I get to through a web browser and the native app, I install through an app store, or I click on a button on my home screen. And I'm like, why? Do users even care? Well, I'm pretty sure if they have to download an ATMEG app, just to get back to the same place that they were on your website, and they're doing that over a 3G connection, I'm pretty sure they care. So why are these things important? Well, users become to expect a certain level of polish in your application. And right now, web apps don't really live up to that same expectation. So how can we address that? Well, I'm going to show you some of the things that we have used throughout our journey with Ember. So add-ons. As I mentioned earlier, add-ons really do provide you this extra level of flexibility with your application to be able to use pre-existing made components and utilities and helpers to be able to make your application perform better. So these are the add-ons that we used within our application. We've got everything here from animation support to concurrency. We've got order prefixes, so you don't have to keep using your browser prefixes. It will automatically do those things for you. And these are all community add-ons. These are things that others have made that have made our lives easier. We haven't had to spend the time reinventing that wheel. So the first one is offline support. And we're going to go back to school for a bit. We're going to do some maths. So the speed of light. The speed of light in glass is 2 thirds of the speed of light in a vacuum. So you've got, what's that, 200 million metres per second. The distance from the US to Australia is roughly 15,000 kilometres. So speed equals distance over time if you take yourself back to your physics classes. Therefore, time equals distance over speed. So if you're in Australia, the round, the single trip from Australia to the US, 75 milliseconds. And obviously it's round trip, 150 milliseconds. You've got 150 milliseconds of nothing. And that's working on the best case scenario that all of the routers and switches between Australia and America are instantly fast, which they're not. Verizon actually publishes statistics on this and they published 152 milliseconds, so I think my maths is right. So offline support, this was touched on earlier. Super, super important. We're using AppCache in here because AppCache has better support across browsers. It's not as clever as ServiceWorker. You really need to do a sort of two-pronged approach there. But you can see here that the app is being put into offline mode and it still works. You can navigate between different pages. Obviously things like authentication do not work. You have to hit your server for that. And this is what it looks like. You've got a manifest here, you basically just list, these are all the files. Hey, browser, go, store all of these offline and the browser will tell you when that's happened. And you can then decide what you want to do when that's happened. You can present the user with a notification saying, hey, how about you add this app to your home screen now that you've downloaded all the assets? So AppCache, great support, ServiceWorker, not great support. It will happen. It'll be amazing. It'll be great. Animations. Animations are a huge part of your application. So we're using an add-on here called Ember LiquidFire. And what LiquidFire provides us is a literal drop-in replacement for your standard outlet tag. And it provides you animations out of the box. You have to do almost nothing to actually support it. It's incredible. And as you can see here, we're logging in. We click Login. We'll get a nice little slide animation. Hey, that looks like a native app. Cool. So this is what it looks like. You swap out your, let me use the laser. Oh, yeah. You swap out your regular outlet with a liquid outlet. And you right here configure a transition. And we're saying from the index route to the login route, we want to use a crossfade. And we want to use that in the reverse. So if the user clicks forward or back in their browser, they get the reverse. For those of you on this side, we're going to do a transition from the login route to the dashboard. And we're going to use the two lefts, like the iOS style UI navigation controller. Transition. Next up, authentication. Anybody here who has an application that has users has to deal with authentication. So how do we do that? Well, we used an application or an add-on called Embersimple-auth. These lasers are cool. Embersimple-auth, it provides you with scaffolding to handle your application's authentication and session management. It provides the basic hooks to be able to authenticate with the API. It provides the ability to automatically attach outgoing headers so you can attach like a bearer token for your Oauth2 header. You can automatically sign outgoing requests for things like basic auth and so on. And it also provides session management such that if the user logs out in one tab, they're logged out in all tabs. Now, this isn't something that native apps have to deal with. Like, if you log out in a native app, you're logged out. In a web app, the user can have as many tabs open as they want. And you want them to be logged out in every single tab. Being logged in in one, when you've logged out in another, that would be bad. Imagine if you were at a web cafe and you'd forgotten to close one of the tabs after you've logged out and now someone has access to all your data. Well, what about that? Web components. So off the back of Glimmer, we've had this promise of web components for some time. And web components is a W3C spec that's still in the process of being finalized, I believe. And as yet, we don't have access to them. But we can get very, very close. So we used an add-on called EmmaComponentCSS. And this is a fantastic add-on. Eric Bryn is somewhere in the audience here. Shout out to him. Thank you so much. This has made our lives a million times better when it comes to dealing with CSS. Anybody who's had to write CSS will know they've accidentally styled an element that they didn't intend on styling because CSS is cascading style sheet. And it cascades down through your browser. So accidentally targeting something else in the browser. I'm sorry, that's a feature, not a bug. This is what it looks like. So you have your styles CSS in your component. And the add-on will automatically take those styles and also generate a class name. And it prefixes all your styles with that class name. It also adds a class name to your component HTML that's output in the browser. And you'll notice that the class name here matches the class name in the DOM. Now, what this means is it's literally impossible to leak a style outside of a component. And this is awesome for that modularity that I was talking about earlier. The ability to be able to reuse components throughout your application and no guaranteed wherever you put this, it's not going to be affected by anyone else's CSS. Of course, you can still affect child components, which is why we used the descendant selector. Which is why I don't know how to use this. Yeah, you'll notice that we're using the descendant selector here. So that in the CSS that's generated, we know we're targeting the specific elements that are in our component itself and not accidentally leaking anything further down to any components that are used within our component. And this is what it looks like. So it looks pretty much like that. You can see that. We've probably got the idea from the previous slide, but you can see there that the component name that's generated there is the same as the component name that's generated there. Alert some dialogues. So users are accustomed to your application providing alerts and dialogues either natively or within the application itself. So we used an Ember add-on called Ember CLI Flash, and this is what it looks like. As you can see, error. Thing did not work. We also color the header bar here up here, the toolbar, with the Ember computer property that just changes the style there. You'll notice that when this one disappears, that goes green. Pretty cool. And you can have sticky messages, which is pretty awesome. So you can stay up there till the user dismisses it. User dismissed it. We also extended this add-on to add dialogue support. So you can automatically add dialogue messages, of course, to your application. This is in line with our standard branding. Our native apps do this. Our web app does this as well. And it provides a pretty simple way of providing flash messages to the user. So we used this add-on, Ember CLI Flash. Shout out to Lauren Tan wherever she is. Great work on this. This has really saved us a lot of time with providing a centralized way of managing flash messages. So if I go through this very quickly, you inject your service, the flash message service. You grab the service and you push a message onto it. So this is like a notice. This can be anywhere in your application. Let's say, for example, you have some offline notification message. We then built a component which outputs the flash messages that are on the queue. And the flash message system itself handles all the expiry of the messages, cleaning them up, making them disappear, and so on. Really, really awesome. It just worked out of the box where we had to do a styler, which is pretty cool. Concurrency. There's a talk on this, I think, tomorrow by Alex. Concurrency is a fun thing. If you've ever done a callback with an Ember component, you will know you get into problems if that component is removed from the DOM. So Ember Concurrency helps with that. So here we've got a like button. And you can see, I'm clicking the like button, and you can see on the right-hand side here the status messages that I just hacked in there. And you can notice that every now and then we've got a bunch of grouped-up messages here. So every time we click this, we're outputting a console log, and then from the action that's triggered, we're also doing a console log. And you'll notice that although you can't see it, I'm clicking this button curiously right now. And outputting here, we've got multiple stacked messages because it's not doing anything. The actual main action itself that's doing the HTTP request, only one of them is happening per full run of this button. So that timeout for that animation to happen and then disappear, we only allow one action to actually happen whilst that's running. Let me show you what that looks like. So this might be, hopefully this is easy enough to see. We import task and we import timeout from Ember concurrency. And we define a property on our component. And what this does is we call the task function and we provide a JavaScript generator. From here, I'm just calling the action data down, actions up. This is good. So our action is going to be called on this like button, and that will be handled further up the stack. And then we're yielding a timeout here. So what this does is this yield and this timeout is actually yielding a promise. And the task system in Ember concurrency is expecting to receive a promise, return from it, and it will pause that, it will pause this function whilst that promise is running. So timeout is just a really simple wrapper around set timeout that returns a promise. And notice we've got this drop keyword on the end here. There are various different ways to configure this and we can figure it in the drop method. Basically what this does is every time this action is tried to be fired, if there's one already running, it'll just drop the existing ones. You can do other things like stack them up, you can cancel existing ones and so on, but this is the functionality that we wanted. And then from our click handler, we just call a form on the task itself there. Super simple. And it means we don't end up in this callback hell nested callbacks or chained then statements. It makes our code very, very declarative and very easy to follow along. So next up, popovers. As I mentioned, popovers are really, really difficult with responsive applications. You have no way of knowing realistically whether or not you should be transitioning to a new route and showing a different screen or whether you should be showing the context information as a popover. So we actually built an in repo add-on to handle this for us and this is what it looks like. So we've got our account menu up here. You click on that, we show a popover and we have logic in the popover to make sure it stays on the screen and doesn't overflow the edges and so on. And if you change the size of the viewport and you go down to mobile size, this now actually does a route transition instead of doing a popover. And it turns out this is actually a lot more complicated than it needs to be. Doing this in a nice reusable way has meant a little bit of hacking, but we're using mostly public APIs to do it, so it actually works really, really well. And I don't actually have a code example of this, but if anybody wants to see it, please come and give me a shout afterwards. And you basically invoke this in the same way that you do a link to. We created a link to popover helper and that basically wraps around the popover functionality and it automatically pulls your model from your route hook. So all you have to do is define what is the route, what is the component that I want to render within the popover itself, and that's it. It hooks up everything else for you. So if you grab your model, it will inject it into the component as if by magic. API actions. So who here has used an API that is not a standard REST JSON API? I'm expecting to see every single person's hand up in here. I call these restless APIs. It's all well and groovy if you have the perfect RESTful JSON API and Embedator is like, cool, bro, I got you back. If you don't, it's not so easy. So our application has some restless endpoints, namely like. That like action you saw earlier is sending an HTTP request to user slash like, you can see it happening here, and user slash unlike. And Embedator doesn't handle this very well at all. In fact, it doesn't even cover this in any way you are on your own. However, Embedator handles like getting the URLs to send HTTP requests to. There is some like crossover there, but it just doesn't handle the non-RESTful semantics. So we built an add-on to handle this for us. We call it Embedator actions. It's currently in repo. I'm hoping to open source it. But this is what it looks like, and it's super, super simple. So, oh, no. So here, I haven't imported it up here, so you can't see that. But basically you call action. So you define a property on your model, call action, pass it the action that you want to run, and you pass it the property that you want to toggle or change when the action has returned true. Same in the inverse, unlike, unlike, and we're going to change like back to false. Then in your adapter, you define a set of actions, and here I'm just defining the like action, and I'm going to say when this action is fired, hit up user slash like, and you're going to take the model keys, ID and reblog key. These are Tumblr-specific things, and those will automatically go in the post body. You can configure whether it's a post request, a put request, a delete request, whatever you like, and those things automatically get added into the body. Then in your component, the API is super, super simple. Grab the post, call like. Seems simple, right? This is really, really difficult with Embedator out of the box. So we built an add-on for it, and we did it as an in repo add-on. In repo add-ons are super cool. You should give them a try. This is what this intax looked like to generate one. You call ember g in repo add-on, and you call give it a name. Then when you want to generate something within that component, you want to say create an add-on, sorry, create a component in that add-on. You just add the dash dash in repo add-on and give the name of the add-on that you're generating the component into, and ember will automatically generate you a structure that looks something like this. This is our Embedator add-ons. Very, very simple. We've got our mix-in for our adapter, and we've got a utility that we use in the model, that action callback that I showed you. Really simple way of prototyping out something that you think, maybe I might want to outsource this in the future. The last thing is living style guides. Now, living style guides has been really, really useful for us, and we're using an add-on called ember freestyle. As you can see here, this looks like what you saw earlier. We've got all our different add-ons. It shows you the invocation below it, you see right there. This is actually rendering what is in here. You basically wrap this in a special semantics for this add-on, and it will render that add-on in that block, and it will show you the invocation at the bottom here. This is really useful for both developers and for designers, because it allows them to separate those concerns. Designers can decide, well, actually, you know what, I don't want to see all the code, I don't really care about that. They can toggle that on and off. Developers can look at this, and it basically provides you a certain level of API documentation for your components. You can see right here, it lists out all the different permutations of a component that you might have. And bam! You can happily manage change management, make sure things don't break. And as you can see, it looks pretty snazzy. So we still have plenty more to do. And one thing that I really want to get across here is the use of add-ons allows you to do one great thing. It allows you to stop reinventing the wheel. Unless, of course, you're in the business of wheel making, then don't go ahead and reinvent the wheel. Build an add-on. Use an add-on. At the very least, if you have a concern in your application, it probably applies to someone else as well. So consider making an add-on. Start it off as an in repo add-on and move on from there. So things are not perfect right now in the Ember ecosystem. Ember data is awesome if your API is a perfect, restful JSON API. Rocket.js. It's super powerful. The documentation isn't great. Ember works out of the box. But sometimes you don't want the whole box. And mobile performance for us in this application is absolutely paramount for 2017. And I know it's something that is the forefront of Tom's mind right now, making sure that Ember really is a first-class citizen when it comes to performance. So the question is, how do we get from here to here? Well, I was just trolling around on Twitter and I found this guy. And he said this, as a tech community, we must treat documentation, marketing, logistics, infrastructure, art, etc. work with as much respect as engineering. And this couldn't be any more true. We have a responsibility as consumers of this application to give back. Now that doesn't mean you have to develop an add-on. It could be that it took you weeks to understand what serializers are and what adapters are in Ember data. And then finally, you read a blog post on it and you're like, oh my God, I get it. Submit a pull request to the guides repo because it's not that clear. Another thing, when you're looking at, this is really interesting, and I probably should have done this really prior to this because I keep my own dog food, but I'm not sure if you're going to be able to do this. Returning something from a model hook, when you look at how that stuff works in Ember, it's just assumed that you're using Ember data. Maybe you're not. It doesn't actually say on the first model, the guides for the model hook that you can just return a promise. Or if it is there, it's very, very buried. And these are the kind of things that stop other developers from being able to get involved in the Ember ecosystem because some people want to persist and really dig into the internals and find out how stuff works and some people don't. For those that don't, that's totally okay. No one's expecting you to do so. But if you know the answer, just submit a pull request, make things better. Another big thing we need to be doing is we need to break up the monolith. And I was really unsure about this slide because it's kind of weird and it creeps me out. But for those of you that haven't read it, there's an RFC. It's up here. It's number 176. And you can get to it, github.com. About breaking up the Ember monolith application. And glimmer is going a huge way to accomplishing this. There's a whole bunch of other things within Ember itself that if you don't need all of the box, don't ship all of the box. And that's imperative not just from a shipping the bytes down the wire, but from the bigger the payload, the longer it takes. The larger the payload, the more JavaScript there is to pass and evaluate and the slower your boot up time is going to be. It also means that we can really start competing in the framework wars as not shipping a monolith. I think people nowadays in the JavaScript community are used to being able to receive small packages. So that's what we want to be able to do. So what did we learn? Well, it turns out making web apps is hard. But making native comparable web apps is even harder. There are so many things that you have to take account of these days. We don't have the luxury of native applications where they come with these lovely APIs and SDKs like iOS that comes with UI kit and all the animation libraries. And we don't have that. We have the DOM. If we're lucky, maybe somebody is abstracted into a library that we can use. This is where add-ons come in. We are providing abstractions on top of this to make these APIs accessible and consumable by other developers like yourself. You can't simply just get by with jQuery anymore. These are some of the concerns that you have to deal with if you're building a mobile web application that you want to be as good as native. There's a lot of things here and I've covered some of them, but there's a whole bunch of other things, even outside of this slide as well, that you have to be aware of. Like security is a huge thing. So things like sub-resource integrity, go check that out. It's supported in Ember out of the box. Cool. Things like cross-site scripting. You want to have 60 frames per second animations. You want to make sure that you have code splitting, which we hopefully will do sometime in the future. You want to have a deployment strategy. You need a build environment. You want to potentially have offline data and use it like index DB. You want to have async bundles so you can split your application down into smaller pieces and only ship the user what they need. So many things that you have to deal with. Don't try and reinvent the wheel. So I'm going to leave you with a few things here. Number one, stand on the shoulders of giants. You can do it by using a framework like Ember. You don't need to reinvent the wheel. You can use the collective knowledge of the community and we can all benefit together. We can do great things. If we work together, build add-ons, as the core team said earlier, as Tom and you had us said, they're going to be focusing on lower-level primitives, so we'll really allow add-on developers to bootstrap and build out functionality on top of Ember for the entire community going forward. So let's say check out emberadons.com. It's a great resource for the latest and greatest add-ons that are on the market. And I want to take this opportunity to give a shout-out to Tom and Yehuda and the entire Ember team and the core teams for building an incredible framework that we've been able to build our applications on top of. So I think at this point I'm just going to give everybody a round of applause for the Ember core teams. So thank you. This is me. If you want to hit me up, please do so. If anybody wants to have a look at the application that we've built, I'm more than happy to demo it. Thanks very much. Enjoy your break.
 Hello, everyone.  Hopefully you've had time with a pee.  Do whatever you need to do, and we're good to go for the next session.  Hi, my name is Oli Griffiths.  I don't know how to use this remote, so let's do that.  There we go. Yes, this is me.  I am a software engineer, and I don't do JavaScript.  I'm actually a PHP engineer.  I work at Tumblr.  Shout out to my PHP folks over there.  Yeah, there's not many in here.  Yeah, I work at Tumblr.  I've been there for a couple of years now,  and moved to the US four years ago.  I'm obviously not American or Australian, as I usually get.  Yes, so I work at Tumblr.  I work on the backend team there.  And what is Tumblr?  Who here has used Tumblr or knows what it is?  All right, that's pretty good.  Usually I don't get that.  Most people are like, yeah, what is it, like a blogging thing?  I don't really know.  So this is Tumblr.  It's a community-driven application full of all sorts of weird and wonderful things  that exist on the Internet.  It's really centered around communities.  It's there to fill the gap between the personalness of Facebook,  the public presence of Twitter,  and the take a photo of everything I eat land of Instagram.  So this is some of the stuff that you can get on there.  Whether you're into comics, computers, home improvements,  supercars, sports, even food porn,  there's pretty much everything on Tumblr for you.  Go check it out.  So Tumblr is a place to be yourself.  So cool, that's that spiel out of the way.  Let's get on to the cool stuff.  Engineering.  So, Tumblr, we deal on a massive scale.  We've got roughly 147 billion blog posts,  341 million blogs.  It quits about 35 million posts a day,  and about 3 million cash hits a second.  This is like, it's pretty big data.  But along with big data and big infrastructure comes a set of pitfalls.  We have a fairly high latency outside of the US.  We are hosted in the US. It's a US brand born out of New York.  We have multiple code bases to support.  It's a 10-year-old application,  and with 10 years comes a fair amount of legacy code base.  And coming with that is an inability to adopt new technologies.  It makes it difficult to be agile,  and we kind of struggle with our web app  to provide native app-like functionality.  It becomes difficult.  So this is our stack right now.  We are primarily in the front end, backbone and jQuery-based.  We have a custom MVC stack that uses backbone.  We have a three to four-minute build time,  which is super fun if you're trying to build any of our legacy assets.  You can literally go and get a cup of tea while that's happening.  And I'll do that now.  And we have some custom data bootstrapping.  So, what am I here to talk to you about?  Well, at Tumblr, we have a thing called Hack Days.  And if your company doesn't do Hack Days,  you should absolutely give them a try.  They're freaking awesome.  We love them at Tumblr.  They drive innovation, and you can basically build whatever you like.  You have 24 hours to build anything.  One year, somebody built an interactive mirror.  Nothing to do with Tumblr, but it was cool, and it worked.  Yeah, so you have 24 hours, you can build anything.  We have prizes and raffles and games.  At Tumblr, we love them.  But this is me.  My first Hack Day.  All bright-eyed and bushy-tailed.  I just started at Tumblr, and I thought, okay, what am I going to do?  Tumblr has an API.  We have a website.  We have apps that are powered off the API.  We have a different website that's not powered off the API.  And it's a completely different code path.  So why don't I try building Tumblr as a single-page app?  So I decided to have a go with EmberJS.  I'd had a tiny little bit of experience with it before,  but it was a good, well, it says on the website,  it's a framework for ambitious web apps.  Tumblr is a web app, so why not give it a go?  And this is me.  24 hours in.  Burning in copied and pasted HTML and CSS  directly into this brand-flashy new application.  And it turns out that just copying and pasting your HTML  from your current app into a new one is a bad idea.  Don't do that.  But luckily, Tumblr has a facility for us to be able to take Hack Days  beyond a single 24-hour period.  We call them labs.  So labs allow us a half a day a week to work on a side project  outside of our daily roles at Tumblr.  And it's encouraged to bootstrap projects that are outside of the,  you know, regular product roadmap.  So I decided, let's start a single-page app lab.  And this started in October 2015.  So half a day a week, not very long.  It started October 2015.  And I'm going to preface this by saying,  the only thing that you're about to see is still a lab.  It's still a complete prototype, but it's proving a point.  So this is the Ragtag bunch of guys that started this project with me.  They are sitting somewhere over there, fellow Tumblers.  And we decided the best thing that we should do  is to see if we could build a native comparable mobile web app.  And what exactly is required in order to be able to do so?  So the key point here is making sure that the application  that we produce is as close to a native application experience  as possible.  And you shouldn't leave your web users behind.  Yes, provide them with a native app if you so wish to,  but don't neglect your web experience just for the sake of it.  So we decided we wanted to make a first-class web experience.  So this was October 2015.  There were really two major candidates at the time.  We had Ember, we had React.  And we put them head-to-head against one another.  We decided these are the two major frameworks  that are going to be able to solve this problem for us.  And we decided to build a simple prototype in both.  The basic idea of the prototype was to fetch data from the API  and render a list of posts.  Nothing fancy, just like title, ID, that was it.  And what would we need to go through in order to get to that stage?  Like from step one, nothing like npm install to that point,  what do we have to go through?  Can anybody name these two characters, by the way?  Yes, and?  Yes, excellent.  Now, I don't know if anybody noticed,  but Tumblr has this really weird obsession with Guy Fury.  And you'll notice he's right there.  Can't get away.  So we started.  We started down this road, and this is where it started.  We jump in head-first into the world of single-page apps.  And this means everything from understanding Ember's build system  to understanding how to do the same thing in React.  We wanted to build a similar prototype in both.  So it kind of felt a little bit like Mr. Krabs here,  with the fast pace of JavaScript.  It's kind of overwhelming.  There's a lot of things to learn.  But we decided, let's do it, feet-first, jump straight in.  We noticed that with Ember, we were able to get up and running  and be productive from day one.  React was a little more difficult.  If anybody here has tried to configure Webpack to build React,  pre-react create app, which is a godsend,  but it didn't exist in October 2015.  And this literally took us days to get just to the point  at which you could do, like, Ember new,  and have a full set up build environment, tests, and everything.  So it was difficult.  And because of that, we also looked into the backing data stores  that were available for React.  And at the time, Redux was still in fairly early stages of adoption.  It's kind of difficult to get started with.  And Ember for us just worked out of the box.  It was simple, Ember new, off we go.  And in the first day, we had our list of posts being rendered straightaway.  So eventually, we ended up here.  We're aboard the Nebuchadnezzar with immense power beneath our feet.  We knew where we were going, we knew what we wanted to do,  and we knew that Ember was the tool to do that for us.  Ember also has one major, major advantage over React,  and I cannot stress this enough.  And that'd be add-ons.  Add-ons provide so much extra for the React ecosystem  that the React, unfortunately, doesn't have right now.  NPM is not a replacement for add-ons.  It allows you to add into your application through build pipelines,  through providing, essentially, full applications as an add-on.  So add-ons really, really allow you to turbocharge your Ember application.  Yeah, right?  Portons cool, by the way.  I've got another cool slide later.  So creating a native comparable mobile web app.  What is actually involved in that?  What things do you actually have to do if you want to build something  that is as good as a native app?  Well, first off, you have offline support, and that was touched on earlier.  For a native app experience, you need to have the app boot immediately.  It needs to load off the device.  You cannot be waiting for round trip times to your data center  to go and fetch your HTML, assets, and so on.  So you really should have the app being bootable off the device.  No HTTP requests.  Offline is absolutely paramount.  Animations.  Users have become accustomed to smooth 60 frames a second animations,  and they provide this sort of skeumorphic experience to the user  where you've got, like, bounces and easing and so on  that feels good when you use them.  And I remember when iOS 7 was released,  and they added all the gesture support and animations and all that kind of stuff,  and it made all the apps feel good.  The web, lots of good.  There are some solutions to these, but out of the box, it has the tools,  but there's no out-of-the-box SDKs that are going to make it nice and buttery smooth for you.  Multirin reusable.  This is something that native apps kind of get out of the box.  You have a UI kit in iOS where you're able to build reusable views  that you can drop in anywhere throughout your application.  So if you render a post in a list of posts on one screen,  that you use that same view somewhere else in your application,  and it looks the same, users, like, they expect consistency throughout your application,  and for it being modular and reusable within the application itself,  so we're talking about components and things here,  that's really, really important.  Push notifications.  We finally got support for this a few years ago,  but prior to that, it was like, you should have a look, I'm afraid.  Push notifications were something only the native apps had.  We can do this now, and users are expecting this.  They expect to be able to have push notifications.  Popovers are a really, really interesting thing.  Popovers provide in-band context information to your application.  So if anybody here has ever used an iPad, which I'm sure everybody has,  you'll notice that on the iPad, you can tap on a button  and you can see a popover in line, because you have much more screen real estate.  Well, on a mobile device, you don't have that screen real estate,  you're transitioning to another page.  The difference with web is that you're really not limited to the screen size,  the user could start out small and then it could get big.  So popovers actually become really, really challenging.  Touch and gestures.  Another really big thing that was introduced with iOS 7,  and that everybody has become used to now,  is you can swipe to go back and you can do native scrolling.  Touch and gesture support is something that the native frameworks provide,  and they provide pretty good SDKs for them.  The web, again, is slightly more difficult.  It's all possible, but none of this is easy.  And the last thing is home screen apps.  Users expect to be able to have your icon for your app on your home screen.  I mean, why wouldn't they?  They want to be able to quickly access your app,  and you want users to be able to quickly access your app,  and use it as if it were a native app.  It's interesting because studies show that users don't know all care the difference  between a native and a web app.  Realistically, what is the difference?  From the user's point of view, they're clicking on an icon,  on their home screen, and up pops your app.  Whether they get that through a web URL,  whether they get that through an app store, it doesn't make any difference.  And fake news, I made that up.  But the sentiment is true.  It really doesn't matter where your application comes from.  Users expect the same level of functionality from a web app versus a native app,  but they honestly don't know or care the difference.  If I ask my mom, for example, and sorry, mom,  I'm going to throw you under a bus a little bit here,  to describe the difference between a web app and a native app,  she'll say that, well, the web app,  I get to through a web browser and the native app,  I install through an app store,  or I click on a button on my home screen.  And I'm like, why?  Do users even care?  Well, I'm pretty sure if they have to download an ATMEG app,  just to get back to the same place that they were on your website,  and they're doing that over a 3G connection,  I'm pretty sure they care.  So why are these things important?  Well, users become to expect a certain level of polish in your application.  And right now, web apps don't really live up to that same expectation.  So how can we address that?  Well, I'm going to show you some of the things that we have used  throughout our journey with Ember.  So add-ons.  As I mentioned earlier, add-ons really do provide you  this extra level of flexibility with your application  to be able to use pre-existing made components  and utilities and helpers to be able to make your application perform better.  So these are the add-ons that we used within our application.  We've got everything here from animation support to concurrency.  We've got order prefixes, so you don't have to keep using your browser prefixes.  It will automatically do those things for you.  And these are all community add-ons.  These are things that others have made that have made our lives easier.  We haven't had to spend the time reinventing that wheel.  So the first one is offline support.  And we're going to go back to school for a bit.  We're going to do some maths.  So the speed of light.  The speed of light in glass is 2 thirds of the speed of light in a vacuum.  So you've got, what's that, 200 million metres per second.  The distance from the US to Australia is roughly 15,000 kilometres.  So speed equals distance over time if you take yourself back to your physics classes.  Therefore, time equals distance over speed.  So if you're in Australia, the round, the single trip from Australia to the US, 75 milliseconds.  And obviously it's round trip, 150 milliseconds.  You've got 150 milliseconds of nothing.  And that's working on the best case scenario that all of the routers and switches between Australia and America are instantly fast, which they're not.  Verizon actually publishes statistics on this and they published 152 milliseconds, so I think my maths is right.  So offline support, this was touched on earlier.  Super, super important.  We're using AppCache in here because AppCache has better support across browsers.  It's not as clever as ServiceWorker.  You really need to do a sort of two-pronged approach there.  But you can see here that the app is being put into offline mode and it still works.  You can navigate between different pages.  Obviously things like authentication do not work.  You have to hit your server for that.  And this is what it looks like.  You've got a manifest here, you basically just list, these are all the files.  Hey, browser, go, store all of these offline and the browser will tell you when that's happened.  And you can then decide what you want to do when that's happened.  You can present the user with a notification saying, hey, how about you add this app to your home screen now that you've downloaded all the assets?  So AppCache, great support, ServiceWorker, not great support.  It will happen.  It'll be amazing.  It'll be great.  Animations.  Animations are a huge part of your application.  So we're using an add-on here called Ember LiquidFire.  And what LiquidFire provides us is a literal drop-in replacement for your standard outlet tag.  And it provides you animations out of the box.  You have to do almost nothing to actually support it.  It's incredible.  And as you can see here, we're logging in.  We click Login.  We'll get a nice little slide animation.  Hey, that looks like a native app.  Cool.  So this is what it looks like.  You swap out your, let me use the laser.  Oh, yeah.  You swap out your regular outlet with a liquid outlet.  And you right here configure a transition.  And we're saying from the index route to the login route, we want to use a crossfade.  And we want to use that in the reverse.  So if the user clicks forward or back in their browser, they get the reverse.  For those of you on this side, we're going to do a transition from the login route to the dashboard.  And we're going to use the two lefts, like the iOS style UI navigation controller.  Transition.  Next up, authentication.  Anybody here who has an application that has users has to deal with authentication.  So how do we do that?  Well, we used an application or an add-on called Embersimple-auth.  These lasers are cool.  Embersimple-auth, it provides you with scaffolding to handle your application's authentication  and session management.  It provides the basic hooks to be able to authenticate with the API.  It provides the ability to automatically attach outgoing headers so you can attach like a bearer token  for your Oauth2 header.  You can automatically sign outgoing requests for things like basic auth and so on.  And it also provides session management such that if the user logs out in one tab,  they're logged out in all tabs.  Now, this isn't something that native apps have to deal with.  Like, if you log out in a native app, you're logged out.  In a web app, the user can have as many tabs open as they want.  And you want them to be logged out in every single tab.  Being logged in in one, when you've logged out in another, that would be bad.  Imagine if you were at a web cafe and you'd forgotten to close one of the tabs after you've logged out  and now someone has access to all your data.  Well, what about that?  Web components.  So off the back of Glimmer, we've had this promise of web components for some time.  And web components is a W3C spec that's still in the process of being finalized, I believe.  And as yet, we don't have access to them.  But we can get very, very close.  So we used an add-on called EmmaComponentCSS.  And this is a fantastic add-on.  Eric Bryn is somewhere in the audience here.  Shout out to him.  Thank you so much.  This has made our lives a million times better when it comes to dealing with CSS.  Anybody who's had to write CSS will know they've accidentally styled an element that they didn't intend on styling  because CSS is cascading style sheet.  And it cascades down through your browser.  So accidentally targeting something else in the browser.  I'm sorry, that's a feature, not a bug.  This is what it looks like.  So you have your styles CSS in your component.  And the add-on will automatically take those styles and also generate a class name.  And it prefixes all your styles with that class name.  It also adds a class name to your component HTML that's output in the browser.  And you'll notice that the class name here matches the class name in the DOM.  Now, what this means is it's literally impossible to leak a style outside of a component.  And this is awesome for that modularity that I was talking about earlier.  The ability to be able to reuse components throughout your application  and no guaranteed wherever you put this, it's not going to be affected by anyone else's CSS.  Of course, you can still affect child components,  which is why we used the descendant selector.  Which is why I don't know how to use this.  Yeah, you'll notice that we're using the descendant selector here.  So that in the CSS that's generated, we know we're targeting the specific elements  that are in our component itself and not accidentally leaking anything further down  to any components that are used within our component.  And this is what it looks like.  So it looks pretty much like that.  You can see that.  We've probably got the idea from the previous slide, but you can see there that the component name  that's generated there is the same as the component name that's generated there.  Alert some dialogues.  So users are accustomed to your application providing alerts and dialogues  either natively or within the application itself.  So we used an Ember add-on called Ember CLI Flash, and this is what it looks like.  As you can see, error.  Thing did not work.  We also color the header bar here up here, the toolbar, with the Ember computer property  that just changes the style there.  You'll notice that when this one disappears, that goes green.  Pretty cool.  And you can have sticky messages, which is pretty awesome.  So you can stay up there till the user dismisses it.  User dismissed it.  We also extended this add-on to add dialogue support.  So you can automatically add dialogue messages, of course, to your application.  This is in line with our standard branding.  Our native apps do this.  Our web app does this as well.  And it provides a pretty simple way of providing flash messages to the user.  So we used this add-on, Ember CLI Flash.  Shout out to Lauren Tan wherever she is.  Great work on this.  This has really saved us a lot of time with providing a centralized way of managing flash messages.  So if I go through this very quickly, you inject your service, the flash message service.  You grab the service and you push a message onto it.  So this is like a notice.  This can be anywhere in your application.  Let's say, for example, you have some offline notification message.  We then built a component which outputs the flash messages that are on the queue.  And the flash message system itself handles all the expiry of the messages, cleaning them up,  making them disappear, and so on.  Really, really awesome.  It just worked out of the box where we had to do a styler, which is pretty cool.  Concurrency.  There's a talk on this, I think, tomorrow by Alex.  Concurrency is a fun thing.  If you've ever done a callback with an Ember component, you will know you get into problems  if that component is removed from the DOM.  So Ember Concurrency helps with that.  So here we've got a like button.  And you can see, I'm clicking the like button, and you can see on the right-hand side here  the status messages that I just hacked in there.  And you can notice that every now and then we've got a bunch of grouped-up messages here.  So every time we click this, we're outputting a console log,  and then from the action that's triggered, we're also doing a console log.  And you'll notice that although you can't see it, I'm clicking this button curiously right now.  And outputting here, we've got multiple stacked messages because it's not doing anything.  The actual main action itself that's doing the HTTP request,  only one of them is happening per full run of this button.  So that timeout for that animation to happen and then disappear,  we only allow one action to actually happen whilst that's running.  Let me show you what that looks like.  So this might be, hopefully this is easy enough to see.  We import task and we import timeout from Ember concurrency.  And we define a property on our component.  And what this does is we call the task function and we provide a JavaScript generator.  From here, I'm just calling the action data down, actions up. This is good.  So our action is going to be called on this like button,  and that will be handled further up the stack.  And then we're yielding a timeout here.  So what this does is this yield and this timeout is actually yielding a promise.  And the task system in Ember concurrency is expecting to receive a promise,  return from it, and it will pause that, it will pause this function whilst that promise is running.  So timeout is just a really simple wrapper around set timeout that returns a promise.  And notice we've got this drop keyword on the end here.  There are various different ways to configure this and we can figure it in the drop method.  Basically what this does is every time this action is tried to be fired,  if there's one already running, it'll just drop the existing ones.  You can do other things like stack them up, you can cancel existing ones and so on,  but this is the functionality that we wanted.  And then from our click handler, we just call a form on the task itself there.  Super simple. And it means we don't end up in this callback hell nested callbacks  or chained then statements.  It makes our code very, very declarative and very easy to follow along.  So next up, popovers.  As I mentioned, popovers are really, really difficult with responsive applications.  You have no way of knowing realistically whether or not you should be transitioning to a new route  and showing a different screen or whether you should be showing the context information as a popover.  So we actually built an in repo add-on to handle this for us and this is what it looks like.  So we've got our account menu up here.  You click on that, we show a popover and we have logic in the popover to make sure it stays on the screen  and doesn't overflow the edges and so on.  And if you change the size of the viewport and you go down to mobile size,  this now actually does a route transition instead of doing a popover.  And it turns out this is actually a lot more complicated than it needs to be.  Doing this in a nice reusable way has meant a little bit of hacking,  but we're using mostly public APIs to do it, so it actually works really, really well.  And I don't actually have a code example of this, but if anybody wants to see it, please come and give me a shout afterwards.  And you basically invoke this in the same way that you do a link to.  We created a link to popover helper and that basically wraps around the popover functionality  and it automatically pulls your model from your route hook.  So all you have to do is define what is the route, what is the component that I want to render within the popover itself,  and that's it. It hooks up everything else for you.  So if you grab your model, it will inject it into the component as if by magic.  API actions. So who here has used an API that is not a standard REST JSON API?  I'm expecting to see every single person's hand up in here.  I call these restless APIs.  It's all well and groovy if you have the perfect RESTful JSON API and Embedator is like,  cool, bro, I got you back.  If you don't, it's not so easy.  So our application has some restless endpoints, namely like.  That like action you saw earlier is sending an HTTP request to user slash like,  you can see it happening here, and user slash unlike.  And Embedator doesn't handle this very well at all.  In fact, it doesn't even cover this in any way you are on your own.  However, Embedator handles like getting the URLs to send HTTP requests to.  There is some like crossover there, but it just doesn't handle the non-RESTful semantics.  So we built an add-on to handle this for us.  We call it Embedator actions.  It's currently in repo. I'm hoping to open source it.  But this is what it looks like, and it's super, super simple.  So, oh, no. So here, I haven't imported it up here, so you can't see that.  But basically you call action.  So you define a property on your model, call action, pass it the action that you want to run,  and you pass it the property that you want to toggle or change when the action has returned true.  Same in the inverse, unlike, unlike, and we're going to change like back to false.  Then in your adapter, you define a set of actions, and here I'm just defining the like action,  and I'm going to say when this action is fired, hit up user slash like,  and you're going to take the model keys, ID and reblog key.  These are Tumblr-specific things, and those will automatically go in the post body.  You can configure whether it's a post request, a put request, a delete request, whatever you like,  and those things automatically get added into the body.  Then in your component, the API is super, super simple.  Grab the post, call like.  Seems simple, right?  This is really, really difficult with Embedator out of the box.  So we built an add-on for it, and we did it as an in repo add-on.  In repo add-ons are super cool.  You should give them a try.  This is what this intax looked like to generate one.  You call ember g in repo add-on, and you call give it a name.  Then when you want to generate something within that component, you want to say create an add-on,  sorry, create a component in that add-on.  You just add the dash dash in repo add-on and give the name of the add-on that you're generating the component into,  and ember will automatically generate you a structure that looks something like this.  This is our Embedator add-ons.  Very, very simple.  We've got our mix-in for our adapter, and we've got a utility that we use in the model, that action callback that I showed you.  Really simple way of prototyping out something that you think,  maybe I might want to outsource this in the future.  The last thing is living style guides.  Now, living style guides has been really, really useful for us,  and we're using an add-on called ember freestyle.  As you can see here, this looks like what you saw earlier.  We've got all our different add-ons.  It shows you the invocation below it, you see right there.  This is actually rendering what is in here.  You basically wrap this in a special semantics for this add-on,  and it will render that add-on in that block,  and it will show you the invocation at the bottom here.  This is really useful for both developers and for designers,  because it allows them to separate those concerns.  Designers can decide, well, actually, you know what,  I don't want to see all the code, I don't really care about that.  They can toggle that on and off.  Developers can look at this, and it basically provides you a certain level of API documentation for your components.  You can see right here, it lists out all the different permutations of a component that you might have.  And bam!  You can happily manage change management, make sure things don't break.  And as you can see, it looks pretty snazzy.  So we still have plenty more to do.  And one thing that I really want to get across here is the use of add-ons allows you to do one great thing.  It allows you to stop reinventing the wheel.  Unless, of course, you're in the business of wheel making,  then don't go ahead and reinvent the wheel.  Build an add-on. Use an add-on.  At the very least, if you have a concern in your application, it probably applies to someone else as well.  So consider making an add-on.  Start it off as an in repo add-on and move on from there.  So things are not perfect right now in the Ember ecosystem.  Ember data is awesome if your API is a perfect, restful JSON API.  Rocket.js. It's super powerful.  The documentation isn't great.  Ember works out of the box.  But sometimes you don't want the whole box.  And mobile performance for us in this application is absolutely paramount for 2017.  And I know it's something that is the forefront of Tom's mind right now,  making sure that Ember really is a first-class citizen when it comes to performance.  So the question is, how do we get from here to here?  Well, I was just trolling around on Twitter and I found this guy.  And he said this, as a tech community, we must treat documentation, marketing, logistics, infrastructure, art, etc. work with as much respect as engineering.  And this couldn't be any more true.  We have a responsibility as consumers of this application to give back.  Now that doesn't mean you have to develop an add-on.  It could be that it took you weeks to understand what serializers are and what adapters are in Ember data.  And then finally, you read a blog post on it and you're like, oh my God, I get it.  Submit a pull request to the guides repo because it's not that clear.  Another thing, when you're looking at, this is really interesting,  and I probably should have done this really prior to this because I keep my own dog food,  but I'm not sure if you're going to be able to do this.  Returning something from a model hook, when you look at how that stuff works in Ember, it's just assumed that you're using Ember data.  Maybe you're not.  It doesn't actually say on the first model, the guides for the model hook that you can just return a promise.  Or if it is there, it's very, very buried.  And these are the kind of things that stop other developers from being able to get involved in the Ember ecosystem because some people want to persist and really dig into the internals and find out how stuff works and some people don't.  For those that don't, that's totally okay.  No one's expecting you to do so.  But if you know the answer, just submit a pull request, make things better.  Another big thing we need to be doing is we need to break up the monolith.  And I was really unsure about this slide because it's kind of weird and it creeps me out.  But for those of you that haven't read it, there's an RFC.  It's up here. It's number 176.  And you can get to it, github.com.  About breaking up the Ember monolith application.  And glimmer is going a huge way to accomplishing this.  There's a whole bunch of other things within Ember itself that if you don't need all of the box, don't ship all of the box.  And that's imperative not just from a shipping the bytes down the wire, but from the bigger the payload, the longer it takes.  The larger the payload, the more JavaScript there is to pass and evaluate and the slower your boot up time is going to be.  It also means that we can really start competing in the framework wars as not shipping a monolith.  I think people nowadays in the JavaScript community are used to being able to receive small packages.  So that's what we want to be able to do.  So what did we learn?  Well, it turns out making web apps is hard.  But making native comparable web apps is even harder.  There are so many things that you have to take account of these days.  We don't have the luxury of native applications where they come with these lovely APIs and SDKs like iOS that comes with UI kit and all the animation libraries.  And we don't have that.  We have the DOM.  If we're lucky, maybe somebody is abstracted into a library that we can use.  This is where add-ons come in.  We are providing abstractions on top of this to make these APIs accessible and consumable by other developers like yourself.  You can't simply just get by with jQuery anymore.  These are some of the concerns that you have to deal with if you're building a mobile web application that you want to be as good as native.  There's a lot of things here and I've covered some of them, but there's a whole bunch of other things, even outside of this slide as well, that you have to be aware of.  Like security is a huge thing.  So things like sub-resource integrity, go check that out.  It's supported in Ember out of the box.  Cool.  Things like cross-site scripting.  You want to have 60 frames per second animations.  You want to make sure that you have code splitting, which we hopefully will do sometime in the future.  You want to have a deployment strategy.  You need a build environment.  You want to potentially have offline data and use it like index DB.  You want to have async bundles so you can split your application down into smaller pieces and only ship the user what they need.  So many things that you have to deal with.  Don't try and reinvent the wheel.  So I'm going to leave you with a few things here.  Number one, stand on the shoulders of giants.  You can do it by using a framework like Ember.  You don't need to reinvent the wheel.  You can use the collective knowledge of the community and we can all benefit together.  We can do great things.  If we work together, build add-ons, as the core team said earlier, as Tom and you had us said, they're going to be focusing on lower-level primitives,  so we'll really allow add-on developers to bootstrap and build out functionality on top of Ember for the entire community going forward.  So let's say check out emberadons.com.  It's a great resource for the latest and greatest add-ons that are on the market.  And I want to take this opportunity to give a shout-out to Tom and Yehuda and the entire Ember team and the core teams for building an incredible framework that we've been able to build our applications on top of.  So I think at this point I'm just going to give everybody a round of applause for the Ember core teams.  So thank you.  This is me.  If you want to hit me up, please do so.  If anybody wants to have a look at the application that we've built, I'm more than happy to demo it.  Thanks very much.  Enjoy your break.
Rebuilding_Tumblr_as_a_Single_Page_App_0_47.png Rebuilding_Tumblr_as_a_Single_Page_App_48_2699.png Rebuilding_Tumblr_as_a_Single_Page_App_2700_7151.png Rebuilding_Tumblr_as_a_Single_Page_App_7152_10055.png Rebuilding_Tumblr_as_a_Single_Page_App_10056_15263.png Rebuilding_Tumblr_as_a_Single_Page_App_15264_20519.png Rebuilding_Tumblr_as_a_Single_Page_App_20520_21011.png Rebuilding_Tumblr_as_a_Single_Page_App_21012_21107.png Rebuilding_Tumblr_as_a_Single_Page_App_21108_22895.png Rebuilding_Tumblr_as_a_Single_Page_App_22896_23099.png Rebuilding_Tumblr_as_a_Single_Page_App_23100_23639.png Rebuilding_Tumblr_as_a_Single_Page_App_23640_24491.png Rebuilding_Tumblr_as_a_Single_Page_App_24492_30203.png Rebuilding_Tumblr_as_a_Single_Page_App_30204_30251.png Rebuilding_Tumblr_as_a_Single_Page_App_30252_31331.png Rebuilding_Tumblr_as_a_Single_Page_App_31332_33587.png Rebuilding_Tumblr_as_a_Single_Page_App_33588_33659.png Rebuilding_Tumblr_as_a_Single_Page_App_33660_33731.png Rebuilding_Tumblr_as_a_Single_Page_App_33732_33827.png Rebuilding_Tumblr_as_a_Single_Page_App_33828_33887.png Rebuilding_Tumblr_as_a_Single_Page_App_33888_33947.png Rebuilding_Tumblr_as_a_Single_Page_App_33948_34763.png Rebuilding_Tumblr_as_a_Single_Page_App_34764_35471.png Rebuilding_Tumblr_as_a_Single_Page_App_35472_38939.png Rebuilding_Tumblr_as_a_Single_Page_App_38940_39551.png Rebuilding_Tumblr_as_a_Single_Page_App_39552_39611.png Rebuilding_Tumblr_as_a_Single_Page_App_39612_39683.png Rebuilding_Tumblr_as_a_Single_Page_App_39684_41135.png Rebuilding_Tumblr_as_a_Single_Page_App_41136_42671.png Rebuilding_Tumblr_as_a_Single_Page_App_42672_43727.png Rebuilding_Tumblr_as_a_Single_Page_App_43728_44003.png Rebuilding_Tumblr_as_a_Single_Page_App_44004_47147.png Rebuilding_Tumblr_as_a_Single_Page_App_47148_54863.png Rebuilding_Tumblr_as_a_Single_Page_App_54864_55487.png Rebuilding_Tumblr_as_a_Single_Page_App_55488_55512.png
Hello, everyone. Hopefully you've had time with a pee. Do whatever you need to do, and we're good to go for the next session. Hi, my name is Oli Griffiths. I don't know how to use this remote, so let's do that. There we go. Yes, this is me. I am a software engineer, and I don't do JavaScript. I'm actually a PHP engineer. I work at Tumblr. Shout out to my PHP folks over there. Yeah, there's not many in here. Yeah, I work at Tumblr. I've been there for a couple of years now, and moved to the US four years ago. I'm obviously not American or Australian, as I usually get. Yes, so I work at Tumblr. I work on the backend team there. And what is Tumblr? Who here has used Tumblr or knows what it is? All right, that's pretty good. Usually I don't get that. Most people are like, yeah, what is it, like a blogging thing? I don't really know. So this is Tumblr. It's a community-driven application full of all sorts of weird and wonderful things that exist on the Internet. It's really centered around communities. It's there to fill the gap between the personalness of Facebook, the public presence of Twitter, and the take a photo of everything I eat land of Instagram. So this is some of the stuff that you can get on there. Whether you're into comics, computers, home improvements, supercars, sports, even food porn, there's pretty much everything on Tumblr for you. Go check it out. So Tumblr is a place to be yourself. So cool, that's that spiel out of the way. Let's get on to the cool stuff. Engineering. So, Tumblr, we deal on a massive scale. We've got roughly 147 billion blog posts, 341 million blogs. It quits about 35 million posts a day, and about 3 million cash hits a second. This is like, it's pretty big data. But along with big data and big infrastructure comes a set of pitfalls. We have a fairly high latency outside of the US. We are hosted in the US. It's a US brand born out of New York. We have multiple code bases to support. It's a 10-year-old application, and with 10 years comes a fair amount of legacy code base. And coming with that is an inability to adopt new technologies. It makes it difficult to be agile, and we kind of struggle with our web app to provide native app-like functionality. It becomes difficult. So this is our stack right now. We are primarily in the front end, backbone and jQuery-based. We have a custom MVC stack that uses backbone. We have a three to four-minute build time, which is super fun if you're trying to build any of our legacy assets. You can literally go and get a cup of tea while that's happening. And I'll do that now. And we have some custom data bootstrapping. So, what am I here to talk to you about? Well, at Tumblr, we have a thing called Hack Days. And if your company doesn't do Hack Days, you should absolutely give them a try. They're freaking awesome. We love them at Tumblr. They drive innovation, and you can basically build whatever you like. You have 24 hours to build anything. One year, somebody built an interactive mirror. Nothing to do with Tumblr, but it was cool, and it worked. Yeah, so you have 24 hours, you can build anything. We have prizes and raffles and games. At Tumblr, we love them. But this is me. My first Hack Day. All bright-eyed and bushy-tailed. I just started at Tumblr, and I thought, okay, what am I going to do? Tumblr has an API. We have a website. We have apps that are powered off the API. We have a different website that's not powered off the API. And it's a completely different code path. So why don't I try building Tumblr as a single-page app? So I decided to have a go with EmberJS. I'd had a tiny little bit of experience with it before, but it was a good, well, it says on the website, it's a framework for ambitious web apps. Tumblr is a web app, so why not give it a go? And this is me. 24 hours in. Burning in copied and pasted HTML and CSS directly into this brand-flashy new application. And it turns out that just copying and pasting your HTML from your current app into a new one is a bad idea. Don't do that. But luckily, Tumblr has a facility for us to be able to take Hack Days beyond a single 24-hour period. We call them labs. So labs allow us a half a day a week to work on a side project outside of our daily roles at Tumblr. And it's encouraged to bootstrap projects that are outside of the, you know, regular product roadmap. So I decided, let's start a single-page app lab. And this started in October 2015. So half a day a week, not very long. It started October 2015. And I'm going to preface this by saying, the only thing that you're about to see is still a lab. It's still a complete prototype, but it's proving a point. So this is the Ragtag bunch of guys that started this project with me. They are sitting somewhere over there, fellow Tumblers. And we decided the best thing that we should do is to see if we could build a native comparable mobile web app. And what exactly is required in order to be able to do so? So the key point here is making sure that the application that we produce is as close to a native application experience as possible. And you shouldn't leave your web users behind. Yes, provide them with a native app if you so wish to, but don't neglect your web experience just for the sake of it. So we decided we wanted to make a first-class web experience. So this was October 2015. There were really two major candidates at the time. We had Ember, we had React. And we put them head-to-head against one another. We decided these are the two major frameworks that are going to be able to solve this problem for us. And we decided to build a simple prototype in both. The basic idea of the prototype was to fetch data from the API and render a list of posts. Nothing fancy, just like title, ID, that was it. And what would we need to go through in order to get to that stage? Like from step one, nothing like npm install to that point, what do we have to go through? Can anybody name these two characters, by the way? Yes, and? Yes, excellent. Now, I don't know if anybody noticed, but Tumblr has this really weird obsession with Guy Fury. And you'll notice he's right there. Can't get away. So we started. We started down this road, and this is where it started. We jump in head-first into the world of single-page apps. And this means everything from understanding Ember's build system to understanding how to do the same thing in React. We wanted to build a similar prototype in both. So it kind of felt a little bit like Mr. Krabs here, with the fast pace of JavaScript. It's kind of overwhelming. There's a lot of things to learn. But we decided, let's do it, feet-first, jump straight in. We noticed that with Ember, we were able to get up and running and be productive from day one. React was a little more difficult. If anybody here has tried to configure Webpack to build React, pre-react create app, which is a godsend, but it didn't exist in October 2015. And this literally took us days to get just to the point at which you could do, like, Ember new, and have a full set up build environment, tests, and everything. So it was difficult. And because of that, we also looked into the backing data stores that were available for React. And at the time, Redux was still in fairly early stages of adoption. It's kind of difficult to get started with. And Ember for us just worked out of the box. It was simple, Ember new, off we go. And in the first day, we had our list of posts being rendered straightaway. So eventually, we ended up here. We're aboard the Nebuchadnezzar with immense power beneath our feet. We knew where we were going, we knew what we wanted to do, and we knew that Ember was the tool to do that for us. Ember also has one major, major advantage over React, and I cannot stress this enough. And that'd be add-ons. Add-ons provide so much extra for the React ecosystem that the React, unfortunately, doesn't have right now. NPM is not a replacement for add-ons. It allows you to add into your application through build pipelines, through providing, essentially, full applications as an add-on. So add-ons really, really allow you to turbocharge your Ember application. Yeah, right? Portons cool, by the way. I've got another cool slide later. So creating a native comparable mobile web app. What is actually involved in that? What things do you actually have to do if you want to build something that is as good as a native app? Well, first off, you have offline support, and that was touched on earlier. For a native app experience, you need to have the app boot immediately. It needs to load off the device. You cannot be waiting for round trip times to your data center to go and fetch your HTML, assets, and so on. So you really should have the app being bootable off the device. No HTTP requests. Offline is absolutely paramount. Animations. Users have become accustomed to smooth 60 frames a second animations, and they provide this sort of skeumorphic experience to the user where you've got, like, bounces and easing and so on that feels good when you use them. And I remember when iOS 7 was released, and they added all the gesture support and animations and all that kind of stuff, and it made all the apps feel good. The web, lots of good. There are some solutions to these, but out of the box, it has the tools, but there's no out-of-the-box SDKs that are going to make it nice and buttery smooth for you. Multirin reusable. This is something that native apps kind of get out of the box. You have a UI kit in iOS where you're able to build reusable views that you can drop in anywhere throughout your application. So if you render a post in a list of posts on one screen, that you use that same view somewhere else in your application, and it looks the same, users, like, they expect consistency throughout your application, and for it being modular and reusable within the application itself, so we're talking about components and things here, that's really, really important. Push notifications. We finally got support for this a few years ago, but prior to that, it was like, you should have a look, I'm afraid. Push notifications were something only the native apps had. We can do this now, and users are expecting this. They expect to be able to have push notifications. Popovers are a really, really interesting thing. Popovers provide in-band context information to your application. So if anybody here has ever used an iPad, which I'm sure everybody has, you'll notice that on the iPad, you can tap on a button and you can see a popover in line, because you have much more screen real estate. Well, on a mobile device, you don't have that screen real estate, you're transitioning to another page. The difference with web is that you're really not limited to the screen size, the user could start out small and then it could get big. So popovers actually become really, really challenging. Touch and gestures. Another really big thing that was introduced with iOS 7, and that everybody has become used to now, is you can swipe to go back and you can do native scrolling. Touch and gesture support is something that the native frameworks provide, and they provide pretty good SDKs for them. The web, again, is slightly more difficult. It's all possible, but none of this is easy. And the last thing is home screen apps. Users expect to be able to have your icon for your app on your home screen. I mean, why wouldn't they? They want to be able to quickly access your app, and you want users to be able to quickly access your app, and use it as if it were a native app. It's interesting because studies show that users don't know all care the difference between a native and a web app. Realistically, what is the difference? From the user's point of view, they're clicking on an icon, on their home screen, and up pops your app. Whether they get that through a web URL, whether they get that through an app store, it doesn't make any difference. And fake news, I made that up. But the sentiment is true. It really doesn't matter where your application comes from. Users expect the same level of functionality from a web app versus a native app, but they honestly don't know or care the difference. If I ask my mom, for example, and sorry, mom, I'm going to throw you under a bus a little bit here, to describe the difference between a web app and a native app, she'll say that, well, the web app, I get to through a web browser and the native app, I install through an app store, or I click on a button on my home screen. And I'm like, why? Do users even care? Well, I'm pretty sure if they have to download an ATMEG app, just to get back to the same place that they were on your website, and they're doing that over a 3G connection, I'm pretty sure they care. So why are these things important? Well, users become to expect a certain level of polish in your application. And right now, web apps don't really live up to that same expectation. So how can we address that? Well, I'm going to show you some of the things that we have used throughout our journey with Ember. So add-ons. As I mentioned earlier, add-ons really do provide you this extra level of flexibility with your application to be able to use pre-existing made components and utilities and helpers to be able to make your application perform better. So these are the add-ons that we used within our application. We've got everything here from animation support to concurrency. We've got order prefixes, so you don't have to keep using your browser prefixes. It will automatically do those things for you. And these are all community add-ons. These are things that others have made that have made our lives easier. We haven't had to spend the time reinventing that wheel. So the first one is offline support. And we're going to go back to school for a bit. We're going to do some maths. So the speed of light. The speed of light in glass is 2 thirds of the speed of light in a vacuum. So you've got, what's that, 200 million metres per second. The distance from the US to Australia is roughly 15,000 kilometres. So speed equals distance over time if you take yourself back to your physics classes. Therefore, time equals distance over speed. So if you're in Australia, the round, the single trip from Australia to the US, 75 milliseconds. And obviously it's round trip, 150 milliseconds. You've got 150 milliseconds of nothing. And that's working on the best case scenario that all of the routers and switches between Australia and America are instantly fast, which they're not. Verizon actually publishes statistics on this and they published 152 milliseconds, so I think my maths is right. So offline support, this was touched on earlier. Super, super important. We're using AppCache in here because AppCache has better support across browsers. It's not as clever as ServiceWorker. You really need to do a sort of two-pronged approach there. But you can see here that the app is being put into offline mode and it still works. You can navigate between different pages. Obviously things like authentication do not work. You have to hit your server for that. And this is what it looks like. You've got a manifest here, you basically just list, these are all the files. Hey, browser, go, store all of these offline and the browser will tell you when that's happened. And you can then decide what you want to do when that's happened. You can present the user with a notification saying, hey, how about you add this app to your home screen now that you've downloaded all the assets? So AppCache, great support, ServiceWorker, not great support. It will happen. It'll be amazing. It'll be great. Animations. Animations are a huge part of your application. So we're using an add-on here called Ember LiquidFire. And what LiquidFire provides us is a literal drop-in replacement for your standard outlet tag. And it provides you animations out of the box. You have to do almost nothing to actually support it. It's incredible. And as you can see here, we're logging in. We click Login. We'll get a nice little slide animation. Hey, that looks like a native app. Cool. So this is what it looks like. You swap out your, let me use the laser. Oh, yeah. You swap out your regular outlet with a liquid outlet. And you right here configure a transition. And we're saying from the index route to the login route, we want to use a crossfade. And we want to use that in the reverse. So if the user clicks forward or back in their browser, they get the reverse. For those of you on this side, we're going to do a transition from the login route to the dashboard. And we're going to use the two lefts, like the iOS style UI navigation controller. Transition. Next up, authentication. Anybody here who has an application that has users has to deal with authentication. So how do we do that? Well, we used an application or an add-on called Embersimple-auth. These lasers are cool. Embersimple-auth, it provides you with scaffolding to handle your application's authentication and session management. It provides the basic hooks to be able to authenticate with the API. It provides the ability to automatically attach outgoing headers so you can attach like a bearer token for your Oauth2 header. You can automatically sign outgoing requests for things like basic auth and so on. And it also provides session management such that if the user logs out in one tab, they're logged out in all tabs. Now, this isn't something that native apps have to deal with. Like, if you log out in a native app, you're logged out. In a web app, the user can have as many tabs open as they want. And you want them to be logged out in every single tab. Being logged in in one, when you've logged out in another, that would be bad. Imagine if you were at a web cafe and you'd forgotten to close one of the tabs after you've logged out and now someone has access to all your data. Well, what about that? Web components. So off the back of Glimmer, we've had this promise of web components for some time. And web components is a W3C spec that's still in the process of being finalized, I believe. And as yet, we don't have access to them. But we can get very, very close. So we used an add-on called EmmaComponentCSS. And this is a fantastic add-on. Eric Bryn is somewhere in the audience here. Shout out to him. Thank you so much. This has made our lives a million times better when it comes to dealing with CSS. Anybody who's had to write CSS will know they've accidentally styled an element that they didn't intend on styling because CSS is cascading style sheet. And it cascades down through your browser. So accidentally targeting something else in the browser. I'm sorry, that's a feature, not a bug. This is what it looks like. So you have your styles CSS in your component. And the add-on will automatically take those styles and also generate a class name. And it prefixes all your styles with that class name. It also adds a class name to your component HTML that's output in the browser. And you'll notice that the class name here matches the class name in the DOM. Now, what this means is it's literally impossible to leak a style outside of a component. And this is awesome for that modularity that I was talking about earlier. The ability to be able to reuse components throughout your application and no guaranteed wherever you put this, it's not going to be affected by anyone else's CSS. Of course, you can still affect child components, which is why we used the descendant selector. Which is why I don't know how to use this. Yeah, you'll notice that we're using the descendant selector here. So that in the CSS that's generated, we know we're targeting the specific elements that are in our component itself and not accidentally leaking anything further down to any components that are used within our component. And this is what it looks like. So it looks pretty much like that. You can see that. We've probably got the idea from the previous slide, but you can see there that the component name that's generated there is the same as the component name that's generated there. Alert some dialogues. So users are accustomed to your application providing alerts and dialogues either natively or within the application itself. So we used an Ember add-on called Ember CLI Flash, and this is what it looks like. As you can see, error. Thing did not work. We also color the header bar here up here, the toolbar, with the Ember computer property that just changes the style there. You'll notice that when this one disappears, that goes green. Pretty cool. And you can have sticky messages, which is pretty awesome. So you can stay up there till the user dismisses it. User dismissed it. We also extended this add-on to add dialogue support. So you can automatically add dialogue messages, of course, to your application. This is in line with our standard branding. Our native apps do this. Our web app does this as well. And it provides a pretty simple way of providing flash messages to the user. So we used this add-on, Ember CLI Flash. Shout out to Lauren Tan wherever she is. Great work on this. This has really saved us a lot of time with providing a centralized way of managing flash messages. So if I go through this very quickly, you inject your service, the flash message service. You grab the service and you push a message onto it. So this is like a notice. This can be anywhere in your application. Let's say, for example, you have some offline notification message. We then built a component which outputs the flash messages that are on the queue. And the flash message system itself handles all the expiry of the messages, cleaning them up, making them disappear, and so on. Really, really awesome. It just worked out of the box where we had to do a styler, which is pretty cool. Concurrency. There's a talk on this, I think, tomorrow by Alex. Concurrency is a fun thing. If you've ever done a callback with an Ember component, you will know you get into problems if that component is removed from the DOM. So Ember Concurrency helps with that. So here we've got a like button. And you can see, I'm clicking the like button, and you can see on the right-hand side here the status messages that I just hacked in there. And you can notice that every now and then we've got a bunch of grouped-up messages here. So every time we click this, we're outputting a console log, and then from the action that's triggered, we're also doing a console log. And you'll notice that although you can't see it, I'm clicking this button curiously right now. And outputting here, we've got multiple stacked messages because it's not doing anything. The actual main action itself that's doing the HTTP request, only one of them is happening per full run of this button. So that timeout for that animation to happen and then disappear, we only allow one action to actually happen whilst that's running. Let me show you what that looks like. So this might be, hopefully this is easy enough to see. We import task and we import timeout from Ember concurrency. And we define a property on our component. And what this does is we call the task function and we provide a JavaScript generator. From here, I'm just calling the action data down, actions up. This is good. So our action is going to be called on this like button, and that will be handled further up the stack. And then we're yielding a timeout here. So what this does is this yield and this timeout is actually yielding a promise. And the task system in Ember concurrency is expecting to receive a promise, return from it, and it will pause that, it will pause this function whilst that promise is running. So timeout is just a really simple wrapper around set timeout that returns a promise. And notice we've got this drop keyword on the end here. There are various different ways to configure this and we can figure it in the drop method. Basically what this does is every time this action is tried to be fired, if there's one already running, it'll just drop the existing ones. You can do other things like stack them up, you can cancel existing ones and so on, but this is the functionality that we wanted. And then from our click handler, we just call a form on the task itself there. Super simple. And it means we don't end up in this callback hell nested callbacks or chained then statements. It makes our code very, very declarative and very easy to follow along. So next up, popovers. As I mentioned, popovers are really, really difficult with responsive applications. You have no way of knowing realistically whether or not you should be transitioning to a new route and showing a different screen or whether you should be showing the context information as a popover. So we actually built an in repo add-on to handle this for us and this is what it looks like. So we've got our account menu up here. You click on that, we show a popover and we have logic in the popover to make sure it stays on the screen and doesn't overflow the edges and so on. And if you change the size of the viewport and you go down to mobile size, this now actually does a route transition instead of doing a popover. And it turns out this is actually a lot more complicated than it needs to be. Doing this in a nice reusable way has meant a little bit of hacking, but we're using mostly public APIs to do it, so it actually works really, really well. And I don't actually have a code example of this, but if anybody wants to see it, please come and give me a shout afterwards. And you basically invoke this in the same way that you do a link to. We created a link to popover helper and that basically wraps around the popover functionality and it automatically pulls your model from your route hook. So all you have to do is define what is the route, what is the component that I want to render within the popover itself, and that's it. It hooks up everything else for you. So if you grab your model, it will inject it into the component as if by magic. API actions. So who here has used an API that is not a standard REST JSON API? I'm expecting to see every single person's hand up in here. I call these restless APIs. It's all well and groovy if you have the perfect RESTful JSON API and Embedator is like, cool, bro, I got you back. If you don't, it's not so easy. So our application has some restless endpoints, namely like. That like action you saw earlier is sending an HTTP request to user slash like, you can see it happening here, and user slash unlike. And Embedator doesn't handle this very well at all. In fact, it doesn't even cover this in any way you are on your own. However, Embedator handles like getting the URLs to send HTTP requests to. There is some like crossover there, but it just doesn't handle the non-RESTful semantics. So we built an add-on to handle this for us. We call it Embedator actions. It's currently in repo. I'm hoping to open source it. But this is what it looks like, and it's super, super simple. So, oh, no. So here, I haven't imported it up here, so you can't see that. But basically you call action. So you define a property on your model, call action, pass it the action that you want to run, and you pass it the property that you want to toggle or change when the action has returned true. Same in the inverse, unlike, unlike, and we're going to change like back to false. Then in your adapter, you define a set of actions, and here I'm just defining the like action, and I'm going to say when this action is fired, hit up user slash like, and you're going to take the model keys, ID and reblog key. These are Tumblr-specific things, and those will automatically go in the post body. You can configure whether it's a post request, a put request, a delete request, whatever you like, and those things automatically get added into the body. Then in your component, the API is super, super simple. Grab the post, call like. Seems simple, right? This is really, really difficult with Embedator out of the box. So we built an add-on for it, and we did it as an in repo add-on. In repo add-ons are super cool. You should give them a try. This is what this intax looked like to generate one. You call ember g in repo add-on, and you call give it a name. Then when you want to generate something within that component, you want to say create an add-on, sorry, create a component in that add-on. You just add the dash dash in repo add-on and give the name of the add-on that you're generating the component into, and ember will automatically generate you a structure that looks something like this. This is our Embedator add-ons. Very, very simple. We've got our mix-in for our adapter, and we've got a utility that we use in the model, that action callback that I showed you. Really simple way of prototyping out something that you think, maybe I might want to outsource this in the future. The last thing is living style guides. Now, living style guides has been really, really useful for us, and we're using an add-on called ember freestyle. As you can see here, this looks like what you saw earlier. We've got all our different add-ons. It shows you the invocation below it, you see right there. This is actually rendering what is in here. You basically wrap this in a special semantics for this add-on, and it will render that add-on in that block, and it will show you the invocation at the bottom here. This is really useful for both developers and for designers, because it allows them to separate those concerns. Designers can decide, well, actually, you know what, I don't want to see all the code, I don't really care about that. They can toggle that on and off. Developers can look at this, and it basically provides you a certain level of API documentation for your components. You can see right here, it lists out all the different permutations of a component that you might have. And bam! You can happily manage change management, make sure things don't break. And as you can see, it looks pretty snazzy. So we still have plenty more to do. And one thing that I really want to get across here is the use of add-ons allows you to do one great thing. It allows you to stop reinventing the wheel. Unless, of course, you're in the business of wheel making, then don't go ahead and reinvent the wheel. Build an add-on. Use an add-on. At the very least, if you have a concern in your application, it probably applies to someone else as well. So consider making an add-on. Start it off as an in repo add-on and move on from there. So things are not perfect right now in the Ember ecosystem. Ember data is awesome if your API is a perfect, restful JSON API. Rocket.js. It's super powerful. The documentation isn't great. Ember works out of the box. But sometimes you don't want the whole box. And mobile performance for us in this application is absolutely paramount for 2017. And I know it's something that is the forefront of Tom's mind right now, making sure that Ember really is a first-class citizen when it comes to performance. So the question is, how do we get from here to here? Well, I was just trolling around on Twitter and I found this guy. And he said this, as a tech community, we must treat documentation, marketing, logistics, infrastructure, art, etc. work with as much respect as engineering. And this couldn't be any more true. We have a responsibility as consumers of this application to give back. Now that doesn't mean you have to develop an add-on. It could be that it took you weeks to understand what serializers are and what adapters are in Ember data. And then finally, you read a blog post on it and you're like, oh my God, I get it. Submit a pull request to the guides repo because it's not that clear. Another thing, when you're looking at, this is really interesting, and I probably should have done this really prior to this because I keep my own dog food, but I'm not sure if you're going to be able to do this. Returning something from a model hook, when you look at how that stuff works in Ember, it's just assumed that you're using Ember data. Maybe you're not. It doesn't actually say on the first model, the guides for the model hook that you can just return a promise. Or if it is there, it's very, very buried. And these are the kind of things that stop other developers from being able to get involved in the Ember ecosystem because some people want to persist and really dig into the internals and find out how stuff works and some people don't. For those that don't, that's totally okay. No one's expecting you to do so. But if you know the answer, just submit a pull request, make things better. Another big thing we need to be doing is we need to break up the monolith. And I was really unsure about this slide because it's kind of weird and it creeps me out. But for those of you that haven't read it, there's an RFC. It's up here. It's number 176. And you can get to it, github.com. About breaking up the Ember monolith application. And glimmer is going a huge way to accomplishing this. There's a whole bunch of other things within Ember itself that if you don't need all of the box, don't ship all of the box. And that's imperative not just from a shipping the bytes down the wire, but from the bigger the payload, the longer it takes. The larger the payload, the more JavaScript there is to pass and evaluate and the slower your boot up time is going to be. It also means that we can really start competing in the framework wars as not shipping a monolith. I think people nowadays in the JavaScript community are used to being able to receive small packages. So that's what we want to be able to do. So what did we learn? Well, it turns out making web apps is hard. But making native comparable web apps is even harder. There are so many things that you have to take account of these days. We don't have the luxury of native applications where they come with these lovely APIs and SDKs like iOS that comes with UI kit and all the animation libraries. And we don't have that. We have the DOM. If we're lucky, maybe somebody is abstracted into a library that we can use. This is where add-ons come in. We are providing abstractions on top of this to make these APIs accessible and consumable by other developers like yourself. You can't simply just get by with jQuery anymore. These are some of the concerns that you have to deal with if you're building a mobile web application that you want to be as good as native. There's a lot of things here and I've covered some of them, but there's a whole bunch of other things, even outside of this slide as well, that you have to be aware of. Like security is a huge thing. So things like sub-resource integrity, go check that out. It's supported in Ember out of the box. Cool. Things like cross-site scripting. You want to have 60 frames per second animations. You want to make sure that you have code splitting, which we hopefully will do sometime in the future. You want to have a deployment strategy. You need a build environment. You want to potentially have offline data and use it like index DB. You want to have async bundles so you can split your application down into smaller pieces and only ship the user what they need. So many things that you have to deal with. Don't try and reinvent the wheel. So I'm going to leave you with a few things here. Number one, stand on the shoulders of giants. You can do it by using a framework like Ember. You don't need to reinvent the wheel. You can use the collective knowledge of the community and we can all benefit together. We can do great things. If we work together, build add-ons, as the core team said earlier, as Tom and you had us said, they're going to be focusing on lower-level primitives, so we'll really allow add-on developers to bootstrap and build out functionality on top of Ember for the entire community going forward. So let's say check out emberadons.com. It's a great resource for the latest and greatest add-ons that are on the market. And I want to take this opportunity to give a shout-out to Tom and Yehuda and the entire Ember team and the core teams for building an incredible framework that we've been able to build our applications on top of. So I think at this point I'm just going to give everybody a round of applause for the Ember core teams. So thank you. This is me. If you want to hit me up, please do so. If anybody wants to have a look at the application that we've built, I'm more than happy to demo it. Thanks very much. Enjoy your break.
out of the way. Let's get on to the cool stuff. a week to work on a side project outside of our why we used the descendant selector. Which is why assumed that you're using Ember data. Maybe the internals and find out how stuff works and