WEBVTT 99:59:59.999 --> 99:59:59.999 Thanks Mike. 99:59:59.999 --> 99:59:59.999 Thanks for the invitation. 99:59:59.999 --> 99:59:59.999 I want to start with a picture from this building, which I have to say 99:59:59.999 --> 99:59:59.999 I love this venue, and especially this building. 99:59:59.999 --> 99:59:59.999 And especially when it's not on fire. 99:59:59.999 --> 99:59:59.999 And I think we were really dumb yesterday. 99:59:59.999 --> 99:59:59.999 So if it starts beeping, I will be leaving this time. 99:59:59.999 --> 99:59:59.999 If you have not done it, I suggest you walk up this round arc— 99:59:59.999 --> 99:59:59.999 you can walk all the way up to the top— 99:59:59.999 --> 99:59:59.999 and on that walk you will see that stairs somewhere that says "The Beginning". 99:59:59.999 --> 99:59:59.999 I want the beginning of my talk to be the end of my last talk on a very similar topic. 99:59:59.999 --> 99:59:59.999 And that was an idea that I was proposing 99:59:59.999 --> 99:59:59.999 and that was the realization first of all that every gem that you start, 99:59:59.999 --> 99:59:59.999 the gem starts with a namespace. 99:59:59.999 --> 99:59:59.999 When you develop a Rails app, you never start with a namespace. 99:59:59.999 --> 99:59:59.999 And so if you compare the two, a gem is like a box. 99:59:59.999 --> 99:59:59.999 You put a little label on it. 99:59:59.999 --> 99:59:59.999 And a Rails app is a gorgeous infinite nothingness 99:59:59.999 --> 99:59:59.999 with all the bells and whistles of Active everything. 99:59:59.999 --> 99:59:59.999 To get a gauge of how relevant that is, who writes Rails apps on a daily basis? 99:59:59.999 --> 99:59:59.999 That's about two-thirds, maybe three-quarters. 99:59:59.999 --> 99:59:59.999 And who writes Ruby, but never touches Rails? 99:59:59.999 --> 99:59:59.999 That's way less. Okay. But still a few. 99:59:59.999 --> 99:59:59.999 So I have a feeling that for the second group, what I'm going to say is going to 99:59:59.999 --> 99:59:59.999 apply a little bit less. 99:59:59.999 --> 99:59:59.999 Maybe because of Rails not being around. 99:59:59.999 --> 99:59:59.999 So the idea from my last talk was: the next time you start an app, 99:59:59.999 --> 99:59:59.999 just put everything in a namespace. 99:59:59.999 --> 99:59:59.999 And I end it with what I found to be an awesome finishing slide, 99:59:59.999 --> 99:59:59.999 give yourself a box so you can start thinking outside of it. 99:59:59.999 --> 99:59:59.999 With the idea that if you have a gem, and you give something a name, 99:59:59.999 --> 99:59:59.999 and you put things inside of it, you can open that box, 99:59:59.999 --> 99:59:59.999 and you can go WTF when you find something that obviously doesn't fit in the box. 99:59:59.999 --> 99:59:59.999 If you try to do that with a Rails app, 99:59:59.999 --> 99:59:59.999 you don't have a box— you don't have a name— 99:59:59.999 --> 99:59:59.999 so everything is going to fit. 99:59:59.999 --> 99:59:59.999 The best game you can play is what doesn't belong with the others. 99:59:59.999 --> 99:59:59.999 Red. Green. Loud. What doesn't belong with the others? 99:59:59.999 --> 99:59:59.999 And you're going to know that it's Loud. 99:59:59.999 --> 99:59:59.999 But if you look at your typical Rails app, what are you going to have? 99:59:59.999 --> 99:59:59.999 You're going to have a User, a Company, Roles. 99:59:59.999 --> 99:59:59.999 And then, the Tulip pricing engine that you wrote. 99:59:59.999 --> 99:59:59.999 And then the marketing part for that and a store. 99:59:59.999 --> 99:59:59.999 You're going to tell me that you can still play that game and find out what may or 99:59:59.999 --> 99:59:59.999 may not belong to this app? 99:59:59.999 --> 99:59:59.999 It's very hard, because it kind of pushes us in the direction of just putting all of 99:59:59.999 --> 99:59:59.999 app in there. 99:59:59.999 --> 99:59:59.999 So what I want to talk about today is Component-based Ruby 99:59:59.999 --> 99:59:59.999 and Rails Architectures. 99:59:59.999 --> 99:59:59.999 I promise you I put this slide in before that happened. 99:59:59.999 --> 99:59:59.999 (Laughter) 99:59:59.999 --> 99:59:59.999 I may want this button more desperately than you. Trust me. 99:59:59.999 --> 99:59:59.999 Stop me if I'm going too fast. 99:59:59.999 --> 99:59:59.999 Because I want to talk about large applications. 99:59:59.999 --> 99:59:59.999 And one thing I heard about them is you just never build them. 99:59:59.999 --> 99:59:59.999 So I work for Pivotal Labs, and we start a new project every few weeks, 99:59:59.999 --> 99:59:59.999 and many of them are greenfield, so many of them are tiny when we start. 99:59:59.999 --> 99:59:59.999 But you heard from Matt yesterday, we have pretty big apps as well. 99:59:59.999 --> 99:59:59.999 And for every app, since our definition of success is that a client can stay 99:59:59.999 --> 99:59:59.999 successful after we leave, what we have to strive for is some kind of 99:59:59.999 --> 99:59:59.999 architecture where you can develop an application well over time. 99:59:59.999 --> 99:59:59.999 So we have to think about the app being larger than it is, while we develop it. 99:59:59.999 --> 99:59:59.999 Despite TDD and all. 99:59:59.999 --> 99:59:59.999 Sandi Metz wrote her awesome book last year and gave a few talks. 99:59:59.999 --> 99:59:59.999 And at the one that I was at— I don't know if she said it in other talks as well— 99:59:59.999 --> 99:59:59.999 but she said, "Your app is out to kill you". 99:59:59.999 --> 99:59:59.999 The growing complexity of your app is going to chase you 99:59:59.999 --> 99:59:59.999 and if the app were a bull, you might feel like that guy. 99:59:59.999 --> 99:59:59.999 And that is very exciting. 99:59:59.999 --> 99:59:59.999 For the next second that is exciting. 99:59:59.999 --> 99:59:59.999 For the next hours or days that is painful. 99:59:59.999 --> 99:59:59.999 Okay, maybe this is a bit drastic, but who has felt like their app was 99:59:59.999 --> 99:59:59.999 behaving like that bull to them? 99:59:59.999 --> 99:59:59.999 I have felt like that as well. 99:59:59.999 --> 99:59:59.999 They are not typically that fast. 99:59:59.999 --> 99:59:59.999 Quite the opposite. 99:59:59.999 --> 99:59:59.999 But they behave like something that is too big to handle. 99:59:59.999 --> 99:59:59.999 So what I want to get to, is for us to be more like this guy. 99:59:59.999 --> 99:59:59.999 Calmer. In control. With sunglasses and a hat. 99:59:59.999 --> 99:59:59.999 (Laughter) 99:59:59.999 --> 99:59:59.999 But I don't really like bull fighting, so I actually want us to be this guy. 99:59:59.999 --> 99:59:59.999 (Laughter) 99:59:59.999 --> 99:59:59.999 Just hanging out. 99:59:59.999 --> 99:59:59.999 I want to go home at five on a Friday night. 99:59:59.999 --> 99:59:59.999 And I think this guy is probably the one who is going to do that the most successfully 99:59:59.999 --> 99:59:59.999 because he is just fine probably all week long. 99:59:59.999 --> 99:59:59.999 This "never build large apps" is part of a larger quote from Justin Meyer: 99:59:59.999 --> 99:59:59.999 "The secret to building large apps", he says, "is never build large apps." 99:59:59.999 --> 99:59:59.999 "Break your applications into small pieces. Then assemble those testable, 99:59:59.999 --> 99:59:59.999 bite-sized pieces into your big application." 99:59:59.999 --> 99:59:59.999 But how? 99:59:59.999 --> 99:59:59.999 He was—I think—talking about JavaScript. 99:59:59.999 --> 99:59:59.999 I'm talking about Ruby on Rails. 99:59:59.999 --> 99:59:59.999 You can tell from the font that you're supposed to read this slide. 99:59:59.999 --> 99:59:59.999 (Laughter) 99:59:59.999 --> 99:59:59.999 I'm going to use a tiny sample application throughout this talk. 99:59:59.999 --> 99:59:59.999 And this is where you can find it. 99:59:59.999 --> 99:59:59.999 If you can't read this after all, just search for github and the next big thing 99:59:59.999 --> 99:59:59.999 and I think it comes up first. 99:59:59.999 --> 99:59:59.999 I promise this thing is tiny 99:59:59.999 --> 99:59:59.999 and it does only one thing. 99:59:59.999 --> 99:59:59.999 It's an announcement page. 99:59:59.999 --> 99:59:59.999 You can announce whatever you have to announce. 99:59:59.999 --> 99:59:59.999 And people can sign up for updates. 99:59:59.999 --> 99:59:59.999 I spell it with two Ns but I don't know if my Twitter handle is only one. 99:59:59.999 --> 99:59:59.999 That's how often I tweet. 99:59:59.999 --> 99:59:59.999 So the service thanks you for signing up and I just press return. 99:59:59.999 --> 99:59:59.999 And when I made this sample app I had to come up with 99:59:59.999 --> 99:59:59.999 a little bit more than just that to actually have something to move around. 99:59:59.999 --> 99:59:59.999 So I realized pressing return makes it really easy for me to do that 99:59:59.999 --> 99:59:59.999 many, many times. 99:59:59.999 --> 99:59:59.999 But of course I only want my email address to be registered once. 99:59:59.999 --> 99:59:59.999 So I thought I'll give the server the ability to kind of feedback the level 99:59:59.999 --> 99:59:59.999 of annoyance that it feels while you're just continuing to press return 99:59:59.999 --> 99:59:59.999 and annoying it. 99:59:59.999 --> 99:59:59.999 At some point it'll just freak out. 99:59:59.999 --> 99:59:59.999 The freak out picture didn't come. 99:59:59.999 --> 99:59:59.999 Let's try again. 99:59:59.999 --> 99:59:59.999 It's an awesome picture of a guy with a lot of hair. 99:59:59.999 --> 99:59:59.999 I need your help though. 99:59:59.999 --> 99:59:59.999 I need you to imagine something really big. 99:59:59.999 --> 99:59:59.999 Because that app is tiny and I'm going to make examples 99:59:59.999 --> 99:59:59.999 of refactorings or rearchitecturings that don't make sense in that app. 99:59:59.999 --> 99:59:59.999 So bear with me and always imagine something very big. 99:59:59.999 --> 99:59:59.999 I'm going to go through eight ways of architecting this application. 99:59:59.999 --> 99:59:59.999 And I have roughly two minutes for every one. 99:59:59.999 --> 99:59:59.999 Let's start with number one. 99:59:59.999 --> 99:59:59.999 If you checkout that GitHub project, you will find those eight steps as tags 99:59:59.999 --> 99:59:59.999 and you can roughly follow the steps that I'm taking. 99:59:59.999 --> 99:59:59.999 The first one is a normal Rails application. 99:59:59.999 --> 99:59:59.999 It's all-in-one. 99:59:59.999 --> 99:59:59.999 Let's look at what's happening. 99:59:59.999 --> 99:59:59.999 There is a TeaseController here. 99:59:59.999 --> 99:59:59.999 It has two actions: new and create. 99:59:59.999 --> 99:59:59.999 Let's briefly see what happens. 99:59:59.999 --> 99:59:59.999 No surprises. It's not a well written method. John would refactor this. 99:59:59.999 --> 99:59:59.999 We're trying to find the entry that you gave this form. 99:59:59.999 --> 99:59:59.999 If we find it, we update tries on that entry. 99:59:59.999 --> 99:59:59.999 When we put it into this annoyance meter it'll tell us how annoyed we are 99:59:59.999 --> 99:59:59.999 at this point, and we'll feed that back to the client. 99:59:59.999 --> 99:59:59.999 If we don't find the entry, we'll just say, "Thanks for signing up." 99:59:59.999 --> 99:59:59.999 And then there's a bit of catching unexpected cases. 99:59:59.999 --> 99:59:59.999 That's pretty much what's going on in this controller. 99:59:59.999 --> 99:59:59.999 There is a model back in this. 99:59:59.999 --> 99:59:59.999 It's Entry. It has the email address and a number of tries. 99:59:59.999 --> 99:59:59.999 And there's this AnnoyanceMeter, which is not really important. 99:59:59.999 --> 99:59:59.999 It just derives new strings out of these counts. 99:59:59.999 --> 99:59:59.999 I suspect that everyone has seen this. 99:59:59.999 --> 99:59:59.999 And I will—for this talk—assure you that it every one of these stages 99:59:59.999 --> 99:59:59.999 you can run the tests, and—if I didn't pick the wrong commit—they will pass. 99:59:59.999 --> 99:59:59.999 So I have tests, but I won't show them. 99:59:59.999 --> 99:59:59.999 I will just be moving them around for every stage 99:59:59.999 --> 99:59:59.999 and adding new ones where there is a new test to be written. 99:59:59.999 --> 99:59:59.999 Now remember, I said at the beginning of every Rails app it's kind of this infinite void. 99:59:59.999 --> 99:59:59.999 A good picture for that is probably a big dump site. 99:59:59.999 --> 99:59:59.999 I've already asked you if you ever felt like the guy in front of the bull, 99:59:59.999 --> 99:59:59.999 and maybe you also felt like the driver in that bulldozer or whatever that is. 99:59:59.999 --> 99:59:59.999 The reason I'm saying that about this tiny app is that there's two things in there 99:59:59.999 --> 99:59:59.999 that are now indistinguishable—if you look at it from 30,000 feet—as to what they do, 99:59:59.999 --> 99:59:59.999 how they're connected, if they have any structure. 99:59:59.999 --> 99:59:59.999 It pretty much doesn't exist. 99:59:59.999 --> 99:59:59.999 So for all intents and purposes— if you just multiply, extrapolate from these two 99:59:59.999 --> 99:59:59.999 classes to, say fifty or a hundred— you have no clue 99:59:59.999 --> 99:59:59.999 who's interacting with whom, what's going on, and why. 99:59:59.999 --> 99:59:59.999 Unfortunately this doesn't look very chaotic. 99:59:59.999 --> 99:59:59.999 But if I were to write a hundred class names here, it would be chaotic 99:59:59.999 --> 99:59:59.999 and you would not be able to assess any sort of structure 99:59:59.999 --> 99:59:59.999 just by looking at those classes. 99:59:59.999 --> 99:59:59.999 A first way of getting structure into this application is modules. 99:59:59.999 --> 99:59:59.999 The plus we get from that is a higher level structure. 99:59:59.999 --> 99:59:59.999 And you've all probably seen this one too. 99:59:59.999 --> 99:59:59.999 Not much changes. 99:59:59.999 --> 99:59:59.999 The controller doesn't change at all, except it now references to Entry and 99:59:59.999 --> 99:59:59.999 the AnnoyanceMeter within their namespaces. 99:59:59.999 --> 99:59:59.999 In the AnnoyanceMeter, Annoyance left the class name; it's now in a module. 99:59:59.999 --> 99:59:59.999 I moved some other stuff around that was mainly for testing. 99:59:59.999 --> 99:59:59.999 We now have this structure. 99:59:59.999 --> 99:59:59.999 And if we come back to this analogy then maybe we have moved 99:59:59.999 --> 99:59:59.999 from a dump site to a recycling yard. 99:59:59.999 --> 99:59:59.999 Recycling yards are awesome. 99:59:59.999 --> 99:59:59.999 I should know this because I remodeled my house, and you visit those places very 99:59:59.999 --> 99:59:59.999 often when you do. 99:59:59.999 --> 99:59:59.999 You can point to a corner of the recycling yard and say, "That's where the scrap metal is." 99:59:59.999 --> 99:59:59.999 However if you've ever gone to such a place, you also know that you stick the hard-to-recycle stuff under your car because you're not a nice citizen 99:59:59.999 --> 99:59:59.999 and then you try to pawn it off to the recycling yard because you just want to get rid of it and it's so hard to get rid of. 99:59:59.999 --> 99:59:59.999 People do that all the time. 99:59:59.999 --> 99:59:59.999 Just stand in front of the clean wood and then suddenly, not-so-clean wood gets thrown on top. 99:59:59.999 --> 99:59:59.999 What does that mean for code? 99:59:59.999 --> 99:59:59.999 It means there is structure. 99:59:59.999 --> 99:59:59.999 I have now an EmailSignup module and an Annoyance module. 99:59:59.999 --> 99:59:59.999 And in that is more classes. 99:59:59.999 --> 99:59:59.999 But there is no guarantee that these classes are a) independent, or b) actually doing what they say. 99:59:59.999 --> 99:59:59.999 I can't prove it. 99:59:59.999 --> 99:59:59.999 That's why I put the rectangles over each other. 99:59:59.999 --> 99:59:59.999 But at least I didn't have to write Entry anymore because I now have a higher-level concept, that being this EmailSignup module. 99:59:59.999 --> 99:59:59.999 If I want to improve on this, I would like to go to the next step, which is prove that these two pieces are independent. 99:59:59.999 --> 99:59:59.999 I call it the gem component app. 99:59:59.999 --> 99:59:59.999 I may need to explain a little bit more about this step. 99:59:59.999 --> 99:59:59.999 First off, you're not seeing that engines folder right there. 99:59:59.999 --> 99:59:59.999 We're not looking at that. 99:59:59.999 --> 99:59:59.999 We're looking at the annoyance gem. 99:59:59.999 --> 99:59:59.999 I made a folder gems and I put an annoyance folder in there. 99:59:59.999 --> 99:59:59.999 Actually this is a complete folder structure of a gem. 99:59:59.999 --> 99:59:59.999 We have a gemspec; it defines our annoyance gem—and I should probably fill in this data—but there is a Gemfile which tells us that we are running tests with RSpec and magically—this may be the only time we need to look at tests—I have tests now in this gem. 99:59:59.999 --> 99:59:59.999 If I go into that subfolder, I can now independently run. 99:59:59.999 --> 99:59:59.999 Because of the way RSpec will load these files, I can prove to you that these tests for the levels and the meter of the annoyance will pass without knowing about EmailSignup. 99:59:59.999 --> 99:59:59.999 Again, it doesn't matter here; it matters a whole lot if you're dealing with tens of hundreds of files. 99:59:59.999 --> 99:59:59.999 I can prove that this thing is independent. 99:59:59.999 --> 99:59:59.999 How many have seen shoving a gem like this into the subfolder of an app? 99:59:59.999 --> 99:59:59.999 Everyone else should start doing that. 99:59:59.999 --> 99:59:59.999 You could ask me two good questions: one is why am I not using git submodules? "I'm not smart enough" is the answer to that. 99:59:59.999 --> 99:59:59.999 Why am I not using a different repository? 99:59:59.999 --> 99:59:59.999 "I'm too lazy" is the answer to that. 99:59:59.999 --> 99:59:59.999 You can just do this. 99:59:59.999 --> 99:59:59.999 So how do I use it in the application? 99:59:59.999 --> 99:59:59.999 The Gemfile of the main application is now just pointing to an annoyance gem and it's just referring to it by its path. 99:59:59.999 --> 99:59:59.999 That's all there is to it. 99:59:59.999 --> 99:59:59.999 This will be loaded. 99:59:59.999 --> 99:59:59.999 I can access it like before. 99:59:59.999 --> 99:59:59.999 And if I look into the TeaseController, we are still down here just loading that AnnoyanceMeter. 99:59:59.999 --> 99:59:59.999 Nothing else special has happened. 99:59:59.999 --> 99:59:59.999 The only thing that happened is we now have provably independent tests; we have provably independent code within that one component that we just extracted. 99:59:59.999 --> 99:59:59.999 In the words of Eric Evans, "Choose modules that tell the story of the system and contain a cohesive set of concepts." 99:59:59.999 --> 99:59:59.999 We are now able to prove that a set of concepts—namely within that gem—is independent of the others. 99:59:59.999 --> 99:59:59.999 I think that is of great value. 99:59:59.999 --> 99:59:59.999 I was in Iceland last weekend and I found something funny so I wanted to put these slides in. 99:59:59.999 --> 99:59:59.999 It's good for push and pull to be so explicit, but if you look at the doors, it's actually not that explicit as to what they do. 99:59:59.999 --> 99:59:59.999 They denote how to operate this door, but now look at Icelandic signs. 99:59:59.999 --> 99:59:59.999 You know how to open these doors. 99:59:59.999 --> 99:59:59.999 They scream at you, "This is how you open me." 99:59:59.999 --> 99:59:59.999 You might also rip them apart, but you know. 99:59:59.999 --> 99:59:59.999 (Laughter) 99:59:59.999 --> 99:59:59.999 For the structure of the app, now there is a second component. 99:59:59.999 --> 99:59:59.999 So we've got a little bit more structure. 99:59:59.999 --> 99:59:59.999 Is that cool so far? 99:59:59.999 --> 99:59:59.999 Gems make up a portion of apps, but if I have dependencies towards anything Rails, I have to do a lot of homework myself. 99:59:59.999 --> 99:59:59.999 In the next step—what I want to call the Rails component app—I'm still going to do that for a Rails component. 99:59:59.999 --> 99:59:59.999 So I want provable structure for Rails. 99:59:59.999 --> 99:59:59.999 The way to do this easily is with Rails engines. 99:59:59.999 --> 99:59:59.999 If a Rails engine were actually a train engine, then a Rails application would actually be a train. 99:59:59.999 --> 99:59:59.999 So you might want to reconsider to call the whole thing Ruby in Trains. 99:59:59.999 --> 99:59:59.999 But I still have to clarify a thing that engines have still this wrong perception of being for pagination and generic administration and authentication. 99:59:59.999 --> 99:59:59.999 They are not. 99:59:59.999 --> 99:59:59.999 If you look at the docs it says Rails engines allow you to wrap a specific Rails application or a subset of functionality and share it with other applications of within a larger packaged application. 99:59:59.999 --> 99:59:59.999 I should know because I pushed that change last time I gave a talk like this. 99:59:59.999 --> 99:59:59.999 It's very good that it says that now. 99:59:59.999 --> 99:59:59.999 Let's look at what that means. 99:59:59.999 --> 99:59:59.999 Now next to this gems folder we also have an engines folder. 99:59:59.999 --> 99:59:59.999 Now you're allowed to notice that the app folder went away. 99:59:59.999 --> 99:59:59.999 Since we only have one controller, there is no longer any necessity for this app to contain any code itself. 99:59:59.999 --> 99:59:59.999 It only contains gems. 99:59:59.999 --> 99:59:59.999 Let's start with the more self-contained one: EmailSignup. 99:59:59.999 --> 99:59:59.999 If we go in here, there is still that Entry and it's now within a module and within a gem, which is also incidentally an engine. 99:59:59.999 --> 99:59:59.999 If you don't know a lot about them check out this source code or find out about them. 99:59:59.999 --> 99:59:59.999 I can't go into more detail but I'm happy to do that in Questions. 99:59:59.999 --> 99:59:59.999 So essentially this means I have my database migrations in here; I have tests for this; it runs independently. 99:59:59.999 --> 99:59:59.999 And the main app, as before, just includes those gems and references them. 99:59:59.999 --> 99:59:59.999 Let's quickly look at the Teaser because the Teaser is now what contains the controller. 99:59:59.999 --> 99:59:59.999 Actually I also want to point out that the Teaser contains assets, controllers and views in my example here. 99:59:59.999 --> 99:59:59.999 It does not contain models because it doesn't have any data itself. 99:59:59.999 --> 99:59:59.999 EmailSignup only has the model. It only has this entry thing. 99:59:59.999 --> 99:59:59.999 The Teaser thus must be getting that data from somewhere else. 99:59:59.999 --> 99:59:59.999 And indeed it does. 99:59:59.999 --> 99:59:59.999 It too requires EmailSignup to be present. 99:59:59.999 --> 99:59:59.999 And it references that dependency of course in the gemspec so that it's actually a valid gem definition.