 I'm also known by the name of Grand Maester Nate. I'm an expert in Rails magic. I've forged my links at the Old Town Great Citadel. Just kidding, Rails is not actually magic. DHH does not actually sacrifice chickens in his backyard to make all of the components work. So we're going to talk a little bit about some of that magic today, being the different parts of the Rails framework and how they all fit together when you type Rails new and get this huge magic set of folders and files. What are they all actually doing for you? And can we get rid of all of them and put a Rails application in a tweet? Again, my name is Nate Berkepeck. You may have seen my blog online, a blog at NateBerkepeck.com. I also have a course called The Complete Guide to Rails Performance. Normally, talking about performance and Ruby's speed is kind of my shtick. That is at railspeed.com, that course. We're not going to really talk about performance today. There is some performance benefits to this talk, which I'll talk about, but they're not really the most interesting part. I think what's interesting here is the underlying modularity of what Rails can do for you. But first, let's do a little word association. Let's do a little psychoanalysis on all our poor Rails developer minds here. When I say Rails, what do you think of? Do you think of bloated or light weight? When you have a Rails application and it's 50,000 lines, does it feel a little bit like this? When you think of Rails, do you think well-architected or do you think of spaghetti code? I've heard action controllers described as a crack den of inheritance. Do you think of object-oriented when you think of Rails? There's a Ruby web framework formally known as Lotus, not known as Hanami. I don't know if they still say this in their marketing, but they used to say that Lotus aims to bring back object-oriented programming to web development, which left me wondering where did it go? And who is bringing OOP back? When you think of Rails, do you think of modular or monolithic? There are many people on Stack Overflow who complain about CSRF not working, and the common advice is to just turn off CSRF protection. Surely that by itself is proof that people should only turn on this feature when they need it. Presumably, whoever wrote this comment also probably thinks that SSL breaks things and so all of us should just not verify our SSL certificates. When you think of Rails, do you think of fast or do you think of slow? Does your Rails application sometimes feel like it's going at a bit of a leisurely pace? Oh, wait, wait, what's that sound? Oh, it's a plug. Oh, I wrote a course about fast Rails applications. Oh, it's at realspeed.com. All right, that's all. I'm going to stop talking about it now. I'm going to stop talking about it now. My thesis here is that although we have a lot of negative mental associations sometimes, I think, with Rails, I think it's actually a lightweight, well-architected and modular framework for creating speedy web applications, but it just doesn't advertise itself that way. I think a lot of this comes from the way that our BDFL DHH talks about Rails. In his keynote last year at Rails Com 2015, he described Rails as a kind of prepper backpack for Doomsday. He wanted to be able to rebuild Basecamp by himself if all of the internet went down and he only had Rails left. He wants Rails to be able to do everything and to be able to rebuild Basecamp with just everything that comes in Rails out of the box. That's a very expansive vision. That's not like, oh, I'm going to do a one-file Sinatra app and it's going to be cute in blog posts. That sort of vision leads to a very different perception of a web framework than what other frameworks come to advertise as their simplicity or whatever like that. That doesn't necessarily mean that Rails can't be simple or can't be lightweight. What we're going to do in this talk is we're going to take the file structure and the boilerplate that you get from a Rails new command line and we're going to basically just hack it all off until we get down to 140 characters. The first thing we do after we type Rails new is we have to add a controller. Our app has to actually do something. I'm going to add a Hello World controller which is going to render Hello World in plain text. That's the only thing I'm going to add to Rails new. I've got to add a route for this, obviously, and configure routes.rb. At this point, we have 433 lines of code generated by Rails new. That's spread over 61 unique files. That is a lot. That includes the YAML configuration files generated, all the .rb files, the rackup file. It does not include blank lines or comments. This is 433 lines that are actually doing things. That's a lot to wrap your head around. Step one is to just delete all the empty folders and files. We get a lot of folders that are generated by Rails new that just are empty. I have this .keep file to force get to put them into your source control. These folders are otherwise empty. They're just there to serve as a placeholder. Rails doesn't need these things to boot up. It doesn't need empty folders to start an application, which seems obvious in retrospect, but a lot of people keep these folders around with no intent of ever using them. Some of them, the more interesting ones here that we're going to delete entirely are the entire lib folder, the entire log and temp folders. Log and temp will just be recreated if Rails needs them. And the entire vendor folder, because we're just doing a Hello World application so we don't need assets or whether or not they come from our own app or from some vendor. We're also going to delete all the sort of boilerplate empty files that don't do anything. A lot of these files, especially config initializers, a lot of these are just blank with comments. There's no actual code in these. There's no comments that tell you, hey, you should think about such and such decision. Go do that. And a lot of these application job or application helper are just blank modules that don't do anything either. There are signposts that say, hey, you should put such and such code here. They don't actually do anything functional. Same thing with the public directory. We can delete the entire public directory. The files like 500.html and 404.html, when Rails has a 500 response in the production environment, it will try to show the user 500.html. If that file doesn't exist, and you have an overridden Rails' behavior, it will just render a blank 500 response, which for our little Hello World application is totally fine. My point here is that empty is not equal worthless. I think this is actually one of the most important things that Rails Nu does, is create all these empty comment-filled files. Because what it does is it creates a common vocabulary. It says, okay, when I come to a new Rails application, I will go to the app models directory to find the domain model. I know that's where most of the business logic is gonna happen, hopefully. I know that the controllers and all the HTTP-related stuff is gonna be in app controllers. If I'm hacking away in the view and I see a jQuery plugin, I know that that's probably gonna be just gives us this common starting place, which makes it so easy for any company that works on multiple Rails applications or as consultants like myself to go from Rails application to Rails application and say, all right, well I know pretty much how this is organized, because we have this default organization that we've all sort of agreed upon by using Rails Nu. We could hash out this problem every time we started to do application, but we don't. I didn't really realize how important this was until I started doing more work with JavaScript on the server side. It's just like a free-for-all. Some frameworks don't even tell you how to organize anything, so this is especially true, it seems to me, in electron applications, where everyone just sort of has their own folder structure and each app kind of does things their own different way. In Rails, we don't have that problem. We just all have agreed that, well, let's finish it on this, and this is where how we're going to organize our applications. Step two is to delete the entire app folder. Some of these are kind of obvious. Our Hello World application doesn't need assets, so I can just delete application.js, like action cable. I'm a bit of a cord cutter, so I don't use action cable. That was a tender love pun, it wasn't mine. Don't need CSS. It's more action cable stuff. For the controllers, we're going to inline, so we're going to move the actual controller-related stuff that matters into configapplication.rb, and we're going to delete all the views and all the models, everything else in app. The only part of app that we actually care about in a Hello World application is this part, the controller. I'm eliminating application controller. That's just a common, useful pattern to have. You should usually have an application controller that has common behavior between all of your controller classes. If you don't, that's fine. You don't need an application controller. In a one controller application, we definitely don't. I'm just putting it at the bottom of configapplication.rb. I think Xavier Noria's talk, just talked before me in here, is going to be a really good compliment to this talk. If any of this stuff, because I'm going too fast here, is like, well, I don't know why it's in configapplication.rb. He talks about the entire boot process and when this file gets loaded and why it's important that I moved it here. So it's really confusing. I would watch his talk when that gets posted. Step three is delete the entire bin folder. This one's a little more obvious, I think. The bin folder is just full of a couple of useful things that make Rails development easier from a developer's perspective. Two of these files are just really simple best practices. They don't really do anything, and that's bin update and bin setup. These are just example scripts that are just intended to say, hey, successful Rails applications generally have a setup script that sets up a develop environment. It installs Redis and Postgres and all the weird things that your app needs. And so a new developer should just be able to run bin setup and then Rails server and be done. That's just a best practice. Here's an example. So you don't need that file. You can just delete it. Same with bin update, same thing. Then the four other files here, bin spring, bin rake, bin Rails, and bin bundle are bin stubs. What are bin stubs? Bin stubs basically just wrap a gem executable in a little bit of environment setup. So usually it means setting up the load path with bundler or whatever. It might set a constant, like it might say, hey, my app lives in such and such a directory. Now load the gem executable. And that's what actually gets run when we type Rails servers. It should be the bin stub that gets run. And if you actually look at bin Rails and bin rake, you'll see they also use spring. So the bin Rails sets up a new application process with spring, then does what it would normally go and do anyway. We don't need these. What we can do instead, because we're just, all we want to do is set up a little hello world server, is to use config.ru directly and use a command called rackup, which comes with the rack gem. So rackup looks for a config.ru file in the current directory, and then it treats the contents of config.ru as the body of a block inside this code. So it thinks app equals rackbuilder.new, contents of your config.ru file, and then calls .toapp on it. The config.ru file in a generated Rails new application looks like this. All rackup files, that's .ru's rackup, end in run, and then some rack-compatible application. You don't know how rack apps work, I'm going to get to that in a second, but that's all you need to know is we're going to call rackup in the current directory instead of Rails server, and it's going to execute config.ru directly, which is basically what Rails server was doing anyway, just with a lot of other fancy add-ins that we don't need for our little hello world server. So the first step for is to start using only what we need of the actual functional components that remain at this point. We're going to, so at the top of every, everyone's config application.rb, you're going to see require Rails all, and what that does is loads up all the different frameworks in Rails. The truth is that I think many applications don't actually use all the components of Rails, especially API-only applications or very simple ones like the one I'm talking about today, like Hello World. If you actually look at the Rails all file, it looks like this. I think this is the entirety of it, I didn't cut anything out here. It literally just loops through all the different framework components of Rails, requires each of them, and then we're done. So all I've done here is taken that out of an array and done it line by line and commented out the parts that we're not going to use. We're literally just going to use ActionController for this Hello World app. I don't need a database, I don't need ActionView, although ActionView gets loaded anyway. ActionMailer, I don't need it, not going to send any email. I don't need ActiveJob, definitely don't need ActionCable, and I don't need Sprockets, because I'm an API server, so I don't need any assets or have anything to do with assets. So to give you a little history lesson on even why this is possible, a gentleman named Yehuda Katz, who I think is here, not in this room, I mean, he's here around in Rails. He's there, hey, Yehuda! Wow, okay, so this is the second time I've talked about someone's framework in front of them. So in 2008, Merb merged with Rails. He merged his web framework at the altar of Rails, and at the time he had said Rails will become more modular, starting with the Rails core, and including the ability to opt in or out of specific components. This was sort of like a hallmark of Merb. You couldn't do this stuff in Rails, too. It wasn't this modular structure that we have today, but that was the way Merb worked. So when we merged Merb into Rails to get to the Rails 3 release, like extracting these different framework components. Now, some people didn't like this. Jeremy Askinis, coffee script author, had said on Twitter that all forward progress stalled for nearly two years. It's still slower than Rails, too. Bundler is a nightmare, no JS won. Case closed. Thankfully, that doesn't seem to have been the case in the four years since he said that. But I think it is true that Rails 3 wasn't really like a feature release. It was a release for cleaning the internals, making things easier for plugin authors. And part of why I'm giving this talk is I think a lot of the work that went into Rails 3 and this modularity that's been given to us isn't used enough, or at least we're not aware of it enough as Rails developers. So know this stuff so that Yehuda's work was not in vain. So the other thing we're going to lead here is our gem file. The only thing we need in our Hello World application is just gem Rails. So we're just going to get rid of the gem file eventually to get down to tweet size. We're actually going to have to get rid of Bundler, which is annoying, but it does technically work. It's important to know that Rails is very conscientious, I think, of what goes in the gem file and what goes into the Rails framework itself. And everything in the gem file is very much a suggestion. None of it is required to get a Rails server working. None of it is like official, you know, I guess TurboLinks is officially sanctioned, right, because it's in the Rails organization or whatever, but none of it is required, you can get rid of all of it if you want. One area that you kind of saw this happen was with action cable. Someone suggested during the development and the merge, let me just make action cable a gem and then put it in the default gem file. But DHH said, I think action cable at WebSockets is so important that it needs to be in the main framework. It's too important to be just in the gem file. So there's a definite decision being made here about what goes in the framework and what goes in the gem file. And so when you look at that gem file, don't think that anything in there is something that you have to be using. We're also going to dump all of the config files that we don't use anymore because I just got rid of action cable by not loading it instead of Rails with Rails.all or Rails slash all. We're not using ActiveRecord anymore, so I can delete config database.yaml. We're not using Puma, so I can get rid of Puma.rb and I'm not using Spring, so I can get rid of Spring.rb. Then at this point we've got like five files left. And now we just inline everything. So we move five files into one. There are four very important files here. Again, Xavier's talk's really important here. He talks about why there are four different files for all this stuff and why we don't inline them all into one. So we have boot.rb, environment.rb, application.rb and production.rb. And we're just going to put all of these in the rackup file. So they're all going to go into config.ru. And once you do that it looks like this. So this is what I consider the smallest practical Rails application. And we're going to get to tweet stupid length in a minute. So let's walk through this one line by line because I think this is important to understand. We're going to require the rail tie for action controller. We require the rail tie and not action controller directly because there's some stuff that happens in the rail tie that we want to make sure that actually gets run. We're going to define an application that inherits some Rails application. This part right here is exactly the same as it is in application.rb. We have to define a secret key. I got rid of secrets.yaml, right? So I have to define my secret key as a config point. Because this is a toy application I'm just going to set it to some meaningless string. You obviously have to do that for real in a real application. And then I've inlined config.routs.rb inside of my application definition. So normally at the top of config.routs.rb I think it says rails.application.routs.draw So we're just doing that here inside of the application itself. Finally I've got the controller at the bottom of the file here and I have to call initialize on Rails application. This runs all the initializers and all the hooks in rail ties. And finally I said every rack of file is going to end in run some rack-compatible application. So this is the smallest practical Rails application for all intents and purposes. Yeah, listen to that bell. Take a look at that. Oh my god! Listen to that horn! It's beautiful. I know. It really is a work of art. But there's a lot happening here. This is like a 10-line Rails application. You're still running even if you just required Rails all at the top. You're running dozens of initializers. Tons of features are still in this Rails application even though it's 10 or 12 lines or whatever. Where I think you can see this the most is in the middleware stack. So there's a stack of rack middleware that each request passes through before it even gets to your router. There was a talk this morning about rack middleware and more about how that works. All you're going to know is basically there are things that wrap your application and are like filters for the request. So first the request goes through this middleware and then that one, then that one, then that one, and so on and so forth. At the very bottom here is where we call the router or it gets passed off to the router. And all these middleware do something. So as some examples here Action Dispatch Request ID adds a unique ID header to every request. Rack Runtime adds a runtime header that says this request took x amount of time. Remote IP protects you from IP spoofing attacks. We have cookies in session store. We have the flash middleware that runs the flash hash. And then we have some middleware at the end for HTTP caching. So all these middleware are all doing things and you didn't have to configure any of them. They're just the default Rails middleware stack. Some of these can be eliminated now in Rails 5. We have this new config point called API only. If you set this to true it will remove the session based middleware down here. I think it removes some more. It's sort of like the similar to when you call an API. One of the things it's going to do is set API only equal to true. You can also delete middleware on your own. So if all of this boilerplate doesn't appeal to you you can just delete each middleware that your application doesn't use on its own. Now why would you do that? Middleware are not free. Each one of these is going to cost $20. Some of them are going to use some slower features of Ruby like block.call. When there's 20 of them here that does start to add up. I'm going to talk a little bit more about that in a second. Here's another hidden Rails modularity feature as we're trying to strip down our app even further. There's a little class called Action Controller Metal which is pretty cool. Action Controller Base and all Action Controller Base is Action Controller Metal plus a ton of modules like 50 modules like a ton of modules. These are just some of them like cookies, strong parameters, force SSL, etc. If all these things don't really mean anything to you that's okay and it's kind of intended but if you do feel like you want to dig into how Action Controller Base works a little bit more, if the idea of a thin controller appeals to you you can inherit directly from Action Controller Metal and build your own controller. So even the render method is actually factored out into its own module so if you inherit from Action Controller Metal basically the only thing you can do is work with the rack response directly and the response body is this. You can't even call render at this point so this isn't 100% equivalent to our Hello World application because this won't set the correct content type but you can basically start here and then just start including all the different Action Controller modules that you need for this particular controller and here's another part where I blow your mind all controllers are actually just rack apps so this is our controller and we can just run that controller in our rackup file as if it was a complete application. The dot action method takes a symbol which corresponds to an action and we can run the dot call method on this object that's returned by this and it's just a rack application. So now we can talk about rack applications. Rack applications, rack sits between Rails and your web server. Rack is like the common language that web servers and Rails speak. This is what allows you to use Pumo, Web Brick, Thin, all between and without having to change any of your code because Rails just speaks rack and the web server bindings all just speak rack as well so that they have a common interface, right? And that interface is a rack application. A rack application is just an object that responds to call and returns an array of three values. The first one is the HTTP status code. The second one is a hash, this slide is wrong, it's not an array, the second one is a hash which is the rack env or environment hash and the second one is the body which I think has to respond to the each method. So usually our responses are going to be arrays because they have to respond to dot each. So our controllers just by themselves are rack applications and when the router, the Rails router is basically working with your Rails application, it kind of doesn't know that each endpoint or when you call get hello hello index a Rails controller is basically functionally the same to it as any other rack application which allows you to do some kind of cool things. You can send routes to Sinatra applications because Sinatra applications are rack applications. As far as I know, Hanami is also a rack application so you can just mount Hanami applications inside of Rails. You can also route directly to Prox which is kind of crazy because they are rack applications that respond to call, et cetera, et cetera. So which we're going to use that in a minute. We could use that in a minute. We could make our hello world application even simpler right by just routing directly to a proc rather than even bothering with a controller. If that Action Controller Metal and all that stuff sounds interesting to you but like you don't want to start from zero, there's this cool method and it's kind of hard to read, I'm sorry, it's kind of small. ActionControllerBase.withoutModules .each and then you give it a block. You can basically start with ActionController and it'll give you a list of modules minus the ones you don't want. So we're saying ActionControllerBase and ActionControllerBase minus ParamsRapper and streaming include all those. So you can kind of start from all the ActionControllerBase modules instead of starting from zero and including them one at a time. Okay. Finally when it comes to modularity I think it's important to know that not all models need to be ActiveRecord. You can put anything you want in the models folder. Actually it doesn't have to inherit from ActiveRecordBase. ActiveRecordBase is just a class that like ActionControllerMetal just includes or extends a bunch of other modules. That's literally it if you look at the base.rb wherever it is in ActiveRecordCode and it's not any more complicated than that. So whatever is in your models folder it doesn't have to inherit from ActiveRecordBase. There's tons of cool stuff in ActiveModel which can make your plain Ruby objects sort of walk and quack like an ActiveRecordBase object but not actually have to do anything with persistence or database. So check out ActiveModel for a way of modularizing your Rails applications even further. So like I said I was going to talk about the performance story so here's what it is. If your Rails application is like not doing anything meaningful or it is an API only application using several of the main components of Rails if you don't require all the parts of the Rails framework you can save some memory. So if you don't require for example sprockets I think you can save like 10 megabytes of memory per process which is like not small potatoes. I mean like when that across four processes in a typical server like that adds up a little bit and if you start getting rid of logging there you can save a couple of milliseconds per request. So when I talked about config.middleware.delete some of those if you delete half a dozen of them you can shave some milliseconds off your request time there. Not really that big of a gain obviously. I mean most Rails servers are 100 millisecond response times right so shaving 6 milliseconds 10 milliseconds off is probably not a big deal to you unless you're at GitHub scale like you have an average response time of 50 milliseconds and so that's like 10% like awesome great I think the performance story here is really not as interesting as the modularity story and the code organization story so in general I just want you to realize that framework code is nowhere near as important as application code. Rails is not slow your application is slow the way you use Rails is slow but you're not starting from from a 5 yard what's the opposite of a head start you're not starting a negative here you're starting at the same level that pretty much everyone else is and it's the application it's everything that comes after Rails new that makes an application slow okay now the insanity that's cold golf so this is the application as we left it last this is the most practical Rails application that makes any sense and here's the world's smallest Rails application amazing beautiful now here's what it actually is so this is a shell command we're going to call rackup we're going to pass rackup a dash R option which is going to require a library we're going to require actually control a rail tie and then the dash B option this string is config.ru I think dash B actually stands for builder as in rack builder and then we're going to give it a string that's like our config.ru we're going to run an anonymous class which inherits from Rails application and then this block here is the body of the class we still have to set a secret key base I don't know why because I'm not using any secret key base related things here's a little code golf trick this question mark X is the same thing as quote X quote is just one character less I think that's only there for historical reasons because Ruby 1.8 had this weird thing where if you did question mark X it returned a number it returned the ASCII code or whatever and they couldn't get rid of that so they made it return a letter instead it doesn't matter because it's a big secret key base to something that makes Rails shut up this is very insecure don't do this but this is a toy application so it's fine and then we called .initialize on it to run all the Rails ties and set up the Rails application and this application doesn't actually do anything it's 404s as a service because there's no routes so all this Rails application can do is serve empty 404 responses but it is a Rails application and it fits in a tweet you could make it useful quote unquote useful by adding routes.draw to some proc here but then you would just have an application that served 200 responses instead of 404s so is this even practical Nate? well no not really there's two practical applications I can think of for Rails applications that fit in a file or probably not a tweet test suites for gem slash engines I think plenty of gems need a way to test with a live Rails application sometimes people just do Rails new inside of their test suite directory which ends up creating this huge 400 line monstrosity which is longer than their entire test suite you don't need to do that you can just use our one line Rails application and it's going to be functionally the same as a real Rails application so we use that technique in the Raven Ruby gem which I maintain that's the Ruby client for the century error notification service and I think every Rails or every Ruby gem that needs to test against a Rails application should be using a similar approach and also for API only applications I'm not the kind of guy that does like single page applications it only uses my Rails server as like a back end for my Angular app or whatever but if you are and you're not rendering HTML responses maybe your Rails application doesn't have anything to do with assets and all the assets are handled by Nginx or whatever there's a lot of the Rails framework that you cannot load Sprocket's an active record and the most important ones as far as memory goes and not loading those parts of the Rails framework will save you a little bit of memory and it will save you some headspace of not having to think about these components being in your global namespace but the reality is that most applications need 80% of what Rails provides and so when you see Sinatra and Sinatra is like require Sinatra, get slash do and all that and that's really nice the reality is that an application that you could get paid to work on every day is going to need about 80% or more of all these things that get provided for us in the Rails framework so while it may not win any blog post beauty contests I think the way that Rails new is set up is actually the way that most of us would minimize the work for the majority of applications so Rails is modular you just maybe never needed it so your homework today try not using Rails slash all in your Rails application, try loading the parts of the framework that you need to actually test to see if this makes any difference to you I suggest using derailed benchmarks, it has a little tool that will help you see how much memory you're using on startup so when you're not requiring these parts of the framework you want to see that number go down it's author Richard Steemans right here in the front row and also if you're thinking about deleting middleware to save some response time you can use Apache Bench that's AB to pound your application with tons of requests per second and see if it's actually improving those times consider Action Controller Metal and Active Model just take a look at those classes in the Rails codebase and see if there's any way that you can simplify your models or controllers, maybe you're using all these features that you don't actually need and then the next time maybe the next time you would reach for Sinatra, the next time you would reach for Cuba or some other simple Ruby framework, try just starting from a one line Rails application and see where that gets you instead so this talk is available in the form of a GitHub repo and if I skipped or if I went fast through any parts and you want to know how do I get from Rails new to a tweet, it's available here in the form of a commit log so if you go to NateBurkepex.tweetlink on GitHub you can follow the commit log line by line with some really long commit messages about why I did certain things it gives a little bit more of the background of it this presentation and the slides are going to be available here at in the form of Rails lightweight stack actually that's Rails underscore lightweight underscore stack markdown, screw it up there in that repo also there's a bunch of different one file Rails applications and different practical uses and different like API only uses for this kind of stuff lots of other resources there like I said my course is the complete guide at Railspeed.com I also have tweeted, I've tweeted the one tweet Rails application at my Twitter at NateBurkepex if you want to retweet it there so thank you very much for your time