Online Marketing

RailsConf 2016 – Turbolinks 5: I Can’t Believe It’s Not Native! by Sam Stephenson

You might know me from some of my open source contributions. I worked on. A lot of projects are being pal and tricks’. Most recently, I was one of the original rails core members back in the day, though I think nearly all of my code has been deleted, or at least by now I hope I work for base camp, and last year we launched an all new version of base Camp called base camp 3.

We created it in 18 months from the initial concept through dozens of designs and iterations and changes, and it’s a it’s. A big app has over 200 screens and we shipped it in 18 months on five different platforms simultaneously, so desktop web browser, mobile web browser and Android Native Client, an iOS native client and email, and we did all this with a tiny team at base camp. We have six rails developers, Android developers and iOS to others, and we also built three open source frameworks during that time period.

Tricks which is our rich text, editor action, cable, which I’m sure you’re familiar with in rails, 5 and also turbolinks 5, and this all sounds kind of unbelievable. When I say it that we managed to do all of this. But really the Rowling’s 5 was a secret weapon. They let us do it and it’s an entirely new version of turbolinks and it’s one of the banner features of rails 5 and it’s what I’m here to talk about today, so how many of you are familiar with turbo links, anyone right! It’s that thing you have to disable every time you started, I know, but seriously it’s it’s that thing that breaks all your jQuery, plugins, no turbolinks is misunderstood and to understand it.

You need some context and that’s what I hope to give you today, because I think it represents a philosophy of web development that deserves our attention. I’ve been at Basecamp for 10 and a half years now, which is a small eternity in Tucker’s and being at the same place for a decade, has been really interesting. I’ve had the opportunity to go deep on a lot of interesting problems and see the effects play out over many years every once in a while.

I think he can help to take a step back in and look at where you’re going and where you’ve been, and things were really different. When I started, do you guys remember what web development was like before rails? The only way to make real software was with j2ee. I found this this diagram of 2004 era, best practices. This is j2ee, it’s beautiful in its simplicity, really and then, of course, on the other end of the spectrum you at PHP but rails came along and changed all of this for the j2ee developers it throughout all that ceremony and had said hey look.

This can be a lot simpler and for the PHP folks, it said here’s some structure that makes sense and it helps you avoid. Writing the same thing over and over again in slightly different ways, and I think in some ways this period was the golden age of wealth development. If you ignore the browser situation at the time, because we could embrace the statelessness of HTTP and we could deliver a fully rendered UI on each request in response cycle, so this is the full request response cycle.

I think it’s one of the best one of the best features of rails and then web apps got more ambitious. We weren’t happy with the performance of the UI built on full page loads, so we started building single page apps and instead of each request returning a full HTML response. Now he started booting the app with an empty page and the page had some JavaScript others JavaScript made JSON API requests, so we made the server-side accept and return JSON and we moved the traditional responsibilities of rendering the UI completely into the client, and everybody agrees.

This is a good idea right: we’ve got rails 5 with rails API and you need an API anyway in your application. So why not consume it yourself plus. We want a decoupled presentation from data and decoupling is good right, but eventually we weren’t happy with the performance of client-side NEC, so we introduced a virtual Dom to minimize the number of Dom operations we make, which is a great idea. The DOM is slow.

So we should make as few changes to the Dom as possible, but you know what all that javascript and all those API requests really make. The initial page load take a long time and all the content on the page is inaccessible to search engines. So we decided to get rid of the bhoot page and render HTML instead, which means now we need a JavaScript runtime on the server. We need to make our client-side code run on both sides and we need the virtual Dom there too, because we don’t have a real table and now we’ve come full-circle and we’ve reinvented what we had in 2004.

But the complexity is increased exponentially and we’ve introduced literally hundreds of new dependencies to our applications, and the whole system is now too much for any single person to reasonably keep in their head at once. You need entire teams dedicated to managing these subsystems, which honestly is not a surprise. How many of you are familiar with Conway’s law. Conway’s law says that the structure of a software system necessarily mirrors the structure of the organization that created it and these client-side patterns and frameworks that were using now he came from Google and Facebook, which are huge companies with thousands of developers across dozens of teams.

Well guess what your team probably doesn’t, have the same problems as Google in facebook. So how did we get to this point? Well, we were chasing performance and each one of these changes that I went over makes sense incrementally each one made sense at the time. But looking back with into the compound effects, we traded small performance benefits for a massive regression in developer ergonomics and the further we go down the rabbit hole, the more we constrain our future choices and in 2016.

This only tells part of the picture, because you need it native apps on Android and iOS too. So you can multiply the complexity on that slide by three and you’ll be reimplemented each one of your app screens, natively, of course, because you want the best performance. So you’re going to have to multiply your headcount by three well fuck that, but if we accept that we need performant web and mobile apps, how do we reconcile this complexity with the rails philosophy, which says you know, we want to empower small teams to do big Things well what if I told you it didn’t, have to be so complex that we could return to the golden era of the request and full response model to rendering HTML views from rails without an intermediate API layer, and then we could use those same HTML view Ease as a baseline for your iOS and Android apps in a way that still feels native, this is what turbo links 5 gives you all right.

It turns your traditional web application into a single page app at a high level. It intercepts link clicks on your page and it turns them into e Jack’s requests. Your server renders a full HTML response and turbo links loads. That response pulls out the head, merges it in with the current pages head, pulls out the body and outright replaces it and, from the server’s perspective, nothing changes, but the client adopts the single process model, which means that it doesn’t incur the expense of reloading and reprocessing Assets on each page, Jewish or reestablishing WebSocket connections, which are very expensive, and this gives you a significant performance boost.

So how significant this is a side-by-side comparison. We recorded last night on the hotel Wi-Fi so this turbolinks on the left and a full page load on the right whoops. I’r sorry did you guys see it. So this is what we call good enough performance if you can transition from a click to the next screen in about 300 milliseconds you’re going to perceive it as instantaneous. Anything longer feels slow, but anything shorter is nearly imperceptible for full page changes, but chasing that kind of performance requires an inordinate amount of effort and resources.

So here’s my highly scientific graph, but I think it’s I think it’s mostly accurate right. We know from experience that it takes a lot of work to get on the left side of that graph, but 300 milliseconds, actually getting your rails request to complete and say 100 milliseconds is totally achievable and there’s a wide array of tools and strategies to help you Get there like Russian doll, caching, etags, lazy, loading content after the page loads and all that’s for a different talk.

But the point I want to make here is in most cases getting faster than 300 milliseconds just doesn’t matter, and what you gain with turbolinks approach is a dramatically simpler model. If you understand this line of code, you understand the core: their beliefs concept, it’s understandable, because it’s just HTML HTML is easy to generate from rails. It’s easy to cache designers already know how to work with it.

They can work directly with your views. Instead of making Photoshop comps or instead of waiting on an API team to expose the data that they need and when you combine this approach with responsive design techniques, your design team can create a single set of views that power desktop web mobile web and hybrid native Apps – and this is incredibly powerful and mr. Birling spy – we made it incredibly easy.

We’ve created these iOS and Android frameworks that let you build native, hybrid apps, around Herter, building, spud and they’re, designed for what we call the hybrid sweet spot, which means that in your native app your view, content is mostly HTML, but the surrounding navigation controls are native And when you want to go fully native, you can do so on a per URL basis. You get to decide and you get all the performance benefits of turbo like turbo links in your hybrid app, but it’s even more efficient, because in a native app we use a single shared web view that we automatically move around from screen to screen.

So not only do you avoid reloading assets, reestablishing WebSocket connections and so on on every page change you also lose use less memory and, by the way, a native animation transition from one view, controller to another is about 350 milliseconds. So I’d like to show you how easy it is to build my OS app using turbo links, so I’m going to do that live, but actually it’s not really live they’re recording articles! No, but it’s not edited so right.

This is base camp 3. It’s running on my development machine, so it’s not as fast as it would be in production, but I’m just going to click around for a bit. You’ll see turbo links in action here and, as I mentioned before, we use we have fully responsive views in base game 3. So when I resize the window, you’ll see what eventually becomes a phone you and this lets us share all of our views across all of our platforms.

Alright, so now, let’s open, Xcode and we’ll create a new project. Sorry I need some music. Okay, we’re going to take everything out of this file, I’m going to start with a totally bare-bones iOS app. The only thing will happen here is the application did finish. Lodging callback alright, so we’re going to set it up as a UI navigation controller app and for those of you who aren’t familiar with iOS building, iOS, apps and UI navigation controller is basically the stack of views that you are probably familiar with from any iOS app Right, I’m going to create one and assign it to the Windows, room controller and I’m going to build and run the app just to make sure that it works.

It’s not going to look like much of anything there. It is alright, so I need to change the setting now and that’s because iOS 9 ships with a feature called application Transport Security – and this is a setting that’s on by default and it prevents you from accessing any non HTTP websites, basically so for development. We want to flip that off so I’ll just run this command and do that and obviously you don’t want that in production and now I’m going to go and just drop the turbo links framework in the app and in a real app you’re, probably going to want To use cocoapods or Carthage or some other package manager to get turbo links in there, but for now this is the simplest way and finally I’ll import the turbo links framework, okay and we’ll compile and run ok, it still works.

No we’re going to point turbolinks to our web application and so to do that. We’re going to create a session and the session is a turbo, is session, comes from the turbo links framework and it works streets. Moving that webview around from controller to controller we’re going to define a function called visit, it accepts the URL and what this is going to do is create a view controller. It’s going to push it on to the navigation controller stack and it’s going to tell turbolinks to visit, which means a little bit webpage and now we’re going to need to make an initial visit.

When we start the app right, it’s got to load something to begin with, so I’m going to grab the Basecamp development URL and we’ll visit that all right now moment of truth. There we go, you can see. We have native controls around the webview, but now tapping on a link doesn’t do anything and that’s because we need to handle visit proposal. So, in order to do that, we need to set ourselves as the turbolinks session delegate and then we need to implement a method, and this method just tells the application.

What to do when turbolinks receives a link. Click event. So we’re going to implement that method now session did propose a visit to URL and we’re just going to tell it to call our visit method that we already wrote and we’re going to compile and run, but we forgot sin to implement our error handling method. This is very important to implement, but I’m just going to step it out for now and let’s try tapping a link.

Oh there, it is, and so we had a native transition. We have two actual native view. Controllers looks and behaves like a real iOS app, but the content of the view controller is rendered as HTML. You can see it’s very fast, navigating backwards. You can even perform the interactive pop gesture here, so you’re seeing the webview on front and a screenshot in the back. I mean the energy all about for you, you get pulled to refresh, but you can see that this doesn’t quite look like.

We really wanted to right. We’ve got all that administrative debris left over from the web app, but we want native navigation and so what we do at Basecamp is we check we check for a custom user agent in the web app and we use that to selectively disable some UI elements, and It’s very easy to do so. I’r just going to go and import the WebKit framework here and make a change the way I create this session, I’m going to create a configuration, object, assign this property pass it to the.

What to the session initializer and now we’ll run the app, and now you can see it’s looking more native, we’ve lost the navigation bar at the top, some some buttons when I now, when I navigate through some of these news, look like they could be real table View controllers but of course we’re doing this with much less effort, and so this is the core of our strategy right. I just built a fully functioning iOS, app wrapper for Basecamp in under 10 minutes and as native navigation, and I think the most impressive part of this is that I have full coverage of the app all 200 plus screens are accessible from this wrapper in 30.

Lines of code and when we roll out a new feature in the web app in many cases, we don’t have to make any changes to the native apps and we don’t need to wait on App Store approval, because when we make a hybrid app with turbolinks, we’re Essentially, making a custom browser, we get baseline coverage for all the apps functionality and we can progressively enhance the native controls surrounding the webview right.

This is how the web is built. It’s progressive enhancement now we’re extending that from the browser to native apps and what that means is. We can spend our time building high fidelity UI, where it its most valuable. Instead of going through the groundwork of recreating each screen natively on every platform, we can even go fully native on a per screen basis, and this is what we do in the in the official Basecamp iOS app.

It has a native home screen. So I’ll show you here, instead of taking you directly to the activity page, we have a native jumping-off point, and this is just using API requests to populate the table view. But as soon as you click something then you’re taken into a turbolinks web view, everything precedes from there, you can see. Also we have native navigation controls in the title bar we’ve implemented.

We extract some metadata from the page in order to generate this title. We have custom navigation buttons, so all this is native what the actual application is in HTML very fast. We even support 3d touch using turbolinks. So, yes, you can do all those things that you expect and we have native menus and again we extract this content from metadata on the page. It’s very easy to do so. That’s a look at what turbolinks 5 gives you new rails 5 projects.

Have it enabled by default it’s also now available on npm as a standalone, javascript library. So if you’re not me using the asset pipeline – and you want to use it, it’s available to you there and it has no dependencies and it’s not for real, not just for rails apps anymore, it’s open to everyone. We have all new documentation on the github project. Page which we’re very proud of the ios and android adapters are exhaustively documented as well, and so, if any of this speaks to you, I encourage you to check it out and there’s.

One final note I want to make before I go. It’s important to note that turbolinks is not about avoiding having to write any JavaScript. Ok, your application will need JavaScript. Nor is it about having to avoid learning native development. Any more than active record is about avoiding how to write sequel, you’re still going to have to know how to do these things, but what turbolinks does is it gives you a baseline for good performance with minimal effort which lets you spend your time.

Choosing what to optimize and of going all-in and recreating everything from scratch? That’s if you, if you take home one point from this talk, that’s what I hope it is choose what you optimize. Thank you for coming. The question is: why is it so-called turbolinks if we rewrote it the so we we decided to call it turbolinks five, because the core idea is still the same, but it’s a pretty massive shift and we wanted to sync the version numbers up with rails five.

I believe your question is: is this compatible with jQuery UJS techniques? Yes, it is this only. This only intercepts link clicks that would otherwise fall through to the browser as standard navigation. The question was: does this support offline applications and turbolinks does not have support for offline, but this should be possible to build with service workers on top of turbolinks. The question was, could you do this in Ruby motion and, yes, you could? You could probably also do it in reacts native, because the turbolinks webview is just a standard cocoa component.

So you can. You can work a straight it with anything that they can talk. Cocoa question was: does the webview wrap wk webview? Yes, it does inspectable. You can go in through Safari and inspect the web application as you’re developing it, whether the whether you have a phone connected to your computer or whether you’re running in the simulator, and you get all the performance benefits of WK webview.

So you get the the full JIT JavaScript engine question is: do you still need to write, jQuery load event, handlers and you need to you need to re-architect your the way you initialize JavaScript on the page. So we provide a series of events. There’s a turbolinks load event that fires on every page change and then in their inner documentation. We have some further recommendations for how to approach that.

But what I really recommend is using mutation, observer or custom elements. These are html5 api’s and they give you callbacks for when elements are attached and detached from the Dom and if you use, if you use these api’s to install and remove JavaScript behavior, it will work transparently, whether you’re using turbo links or not. And I think it’s a much better approach question is there? Is there an API for rendering just a section of the page rather than the whole page, and there was a version of turbolinks called version 3, which is in development for about a year in half and never officially shipped, and it had a feature called partial page Updates and it’s expanded, the turbo links API in you know in a way that didn’t seem proportional to the amount of value it provided, and I also just think partial page updates in many ways are orthogonal to the navigation.

So my recommendation for now is to use an sjr technique so just a create or like a jst RB response and combined with a UJS remote link to update parts of the pages as necessary, as well as for handling form submissions and stuff like that. But in the future we might explore adding official personal page updates API yeah. The question was, if I can summarize, if you’re rendering hundreds of partials in a single response disturb the links handled that well and the answer is it’s going to handle it as well as the browser would really, if you’re, if you were sending that over a full Response but my suggestion would be one don’t return, so many results at a time, use it in imagination and their techniques like that to to load content and on demand, and two would be to consider writing that in client-side MVC.

It’s it’s totally fine to do that, and we do that in a lot of places. I think the difference is doing that in a component based way versus saying you’re, going to write your entire application. That way, right it’s much easier to to optimize something by just looking at it as a component than it is to throw everything out and write the whole thing twice on both sides. A question was: what’s the upgrade path like from older version of turbolinks, and now we don’t.

We don’t have a good guide for that right now. Some of the all of the event names have changed and there are some some events that went away but we’ll be documenting that before the final rails, 5 release. Yes, any plans to support Windows Phone. I don’t have any plans, but if someone wants to look into that, you can take a look at the way we built the iOS and Android adapters. So there’s a JavaScript component, that’s bundled with each one that basically communicates with the webview over a bridge.

So you could take the same approach on other platforms. We’d love to see that happen. The question was, can you can you use turbo links to access, GPS, camera and so on? And the answer is yes, of course, because you’re you’re still writing a native app okay. If you want to get that over the the webview you’ll have to use a bridge, the bridge, that’s provided by the operating system, but it’s it’s really worth orthogonal to the turbo links.

You would build the native UI yourself and then you would use the platform specific webview rich to get that into your application. If you wanted – or you could consider – using, for example and JSON API – to communicate with your application. If it’s simpler, all of those things are possible. The question was: if you want to access sequel Lite, how would you do that from a web page, local sequel, Lite? You would do that over the JavaScript bridge, that’s provided by the operating system.

So it’s in that way, it’s not any different than using a normal web view. Question was: how does a out of the native adapters handle request, failures and they’re they’re? All of all request failures go through a single method handler, so you choose how to implement that. In your application, my recommendation is to show a native view that gives you basically a reload button or retry button. That’s what we do in Basecamp all right.

Thank you. So much for coming!


By Jimmy Dagger

Find out my interests on my awesome blog!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.