 Thank you, Marty. I'm so excited that Ruby Central has announced that RailsConf is going to be held in Seattle next year. My hometown. So I don't have to drive the three hours to Portland. All right, hold on a sec. My computer fell asleep here. There we go. There we go. There we go. OK, so we have one thing I got to take care of first. Someone in the audience named Devin, he is down here, he brought this turtle with him. It's his daughter's turtle, and he asked me to take a photo of it in front of all of you. So I'm going to do that now. But I would like you all to clap for the turtle, because this is the turtle's biggest audience ever. So please. All right, thank you. Thank you. The next thing is that, oh, crap, hold on. I'm using up all my time. Hold on. I got to get a cell. I'm trying to remember all of these. OK, OK. Now it's my turn. All right, everybody. Yay. OK, I heard that there's also a bingo thing going around, like a bingo card thing going around. And I believe it's unauthorized. But I'm going to read out a few of them here. Let's see if we can get a bingo going. Let's see. Common backgrounds. What an ungrateful bunch. I was born into the software community through the grace of open source, a land where milk and honey flow eternal. Any bingos yet? No? OK. All right, well, I'll continue. I built some more of these into my talk, so hopefully you can get. Thank you for laughing, Evan. I don't know where you are. I can hear you laughing, though. All right, so every year it seems like David gives the very first keynote of RailsConf, and I end up giving the last keynote at RailsConf. And typically my strategy for this talk is essentially to get on stage and make fun of David and his Danish privilege. But the problem is, like, I was thinking back to myself, like, OK, I do this every single year, but the issue is, does it scale? Any bingos yet? But I wanted to declare that this year is a jubilee year. The year I will not make fun of David in my keynote. So I'd like you to look at all the things that I am not doing. Boy, OK. So this year I decided to do something different. This year I decided to put all of my thoughts down on paper. And rather than standing around here rambling at you, I thought I'd try to do something a bit more thought provoking. So I've written a script, and I'm going to read it to you. Hold on. Shibang slash user slash bin slash ruby. Def fib parentheses n parentheses. If n is less than 3, 1. Else, n parentheses n minus 1, close parentheses, plus fib open parentheses n minus 2, close parentheses. End, end. Yes, that was my ruby script. So I actually make fun of David a lot on stage, but I actually really like him and admire him. The other day I met with him at the speaker's dinner before the conference, and I actually got him to sign a copy of his book for me. And this is me getting a signature. I was very excited. So yes. All right, let's actually start the presentation now. I am calling this talk, but at what cost? Thank you, Eileen. She gave me the title, yes. My name is Aaron Patterson. I'm really, really happy that I could be here in Minneapolis to give a talk to all of you. You could say that I'm really happy to be presenting to you live from here. I know there's like six people in the audience to get this joke, but it's really, yes. It's important to me. Those six people are very important to me. Anyway, I have a cat. His name is Gorbachev. Gorbachev Puff Puff Thunder Horse, the third. I'm so sorry. This is another picture of him. He has an extremely flat face, which I think is extremely adorable, but it also means that he can't go outside. Otherwise, he'll probably die because there's no way he can defend himself. This is my other cat. Her name is, oh no, C-TAC, airport, Facebook, YouTube, Instagram, Snapchat. Sorry. I should have thought of this in advance. I apologize. So she likes to stick her tongue out a lot. And there's another photo of her. She sleeps next to me while I'm working. And I was like, how much is she sleeping? Like how deep is this sleep? So I decided to, so I mean, honestly, it kind of makes me mad because I'm sitting there working and she's like sleeping right there. And I'm like, why am I working so hard? Geez. Anyway, I brought stickers of my cat, both of them. So if you would like a sticker of my cat, come say hello to me afterwards. I like to bring stickers because I know that people are shy and don't know what to say to me. So you can ask for a sticker if you don't know what to say. Also, I am into Ruby programming. I also like taking selfies. And I am a member of the Puget Sound Mycological Society. I am a mushroom hunter. It is true. So anyway, my name online is Tenderlove. If you don't recognize me in person, this is my avatar is what I look like online. And I was looking through my Twitter like the information in Twitter. So you can look at the advertisement information, like what it is that they advertise to you. And one of the sections in there is like, it is, what are you known for? So what are you known for online? And it tells you, and I was very amused at what I am known for. I am known for software development and other. And I feel like other may be puns. And if that's the case, then I've really nailed the other category. But I'm not totally quite sure. So anyway, speaking of Twitter, I want to tell you a story of how I accidentally made Twitter completely useless for myself. I typically use Twitter for just telling bad jokes online pretty much. And sometimes I'll tweet a tech thing or two. But typically it's just, excuse me, not bad jokes. They're super good jokes. I only tell super good jokes online. Anyway, I'm pretty sure that Twitter, like Twitter can only get users to have basically one of two feelings. And that is either anger or facepalm. And I typically, like I prefer to generate facepalm. So that makes me really happy. I tell puns online. And I like to imagine that there's somebody out there reading that, just going like, oh, god. And I sit back. And I just go, yes. Anyway, people started responding to my puns with just my name. They would just say Aaron. And so every time I tweet a pun, this is all the responses. This is all of my mentions. Just say my name. And then it's gotten to the point where when other people, if my friends say a pun, then people will respond to them with my name. And actually, one of the speakers here, Will, he put together a bot that all it does is a certain percentage of my tweets just responds to them with Aaron. I've got 50 minutes left, so I think I'll tell this story. But Will, this bot has been replying to me for like a year. And I didn't know who had written it. Nobody told me who wrote this bot. Now I was at a conference recently, Keep Ruby Weird, where Will was giving, yes, Keep Ruby Weird, where Will was giving a presentation. I was the emcee at the conference. And Will was talking about all this crazy stuff that he did. And one of the things he talked about, he announced at this conference where I was emcee that he is the one that wrote this bot. And I'm pretty sure I was just like, what? Anyway, I want to share one more response to me, which this is probably the most high effort Aaron response that I've seen so far, and I want to share it with you. This, okay. So this, I think this was the best, the highest effort response to me so far. But this only made my mentions approximately 54% useless. So until one fateful day, March 14th, 2019, I made a very huge mistake. And that was that I tweeted this. I am trying to respond to things with, but at what cost more frequently. And of course the very first response was, but now the other 46% of my mentions are essentially this. Fortunately, nobody yet has built a but at what cost bot. But my Twitter is 100% useless at this point. I mean, I think you could make it, like you could make a strong argument that Twitter was already useless, but you know what I'm saying here, right? Like, you get what I'm saying. Unfortunately, I've gone from being the one who imposes face palms. I'm no longer the face palmer, I am now the face palmie. Which is kind of unfortunate. But we have, there is an algorithm to this, and essentially the response algorithm is, if I say something that's a pun, you respond with Aaron. And if I say something that is not a pun, you respond with but at what cost. And I would like to do that here today with all of you. So let's practice doing this. So let's do this. I'm gonna say something and you have to respond with the correct response. Okay, people keep telling me I should try Docker, but I'm not a fan of business casual. I'm glad they're ending Game of Thrones instead of just letting it drag on. I love that one. Okay, I love speaking at RailsConf and I'm so happy that I could be here today. Okay, thank you. So I work for a company, a tiny startup called GitHub. I'm a software engineer at this company. You could say that this is my active job. 45 minutes of this left. Last year, my GitHub was acquired by a late stage startup slash lifestyle business based in Redmond. The question was but at what cost and I will tell you what the answer is. I don't know. Many people, so many people have asked me like has anything changed? And I can tell you that the only thing that's really changed is I changed my desktop background and I changed it to a picture of Redmond which is if you've never been to Redmond before, it looks like this. It's really beautiful. Anyway, please now pay attention to the obligatory GitHub We Are Hiring Slide. Now I think that most companies at this conference have We Are Hiring Slides but I'm pretty sure that GitHub can provide something that no other company can and that's essentially access to all of my unreleased puns. The cost is you get to face palm every day. And if you don't believe me, here's one of my co-worker is tweeting. She quoted me from, this is from Slack. So one of my, I have to admit one of my most favorite things is that I will type a pun into Slack and then it's just the best. Like it'll just sit there. And I'll see like typing and then no typing and then typing and then no typing. Like five minutes later, somebody says something else completely unrelated. I'm like, my work here is done. So if you would like to come work with me and also face palm, then you can go to this website or scan this QR code or come talk to me later. Anyway, I'm also on the Ruby Core team, which is a team responsible for developing the Ruby language. I recently learned that this is my, actually my 10th year. So it'll be my 10th anniversary in October of being on the Ruby Core team, which is amazing to me. I'm also on the Rails Core team and I decided to research how long I've been doing that. I've been on the Core team since 2011, but my first commit was in 2009. So it was actually about 10 years ago now. And Jeremy is the one that merged it. So thank you, Jeremy. I really appreciate it. Now I'm not saying this to like toot my own horn or boast about how long, you know, my accomplishments or whatever, but what I want to talk about is how long I've been in the Ruby community and why I stayed for so long. And the reason is that I just, I love Ruby a lot. I love the language. I've been doing development work since 1999 and I got into Ruby programming around 2005 because some of my coworkers went to the No Fluff Just Stuff conference where Dave Thomas was giving a talk about Ruby. They brought it back to our office and showed it to me and I really enjoyed it. At the time, in 2005, I was a reluctant Java programmer and programming in Ruby felt like a breath of fresh air to me. The language was so easy, everything just worked. It worked the way I thought it might work. When I made a mistake, it was easy to tell why, like the exceptions were clear. The basic patterns of the language were very easy to pick up. So it required much less mental overhead than, say, Java. So I didn't have to think about as much stuff when I was programming and I could just focus on getting my job done. It was also nice that there was no boilerplate code and to give you an idea of what it was like to write Java back then, this is an example. On the left is Java and the right is Ruby and they're essentially doing the same thing which is just mapping over a list, trying to convert a list of strings into a list of integers. Now, I don't think the Java example here actually works because when you do a parse int, it actually returns just an int primitive rather than an object and you can't push that onto this array. So you have to think about stuff, not everything is an object and you have to think about that stuff and that's the type of mental overhead that I'm talking about that Ruby doesn't have. So I wanna tell you a story about my very first Ruby program, my first serious Ruby program. In 2005, I wanted to see Lord of the Rings. So it was the third Lord of the Rings movie coming out and there was going to be all three Lord of the Rings movies all shown on one day and they were showing it a day early and that was my birthday. So I really wanted to go to this. Now, I went into work and I tried to purchase the tickets online but the website just crashed and I tried a few times and the website kept crashing so I decided, okay, I'll write a Ruby program to do this. Like I said, at the time I was a Java developer and our project took about like 10 minutes to compile. So what I would do is during the compilation time I would write my Ruby program to go buy these movie tickets for me and I even put my credit card information into this script. Like I literally put my credit card number in a text file and saved it on my hard drive. And it was not encrypted. Anyway, so the program would make a request and try to purchase the tickets for me and I knew what the failure state was. Like I knew that the website was failing and what I would do is, okay, if the website fails I'll just retry but I don't know what the success case was so my thought was, all right, well, it's probably gonna be a failure but a different type of failure so what I'll do is just log a message and then retry. Right? So it never ended. It was an infinite loop. I figured I would know it but at what cost was a very good, that is a very good question and we will get to that. So the program was running and I forgot about it and was going about my job and then later on I noticed that the output had changed so I killed the program and it had changed quite a while ago. So I went to the website and I tried to go through the process and I realized that it had actually been succeeding a whole bunch of times. So I called the credit card company, like I had a mini panic attack and I called the credit card company and I asked them one of the most shady questions you can possibly ask. I'm like, how many times has my credit card been charged today? I feel like that's something you should know but I didn't. Credit card person says, your card's been charged once for like 40 bucks, something. So it's like, great, I only one transaction, great. But I wasn't sure whether or not I actually got the tickets because the website had kept failing so I called the ticketing company. The ticketing company, like I told them my information and I said, hey, I've been trying to buy movie tickets and I'm not sure, like the credit card company said I was charged. Did I actually get any movie tickets? They looked up my info and the guy says, well, yeah, you bought two tickets but it looks like we've tried to charge your credit card hundreds of times. And I was like, oh yeah, I just kept hitting like refresh on the website. I don't know. Anyway, the moral of the story is never write infinite loops. Make sure there's some way for it to exit. Anyway, around the same time I discovered rails, like that time was when DHH released the Make a Blog in 15 Minutes video and I tried out rails and I fell in love with it. It had exactly the same features as Ruby. It was very easy to use, everything just worked. It worked the way I thought it would. I could build a website really quickly. I could focus on my actual job and there was no boilerplate code. But it wasn't just these specific traits about the language and framework that made me stick with it for so long. It was that there was a kind of community that valued this sort of stuff. It's this type of thing, like we want the computer to do the work for us. This type of value is the type of values that I want my community to have and the people that I work with to have. It's the situation that I would like to be working under every day. So I decided at that time I have to become a Rails programmer. I must write Rails for my job. This is an actual photo of me from 2006. I looked through there. I have much less gray hair than. Anyway, I knew I had to become a Rails developer but at the time nobody was hiring Rails developers. However, I knew I had to become one and I know what you're asking yourself. Man, no, I had to prompt that one. There were so many other unprompted ones. Anyway, I will tell you exactly what that cost was. It was minus 25% of my salary. So I took a 25% pay cut to get my first job programming Rails but I was in a place where I was more happy and I knew I wanted to be more involved with Ruby in the Rails community because I wanted there to be more companies that had Rails applications. I wanted there to be more competition so I could find a better job so I could get a better salary so I could work around other people who shared the same values that I did. I wanted more people who are in the community as well. People who also shared these same values and this is the type of stuff that kept me motivated. Now, I speak at a lot of conferences and frequently when I go to conferences, people will come up to me and say, thank you for the work that you've done on OSS. Thank you for the work you've done on Ruby and Rails. Those things and the story here is essentially a long way, a really long way for me to get around to saying actually thanks to you because you are all the ones that are building companies using Rails. You're all, you're the ones programming in Ruby. You're the ones creating demand. You're the ones that are enabling me to actually be able to do Ruby as my day job. So if it weren't for you, I wouldn't be able to get a job writing the language and framework that I love so much. So I want to say thank you to you. Oh. The question is, but at what cost? I will tell you, let me give you the answer. The answer is endgame spoilers. I am going to spoil endgame. This is my endgame keyboard. And I know you're asking it. I will never buy another keyboard. This is the best keyboard in the world. And I know you're going to ask me, but at what, I will tell you the cost. It cost me everything. Seriously, if you don't get it, don't get into mechanical keyboards because they're extremely expensive. So all right, so now that we've spoiled endgame, it's time to spoil Game of Thrones. I mean, I don't even watch Game of Thrones. I don't really care whether or not it gets spoiled for all of you. So I'm just going to let you all know that Dumbledore dies. I'm just kidding. I've never seen Star Wars. So now for the technical portion of my presentation, shifting gears just a little bit. All right, let's do, let's talk about some cool performance tips. Like I know we're all here to actually get some, you know, get some value out of this conference. Besides me wasting the last hour for AilsConf. Let's do something productive. So I'm going to give you a few cool performance tips that these are performance tips that you can use on any Ruby application or Rails application. It'll work for either of those. The first tip is that emitting parentheses is actually faster than having parentheses. And if you don't believe me, this is what I'm talking about. So I prefer the style on the right versus the style on the left. Now these two pieces of code have exactly the same functionality. Just one has parentheses and the other one does not. Okay, now I can tell you that the one without parentheses actually runs faster than the one with parentheses. And this is actually very easy to intuit. Like I will give you a logical reason why that is. We've all heard this adage that no code is faster than no code. Thus, if we omit the parentheses, it must be faster. But okay, if you don't believe me here, use this command, Ruby dash Y. This is a flag that you can give to Ruby that will output the state of the parser while it's parsing some code. So we can actually see how many transitions the state machine makes while it's parsing your Ruby code. And if we pipe that to WC dash L, we can tell how many states the parser had to go through while parsing it. And it turns out if you have parentheses, it has to go through more states. So clearly it must be faster. Sorry, I'm just pushing my own coding style on all of you. Let me give you the next speed performance tuning pro tip. And that is that single quotes are two times faster than double quotes. At what cost? I'm making the entire Rails Conf audience mad at me. It's fine, I don't mind. So I'll tell you why single quotes are actually two X faster than double quotes. And the reason is because you don't have to hit the shift key. Just think how much time I've saved you right now. All right, so I'll give you an actual performance tip for Rails 6. David didn't talk about Rails 6 in his keynote. And I am going to now, and this is one of my favorite things. It's a very, very small thing that's in Rails 6, but it's one of my most favorite things implemented so far. And that is if you take a look at the output in Rails 6, if you upgrade and look at the output, there is a new thing in the output which shows the actual number of allocations that any particular template has made. So this is an actual pro tip for all of you who are going to write Rails applications today. I didn't wanna just troll you the entire time. So there is something that is actually useful. And those allocation counts are actually cumulative, so it's the number of allocations in self plus children if you're wondering what that output is like. Kinda similar to the time, so you can see those in the logs. So I wanna talk a little bit about GitHub at Rails or Rails at GitHub, bleh. I'm on the Ruby architecture team with Eileen. Our team mostly works on open source libraries, Ruby and Rails included. We focus on extracting parts of our application that are not core to the business and things that we could open source and push back upstream. And today I wanna talk about two of the things that we are working on and currently trying to get upstream and that's template pre-compilation and method name generation. Template pre-compilation is essentially a technique where we say we compile all ERB templates or all of our view templates as the application boots rather than lazily compiling them, which is what Rails does today. Method name generation is a little bit harder to explain. This is a compile time optimization. And I'm not gonna explain it too much in detail. We'll get into it later in the talk because I think we need to build a little bit of background before we actually get to this. And in order to do that, we're gonna build a template renderer. At what cost? But at what cost is your saying it? No. So we all know that Rails is MVC and for those of you that don't know this, Rails is MVC. The way it works is when a request comes in, the request is handled by a controller, the controller gets data from the model, passes that data to the view, the view goes back to the controller, and we send that back as a response. Now, at one point in my career, I actually worked for a company that worked on a system for blocking pop-ups. It was a pop-up blocking company. And there we developed a system that was actually known as a modal view controller. Your time, and that is... Your time is the cost. I'm sorry. Anyway, so we're gonna be looking at this part of Rails. We're gonna look at ActionView here. I typically talk about active record, but I'm going on record as talking about ActionView. Sorry. As I said, we're gonna talk about ActionView. And ActionView's responsibility is for finding templates, compiling templates, and executing views. So we'll talk a little bit about view templates. The view templates are implemented with a templating language called ERB. This is specific to Rails, but ERB, like this, you could be, we could be talking about Hamel or any other templating language, really. But we're gonna use ERB in these examples. This templating language was developed by Emseki. It's part of the Ruby standard library. And ERB templates look like this. I'm sure you've seen them all before. And the way that you actually compile and use these is with code like this. You basically just, this example here just compiles the template. That second line there prints out the source of the template. All the ERB compiler does is take your view as input and translate that into Ruby code. And then we evaluate the Ruby code and print out the result. So if we look at the compiled values, this is what the actual compi- the generated Ruby will look like. Now, if we execute this code, it's not gonna work. And the reason it won't work is because we don't have a render form method or a link to method. So if we implement some very simple versions of those, we can actually compile and run this ERB template and the output will look like this. So we can make it work. Now, there are two tags that we looked at in here and we're gonna talk about a strange and not fun feature and challenge of this. Template language. Here are the two tags that we saw. The first one evaluates some code and prints it out. And the other one, it just evaluates some code. So it doesn't print out the return value of that. So one common thing we do in Rails is we like to capture the value of a block in helpers. So you maybe with form for do or something like that. We wanna capture the block output. So for example, we have a template that looks like this. Now, if we execute this code, what is the output going to be? Is it gonna be hello first, hello in the middle, or are we going to get an error? Well, the answer is we actually get a syntax error and I'm gonna show you why. The reason is because when I capture this template gets translated into Ruby, ERB doesn't know that do belongs to end, so it just does an unintelligent thing and just translates those. So we translate over the capture do and try to print that out and then the end is just evaluated as normal code and we get a syntax error. This won't work. So how does Rails deal with this? Cause clearly we have to use this in our templates today. The way it works is in the ERB compiler, you don't need to read this code. Essentially all it does is it scans your Ruby code looking for things that could be blocks and then treats them that way. So that's how it can detect whether or not do and end belong together. We got a little bit down the rabbit hole here so I think we're gonna step back a little bit. There are actually different flavors of ERB. There's the original ERB that ships with Ruby, then there's E-Rubus, which is what Rails used for a very long time. So Rails started out with ERB. Then we moved to E-Rubus, so it was faster than ERB. Now we're onto E-Ruby, which is another implementation. These all essentially do exactly the same thing, like you can upgrade Rails and never know that you have switched between any of them, just that one is more performant than the other. So E-Ruby is the current one that we use and it is the most performant. So let's talk a little bit about ERB performance. This, again, this isn't Rails specific or template language specific, but we are gonna use ERB in these examples. So let's take a look at rendering speed here. This is just some code that renders a template and if we time it, our output is about, we're able to render about 27,000 templates per second. And that's pretty good, but we can actually do better than that because our benchmark, what it was doing, was actually compiling the ERB and evaluating the ERB and executing it every single time and we can cache some of that. So in this example, we've changed it such that we cache the compilation. So rather than compiling it every time, we'll just compile it once and then evaluate it each time. And if we compare this to the original version, it's about two times faster. So we're able to render about 63,000 templates per second. So this is good, it's much faster. So we can do even better than that though. We can say, let's only evaluate once. So rather than calling eval every single time, what we can do is define a method. So in this example, what we'll do is we take the source of the template and we actually use that source of the template to define a method. And if we look at the method output, the method will look like, or if we run this example, using the method, the declared method runs about 52, is that right, 52 times faster than the original. So we're able to execute this template about 1.4 million times per second. So let's take a look at method definition a little bit more in depth. So this is a very simplified version of that benchmark, but essentially what we have is the template source here. And in order to generate a method with this, we just evaluate it, we compile the source, we calculate a method name for it, and then call eval to define the method, and then we just essentially send the method. So all that stuff before, we only do that once. So this is what the compiled source of the method looks like. It's just the source that the ERB template handler generated. So we just take the output from ERB and define a method with that as the source. So we can say generally that templates are translated into methods. This is exactly what Rails does in your applications. All of your templates are actually generated into a method and evaluated onto a particular class. So given this knowledge, if we have a template that looks like this, we can actually print out all of the methods that are defined. So if you put this template into your application and then look at the output and the logs, you'll see a bunch of methods that look like this. And these are the generated methods. These are the methods that are generated from your templates. Now with this information, we can also start calculating the size of these methods as well. So we can calculate how much memory each of these templates are using. You can do that like this. This is an example. We just get the method and then we can check the size of the method. So let's take a look at template handling in Rails. Up until this point, everything we've seen has just been Ruby's standard library. None of this was specific to Rails. But it builds a foundation for understanding some of the behaviors of Rails application. So from here, I want to talk a bit more specific about Rails. So first, let's take a look at instance variable visibility. Now these are two separate templates, but both of them are able to see the same instance variable. The question is, why is this? And if we think about the way that templates are compiled as being turned into a method and then put onto a class, the answer is actually pretty simple. If we expand these out, we'll see that these two methods are on the same class. And of course, methods on the same class can access instance variables on that class. It is just a regular class, and those are just regular methods. They just happen to be your templates. So one thing that's kind of bothersome about this is it means that you can't really tell which template, since you can't see this class, you can't tell which templates define which instance variable. So one thing that we actually do at work is we don't allow instance variables in our templates. So don't use instance variables in your templates. Now I'm not saying don't run out and change your entire application to remove all these. It's just kind of confusing, because you don't know who defines what and in what order they need to be run. So you can actually quote me on this. Don't use instance variables in your template. Just quote. At what cost? I don't know. Not much, really. I think you do it if you want. I just wouldn't. So another instance here is, oh, we already complained about this, another issue with this is local variables. So we typically pass local variables to templates. And the way that you pass local variables to templates is with code like this. Sure you've seen it. We just pass a hash there. And in this particular example, we're passing a local variable with the name. The local is named name, and the value is gorby. But we have kind of a problem. If we think about the way that these templates are compiled and evaluated, we know that they're turned into methods. But if we take a template and this template content and turn it into, like, look at the source for that, we'll see that it calls this name here. But the generated method is a method call. There is a method call inside there. We don't have the name parameter passed to that method. So how do we get those? How do we get the locals passed into a method like that? The way that Rails deals with this is that we define a preamble that contains the locals in it. So if you look at the method that's generated, the code for the method that's generated, it will actually contain this preamble up at the top that defines the local name. So it takes that hash and defines it. So unfortunately, this leads to a couple different problems. And that is, let's look at this example here. On the right is our content template, and on the left is the template that's trying to render the content template. If you look at the content template, can you tell me is name a local variable or is it a method call? And unfortunately, it depends on the context. In this first case, it's a local variable, the way that it's being rendered. But in this second case, it's actually a method call. So the second case will get a method missing error where the first case will actually print out the name Gorby. Now, unfortunately, this means that we can't compile templates in advance because we don't have the context within which the template is supposed to be compiled. We don't know what the local variables are supposed to be for a particular template. Now, this can lead to another problem, too, which is that we can accidentally compile a template too many times. So here's another example where we have two templates there are two render calls. They're both rendering the same template, but they're passing in different locals' hashes. And that means that we have to define multiple preambles. So we end up defining a method for this template twice. And this is sad because we have to use extra memory for these templates. And also, the friend local is never used, and I like friends. Aaron. So you may be saying to yourself, Aaron, this is nice. But what should I do about this? And my pro tip for you today in your Rails applications now is to make sure that you always pass the same locals to individual templates. Try to make sure that you're consistent in passing the shape of that local's hash to each template. And that will ensure that your templates are compiled multiple times. So let's take a look at the render function, too. The render function at a very high level, this thing just renders templates. It's responsible for finding a template, compiling templates, calculating a method name, and calling the actual method. And if we break this down into a diagram, like a logic diagram, it looks something like this, where we have to find a template, we check to see whether or not the template is compiled. Sometimes we have to compile the template and then figure out a method name, and then we call the method. So I want to talk a little bit about finding a template. We've seen most of the other pieces of the puzzle for rendering things and calling methods, but let's look at finding files. To do that, I built a little test application. Now, unfortunately, when we render templates, the template rendering depends on the requested format. And let me show you an example of what that means. So here we have a user's controller, and it renders an index template. And we have two templates there, an HTML template and an XML template. Now, here's the contents of each of those templates. One is just XML, the other one is just users. And here is a table breaking down the responses for particular requests. So if we request with XML, we'll get the XML template. If we curl with nothing, we'll get the HTML template. And if we curl with HTML, of course, we get the HTML template. So to know which template needs to be compiled, we need to know the requested format. This means that our cache keys are something like local variables. In order to figure out the template, we need to know local variables. We need to know the format, the locale, the variant. Now, some of these things we could know in advance, and it would be nice if we did, then we could actually start pre-compiling these templates. So a strange, I want to talk a little bit about a strange render behavior as well here. In order to demonstrate this, I made a little sample application as well. Here is our user's controller. I've added two templates. One is a ping, and the other one is an XML. So we have the same controller, but the views are slightly different. So here is what our templates look like. We have one template that all it does is it's our main index template. It renders my template. And then the ping template just says, I guess this is a ping. And the XML template says, XML is cool. So let's do the same request thing that we did before, same request table. And the interesting thing is that if we request XML, we'll get an error, because there was no XML template. If we request text HTML, we get an error, because there was no HTML template. If we request that the browser, we get the same thing. But if we do a bare curl, for some reason, it renders the ping. That is neat, I think. Here is the browser error, just to prove I did it. So yes, this is our outstanding one. For some reason, a bare curl will render a ping, and I personally think that is a bad behavior. So in the end, what this means is, if we look at this particular template, we can't predict what this call is going to do. So which template will this render? We don't know. OK, I'm going to make it a little more annoying. So let's add respond2 to the controller. We've changed it just a little bit. Before we had just a bare render, now we're going to do a respond2 with HTML, and we're going to do exactly the same tests we did before, the same curls, and see what the responses look like. In this case, it's completely consistent. We get errors across the board. Even a bare curl gives us an error that we're missing an HTML partial. What this means is that that render call is actually context dependent. When we call this render here, it's not just dependent just on the format that's being passed by the request. It's also dependent upon the context within which it's being called. So this render is impacted by that render above it or this render above it as well. It means we can't look at this template and know what it's going to do without having a particular context. It means these templates are not context-free, right? So I've been harping a lot on prediction and consistency, and I want to talk a little bit, with regards to the template rendering, I want to talk a little bit about why. The reason is because we're going to go back to this diagram, and after doing performance testing on our system, we found that testing whether or not a template was compiled is rather cheap, and that calling a method is rather cheap, but the bottleneck here is actually for calculating the cache keys. So these things are cheap, but calculating the cache key is actually very expensive. So it would be nice if we didn't have to calculate that cache key, and unfortunately, when we look at this, we don't know what it's going to render, so we can't calculate the cache key in advance. So what would be nice is if we knew what this was, we all know after going through developing an ERB renderer that everything just boils down to calling a method, but the question is what method does this thing actually call, and we don't know. What would be nice is if we could know that, we could translate these directly into method calls and skip this cache key altogether. So unfortunately, we don't know what that will do, and if we did, we could actually eliminate a whole bunch of this diagram. We could eliminate this finding a template since we are able to do it at compile time, and actually, we could eliminate all of this at runtime. We would have to do none of it. Okay, spoiler, spoiler alert. Unfortunately, if we did this at boot time, it would mean that we would get a slower production boot time, but we would have overall lower memory because we could have memory usage because the parent process could compile in advance and then do that before it forks any children. We could also have a faster runtime if we were able to do that because we would have no more cache check penalty. So I'm gonna take a quick detour here. I wanna show you some really odd behavior I ran into while debugging this. Here we have three templates. On the left, the left two templates are just rendering a collection. This one is rendering the collection. This one is rendering the collection, but saying cached. And this is the template that it's actually rendering. Each of them are rendering 1,000 customers. And the question is, which one of these is slower, the cached or the non-cached template? So people think that the non-cached template may be slower, but actually the answer is, of course, it is the cached one that is slower. Otherwise, I wouldn't be showing you this slide. If you go look at the benchmark, there's the issue for the benchmark. I'm not gonna put it here because it's kind of long. But if we run it, the cached version is actually slower. It's 5.6 times slower. This is, I couldn't believe this. When I was looking at this, I was convinced that the cache didn't work. I was convinced, convinced, convinced it did not work. But there were cache entries for that, in the cache for that particular template. So then I had to wonder, so I profiled the cache and it turns out that cache key calculation was actually more expensive than executing the template itself. So I had to say, okay, where does this actually pay off? And if we look at this, that's the old template, the new template, I had to make it do that much work for them to become equal. If you do that much work in your new template, now it's equal. So they're approximately the same. So what this means is that cache does not mean fast. All right, so let's talk about speeding up templates. Our main weapon for making template rendering faster, but while maintaining developer ergonomics is essentially doing static analysis on our templates. We've seen so far that it's hard to get some of the data out of our templates statically, but that's one of the optimizations that we actually do at GitHub. And we're starting to extract this into a gem and hopefully push it upstream. And here is the initial work on that done by John Hawthorne, one of the people on my team. And if you go take a look at this, it is a work in progress, but it is being extracted from a real application. And it allows you to make this call that just pre-compiles all of your templates. And essentially the way that it works is it looks at your ERB templates and it says, okay, in this first case, we know we can pre-compile it, it has no locals. We know we can pre-compile a second one that has locals, but we're able to tell what they are without running. And this third case, it cannot pre-compile the dynamic locals template because we don't know what it is exactly. So we're able to predict essentially what the template source will be for those first two, but not for the last one. So this is two parts of the puzzle. This is pre-compiling the templates, or one part of the puzzle is the pre-compiling templates. The other part is figuring out what method we're going to call. So I wanna talk a little bit about rendering methods and a change that I would like to make in Rails that's slightly backwards compatible. That's essentially the change I would like to make is something I like to call the same format assumption. Essentially what this is is, in this case we have an, is this one ambiguous? Yes, we have an ambiguous template rendering issue here. Where this first one, we don't know what it's going to render. We know that second one will render a ping. Oh, I'm sorry, caption people, I'm speaking fast. If I read the captions, do I go into an infinite loop? If I read the captions, do I go into an infinite loop? All right, so in this case, in the second case, we know we're gonna be rendering the ping, but in the first case, it's kind of ambiguous. We don't know if it's going to be what it's going to be but what I would like to say is, well, since we're inside of an HTML template, we should assume that it's going to be HTML. So a child of an HTML template should also be an HTML template unless otherwise specified. So here is another example with an ambiguous render issue. In this case, what we say is, well, it is HTML, but we don't know where this is going to go. We know the second one is supposed to be HTML. Sometimes this first one can raise an exception and sometimes it will be a ping where we know the second one will always raise an exception because there's no HTML case. Now what we can do is if we make this assumption, I think that we can always optimize our templates. So in this case, we can say, well, we know that it's supposed to be HTML and we know that given the same format assumption, we assume that that child template is supposed to be HTML, but what it would mean is that if you have an ambiguous template, it could raise an exception and you would have to change your code to be specific about the render format that you want. You'd have to say, well, I really want a ping here. I know you're inside of an HTML template, but I really want a ping. This would allow us to optimize every single render call and be able to predict the methods that we're going to call. My theory is that this same format assumption will work for 99% of the apps today. I guess that most HTML templates have corresponding HTML child templates, which means that we could optimize this case. So if we're able to do this particular optimization, what it would mean is that when we actually compile the template, we'll generate those method calls and embed those into the ERB rather than doing the lookup and paying that cash key cost. At what cost? Low, hopefully low. The cost is you need to put format when you're rendering pings. So all right, so we could do another, there's another thing we could do where we have no backwards incompatibility problems, and that's essentially what we could say is, well, in cases where we know that it's ambiguous, we won't optimize it. We'll just render a template that looks like this. We'll fall back to the original thing, and then in this one where we do know what it is, we can raise an exception. So, okay, I'm going to wrap this up very quickly now. So today I've presented some really bad jokes. Excuse me, some really good jokes. You're an hour of your time, I apologize. I've also presented some of the things that we've been working on at work to speed up Rails, and I want to end here with a few lessons that you can take in general for your applications at home, and if you can, always try to be context-free. Don't depend on side effects in your application. It means that when you're looking at a piece of code, you're able to reason about it within isolation. You don't need to know about the rest of the system. You don't need to know who's calling you, or in what context you're being called. You can just look at it and reason about the code. Now, this is kind of the same thing, but be consistent, always write functions that have the same behavior. Calling a function should always do the same thing, otherwise the system can become too hard to understand. Also, cache does not equal fast. Benchmark all of your things before you just enable cache. So try benchmarking it, then cache it, then try benchmarking with cache enabled. And also, please keep building applications. Please keep writing Ruby code so that I can continue to write Ruby code into the future. Thank you so much, all of you. It was a pleasure to be here.