 Before we start, why are you here? No, I'm actually really interested in what brings you to this workshop. Are you like, does anyone here have an app that has, say, a code base of a couple hours, sorry, a test suite of a couple hours, a hundred thousands of lines of code, and they're like, this anyone? There's a couple hands, okay. This sucks, I want to change something, cool. Is anyone here writing a lot of Rails apps? Someone doing consulting and spinning them up really often and wants to, okay, cool. So when I had a three hour proposal, the second half was gonna be that really large code base that we try to tear apart. In fact, you don't need 90 minutes to tear a large code base, part is more like months, but it would have been condensed. So this one is, this part that we're not gonna be doing is only kind of refactoring out of this sample app, SportsBall, but I hope that you will see that the ideas will apply in larger contexts. Again, whenever you do something about architecture, you make these examples that make you look totally goofy because you're doing these super Uber complicated things for almost nothing, and that's gonna happen again today. So bear with me. So getting started with component based Rails applications. I should tell you why I am here. My name is Stefan Hagelman. I work for Pivotal Labs and I'm in that category where I've spun up a lot of Rails applications and sound, I have a feeling that I keep touching the microphone in a funny way, is that true or not? I wrote a lot of applications, but there was one client in particular that kind of kicked off where we were engaged with them for almost two and a half years and the app grew really, really large and we were looking into ways to kind of bring some structure into it without leaving the realm of the monolith, if you will. So I then did that to almost every app that I did thereafter, and it's kind of still such a, it's a weird thing to talk about because if we were at a Java conference, I wouldn't give this talk. If we were at a Go conference, I wouldn't give this talk. There's many languages in which this is so common that there would be no topic for me because structure within applications is something that is a problem for Ruby because of the way it is. It's not a problem in other languages. And I've kind of become obsessed about it. If you kind of want to just get an overview, I go to conference and find the talks on this topic that I've given. So, but despite this not being a talk, but a lab, I want to answer the question quickly of why you want to do component-based Rails. I see a host of benefits when you add this one tool, namely a component to your toolbox for Rails. Let me go over these. So the main change that you're gonna see when you add components to your application is that you're gonna be able to draw something like that graph. Okay, so let me just go over the examples because it's hard to otherwise explain how important that is to have another structure like that. Communication. So we don't know what this application is about, but I can tell you that there's an importer that has something to do with programs, as you can see at the bottom. And obviously there's also something going on where there are sources and master, so probably some sort of import and reconciliation of some sort of thing that's called a program. Oh yeah, look at the cluster. So yeah, probably that is the reconciliation piece where sources get kind of merged or whatever into masters. If I don't, you indeed pick the worst spot in this lab. You should probably, if you. So you can talk about your application at a higher level, which you can't do, because what else do you have? You have module names, but modules don't kind of segregate. So the important piece is not just that you have a box, classes are boxes, if you will, modules are boxes, but what do the boxes mean? And when you go to components, you have arrows and you can draw them from one box to the other box, but you cannot draw them back, and that's the important bit. So I can communicate much easier about an application because I have a new higher level structure that I didn't have access to before. It makes collaboration easier. Let's say I'm working on source today, stand up this morning, we decided I have a feature that touches source, you have one that touches user. In this graph, again, without knowing nothing else about the application, we know a few things. If I truly only change source, I do not need to run the tests for master programs, reports, users, and styles. My source code does not touch that source code. That source code doesn't know that I'm touching something because it doesn't even know that that exists, it doesn't care about it. So I don't need to run those tests. And similarly, you have things you don't need to run tests for because you're not affecting that part of the code. It's still one application, mind you, right? But we don't need to test those things. We only need to test boxes that either we change or that have arrows pointing into us because those are the things that could be affected by our change. And it also means that the area where we could potentially be in conflict with our change at the end of the day, there's only two, if you just go strictly by numbers and set all these boxes at the same size, two out of 10 boxes, we could potentially have a conflict. If you have one application and no further structure, you can't think about these things. You certainly don't have the benefits of running less tests and having less conflict possible. Creation, super interesting. When you say you have a larger new feature that you want to roll out, you can put it into a component and switch that component on in development and in test and then in review, but you never switch it on in staging until you're ready. You can talk about it. You can change it. You can show users. You can maybe give beta users access. And the way you turn it on and off is one line of code where you turn that gem on or off in the respective environment. Maintenance, say reports is an API. Save, I don't know. Maybe it's a SOAP API. We wrote that and we don't like it anymore. We want a JSON API. The way you can create the JSON API is you take reports, the folder, there's a component there, you duplicate it. You call it reports two, you rename every occurrence of reports with reports two, and you just start changing reports to whatever you want. You can slowly work your way to reports two, deprecating reports. And when you're done, you just turn on reports two and you turn off reports and your code base is as clean as it was before. And there's one major thing that kind of underlies all of these benefits, and that's comprehension, because you can suddenly do something, again, you can't do when you just have classes and these other concepts as your mechanisms. And that is, you can do this. When you are onboarding someone, when you're thinking about what you're doing, you can treat everything that is below you as external and everything that's above you as not mattering at this moment. So whenever you look at a part of code, you have a much lower cognitive overhead to kind of understanding what's going on, because it is all smaller and you're creating more separation. All right, so Ruby installed now. You were somewhere there, sweet. Did the test pass? Cool. All right, so what's the app? I don't know if anyone started it. It's a tiny app. Is scaffold deprecated? I don't know, but I use scaffolding to create these teams, I have pages here. Falcons, Braves, I have on my app. They also play each other, which is funny. Also the Falcons win, which I don't understand because the Braves have clubs, but okay. So the Falcons win in this case, and this is, here's the main contribution of the app. So this is all scaffold, so you know the views, right? You can do everything with this. So, but then you can ask the app if the Falcons play the Braves, who's gonna win? We have only one game, Falcons won that game. Prediction Engine, pretty smart, says Falcons are gonna win again. So this is what the app does. The structure of the application, and now join me in opening some code. The structure of the application is interesting. If you open the source code, the first thing you notice is what? He notices components, but there's no app directory. So you can, but you don't have to, you can delete the application directory because you can replicate it inside of a component. Sorry, I'm using component when I talk about gems and engines inside of an application that are vended with the code. I just, I use all of those things and I often mix them up. And yes, there is a components folder, right? But when you, when we try to understand what's going on here, probably the easiest thing is to look at routes and realize there is indeed nothing in this application that provides anything. It's just kind of gluing an engine into this app. So to see what's going on, we could look into the routes of the component and that looks more normal, right? Now you see our games and teams and prediction. But keep browsing around. If you see something weird, just shout it. So, and I'm just gonna talk over how I think or how I view these things. So the interesting bit in the gem file, which obviously, so app, app is the component. This app needs to be loaded. So we have a gem app here in line four and it pulls it out of the path. This path thing is usually, is documented as being for gems in development, where you have a local dependency on another gem and you're developing that so you're pointing to it locally. And I guess that's kind of still true because I'm working on this app so everything's under development, if you will. But, so this is how this application, and when you bundle that, it's going to show you everything and it's gonna show you here that it's loading app from source. Okay, and this is something that, when other people talk about engines, they often say, and then you have to publish it. And what I say is, and then you do not publish it, not ever, unless something else happens, way in the future, but you don't publish it. This is wonderful, I still have one application here. And if you look into app, in fact, this is totally normal except for this annoyance of seeing this namespace app everywhere. But if we go through this, there is nothing surprising here. Again, yes, an engine, so we use mountable engines, as we'll see in a second. But, so everything is namespaced with the name of the engine. Games controller, pretty normal. Predictions controller, teams as a welcome page. And then game again, namespaced, so yes. If we find the migration, that's also an interesting bit, the migration for this team, obviously it's called app teams because also the tables are namespaced to the component. But as you can see, the migration lives inside of the engine. And you may have seen for, what is it, device, I think, has a helper to install a couple tables. And many engines that you download, sorry, that you import as kind of external dependencies, will have this rake task, which is a rake engine name, migrations installed or something like that, which installs the applications into the main application. Now, we didn't mention earlier that in DB, there are no migrations. And there's a little trick you have to do to make this work. And that is, I threw an initializer in here, which append migrations, it detects when it gets loaded into a Rails application. And in that case, it takes its own migrations, so the app's migrations, and adds them to the main app's migrations. That way you don't have to copy them, and they just get run. That's why when we run rake, DB drew up. So again, you saw the main app doesn't have migrations, but when I run migrations, they're just there. Because the app kind of hooks them in. See, I was going to have a later slide that says app is the worst component name you could ever come up with, but I've already confused myself three times. So app is clearly the worst component name you could ever come up with. I'll have more of those later. Okay, let me check if I've talked about all the things. Are there questions? People are concerned about me doing this. So no app folder, gems loaded in the path that we saw that it is an engine. It was a mountable engine, I mentioned that. Saw the routes, migrations. Non-published gems are funky. This is one thing you will find annoying, and it is truly annoying. When bundler pulls in a gem, it looks at the gem spec of that gem, which is why it's important that you always list all the dependencies of that gem, and I would suggest you do both runtime and development dependencies. You list them fully in the gem spec. Now, you can still give gem a gem, and it runs gem files. That's used for local development, right? And so we're locally developing, and I happen to check out this true skill, which is the prediction algorithm this thing is using. In fact, it's a ranking algorithm. So it's using a git version because the published version is broken, it's unmaintained, so I'm using this. Why is this funky? Well, when bundler loads this gem, it looks at the gem spec, and it says true skill, and it's gonna go to RubyGems and download any version of true skill, because in fact, I didn't even give a version. To prevent it from doing that, I need to restate that line in the main app, and in fact, up the transit dependency path, I need to restate such an unpublished gem should I have it. That's an annoyance, it is unfortunately, I looked into bundler, I think it's not possible to change that in an easy way where we would kind of not have that problem. You also saw that in the gem spec, every other production dependency has an explicit version, not a tilde arrow or not a grater. The reason for that is we don't want untested code in production, and it could happen that if you just say rails tilde arrow 419, that you actually get a higher version. So you can run your tests potentially, you can run your tests in the gem with some version of rails, and if your application states this dependency slightly differently, you could be running the application in production against a different version of rails. That's like having untested code, because you're testing it with some dependency and you're running it with some other dependency, so lock the dependencies down so you're ensuring that you force the app to have this version of rails. All right, this is a lot of stuff. And then there's one more thing I wanted to talk about. So I created this test file here, which kind of wraps the RSpec test that this engine has. And the reason for that is that I don't want to have to worry about changing my build script, the thing you ran at first, when I add more components. I want to be easily able to refactor, and this build script just finds all the test.sh files under this application and runs them. So as you notice, there is a test.sh right next to this, which runs the test for the main app. What you probably also notice when you ran it is there are a lot of tests in the engine app, and there are very few tests in the main application. The reason for that is I highly recommend that you run, that you create your tests local to the code so that you can in fact think about those things as components, and they're independent in a certain way. And so you have to think about what is the contribution of some component that it needs to have tests for? And there's only four tests in this main application because really the only thing it does is this. And so the tests are in fact, you could say, that's a little bit much, because it's testing that the structure inside of app, teams, games, and predictions are not a feature of the application, they're a feature of that component. And you can kind of go, give me a beer, I'll discuss that more, but this kind of test just high level that the glue code works, that's the intent, right? Test that the glue code works if your job is to glue, test whatever you're implementing if that's your job. All right, so that's all you need to know about components, so now we're gonna make some. But which one? So I showed you the app, I showed you a couple of the files, you have them in front of you, please look through them. What should we extract first? So right now it looks like this, but do you see anything in terms of structure that might be in this application? Sorry? The prediction engine, yes, thank you. Did I show it through my slides? So the prediction engine is one suggestion. Any other suggestions? If there are no other suggestions, I couldn't quite see who that was. Why? Why the prediction engine? It feels natural to extract. I think that's, yes it does to me too, but if you dig why does it feel natural to extract prediction, yes? Yeah, I totally agree. So games and teams are tightly coupled, in fact if we look at game, if you open game you see that game has associations to the teams. The prediction engine is kind of a layer on top of that. And if you look at prediction, open the predictor. The predictor has gets things, right, that are incidentally called teams and games. But if you look at what it cares about, the predictor doesn't care that teams have names. The only thing it cares about is that teams have IDs. And the only thing it cares about for games is that two IDs have a certain outcome in a thing that is called games. So it looks like that, at least when you look at predictor, it doesn't care at all about teams and games. And so let's extract predictor. What I'm saying by that is, let's try to extract predictor and prediction. If you look at prediction, that's kind of the result, I think anyways, of that prediction. So extract the domain gem. If you watched the Uncle Bob talk where he rants about having lost so many years in architecture, where he says that we have this problem where we bind our application to this weird delivery mechanism that happens to HTTP and this library that does that well, which is Rails. If you think about this app, and yes, it's tiny, and I'll get off my soapbox in a second. This app is tiny. You can see that the left side, teams and games, is kind of crud, that sets things up. What we're really changing the world with, the Uber of predictions here is this right side where you put in two games and it tells you who's gonna win. It's gonna make you a millionaire if this thing actually works, right? So that's its contribution. That's our domain. So it feels natural, it should feel natural, and if we extract it, and if I just am right about this, what we just kind of discussed, namely, it might not have that much to do with Rails because it doesn't even care about teams and games being active record. Then maybe we can make this thing not know about Rails. And we can't. All right, so let's do that. And by that I mean you do that. So the idea is we have one component already, it's app. We wanna create a second component that's gonna be predictor. And you're gonna do it. This is terrifying, by the way. I have no idea if this works. So because we said this is likely not caring about Rails, do these first two lines. So what they do is we're creating a gem, a straight-up gem, not an engine, and you'll see it generates a couple files for you. And then I want you to delete git because we are keeping it in this repository and we just want mother git to kind of take over. And then there's just a few steps, albeit very gnarly steps that we have to do to extract predictor into this. So it felt natural to think about prediction separately, move the classes, so predictor and prediction, move the tests, there's I think only a predictor test in there. Now these things were namespaced as app, right? We're taking it out of the app component, we're putting it into a predictor component. So we probably need to rename space, everything that's in there. Then we have to fix tests. Then we have to obviously, we're made this a gem now, so we have to make sure that it gets properly loaded in the component because app depends on this thing, and then hopefully we can fix the tests in the engine and we can check that the main application still works. Just so I'm not kind of doing something that most of the people are not interested in, is anyone actually interested in doing this right now? Then raise your hand. Okay, so just go, I'll walk around and see how this is going. And it would be really interesting if you have, if you can't run your tests or if you find out what you need to do to make the tests in predictor run. Shout out, I'll come by and we'll discuss. So what you should do is, you should start by creating this gem and making sure you can run tests in there. Then you move the files in, it'll blow up all over the place. Keep running the tests and fix the first thing that breaks these things and keep going, and as soon as you have the tests inside of this new component working, go one step out. What's one step out for this one? Well, the app component depends on the predictor component. So the next step out is, run the tests of the app component. And only when you've done that, run the tests for the main app. Rails Engine New Mountable, is that off limits? For this one, yes. Just create the straight up gem. So are the tests running for anyone? And if so, what did they do for it? Someone here had a bundle install error for PG. Right, so it was going pretty good. Are you going, go into the component that you just created. So go into predictor. I can't even get the app to run. This app does not depend on PG, so I'm kind of concerned that you seem to have a PG dependency. There you go. Where was it? In the production gem file, there's Postgres. If you have trouble with Postgres, delete that. Okay, so anyone else having a different problem than Postgres? Yes. At least for me, if I do bundle gem predictor, it'll create an RSpec. I get a whole bunch of options. Oh, okay. Oh yes, I guess it says there, answer the questions. I did that again this morning and it didn't ask me questions. I would take RSpec because the rest of the app is tested with RSpec. Yes. Which folder do I want predictor to go into? That's a great question. So these things are gems and the only thing we care about is that we can load these components, right? And you saw the path command. You saw the path option on this gem. It doesn't matter. If you put it wherever you want, you just change that path to be correct and everything else is gonna work. I already created that components folder, so the next command might be for you to move predictor into the components folder. It will also have benefits when we do the next one. Is anyone at least kind of getting an RSpec test output? Is anyone getting read or is everyone still blowing up? Yeah, okay. So if you've created the gem, you can actually run RSpec right away, which is great because it's gonna tell you that it's passing because you have a sample test in there. I think that is like expect true to be true. So now if you're in that state, take the predictor and the prediction, put them into the predictor gem, move its tests into and try to run that. That's gonna blow up. Yes. When you copy the predictor spec and you created the bundle correctly, it will ask you probably to overwrite the existing spec file in there. So then you'll get rid of that sample test and you should see breaking tests. What to do next? Oh no, it isn't really time. So go into predictor. So you have no tests in here. So take whatever IDE you use and take the predictor test out of the app component and move it in here and take the files, predictor and prediction out of the app component and move them in here. And then run the test again. Yes. Here's someone else's lost. So this is working out great. So we just did those two first two lines. Okay, cool. All right, let's do this together. Keep going if you're going, yes. If you're gonna do it on the screen, you can watch that. So you're hacking away and yes, you're realizing it's kind of gnarly. So let me flail in front of you. And by the way, I always use RubyMind and I think it's pretty good at this. All right, so I said we remove git from predictor. If you're still going strong and you have a feeling you're getting there, please keep going. I'm just gonna try to do the same. All right, we're taking the predictor. I'm moving it into components. If I now look at my application in RubyMind, I have predictor in here. Now predictor doesn't do anything, right? It's an empty gem, but it was nice enough. And again, I'm sorry, you have a different version of this bundle. So it asks you a question. For some reason, it didn't ask me a question. So we want it to extract just the prediction. So we're gonna look for models. Here's the predictor spec. I'm moving that into the spec here and just overwriting what was there. And I'm going into models and I'm taking prediction and predictor out and moving them into the namespace predictor inside of the gem, okay? Is that clear? Stop me at any time. You realize you can just start talking. So if we now go into this gem and run specs, it's blowing up. The error I'm getting is unilitialized constant app. And that makes a lot of sense because we just took something out of app and moved it into the module predictor, which is what the gem has. And obviously that doesn't exist. This particular error comes out of the predictor spec. If you wanna do this test driven, you just fix this one thing here and run the tests again and it's gonna fail on some different line. It doesn't know predictor predictor. If you don't wanna do this test driven, you can kind of just start searching for app and replace it with predictor. So let me do that here. So my tests kind of look okay, right? There is one caveat, sorry, that's garbage. No, there's many caveats. One particular one right now is that predictor being the class name is also the module name which confuses Ruby. So in this particular case, and there's one more, you should use the colon, colon in front of predictor. All right, let's go to the prediction. This is where it's going wrong. Or in fact, if you run the tests right now, it's going wrong somewhere else. It doesn't know predictor. Why is that? Gems don't auto load stuff, okay? So we have to actually require those two files. So I like to put my local requires here. Whoops, require relative predict. This is to the prediction predictor. If I run the tests now, still same problem because, well, this is still the wrong namespace. Fixing the namespace in these two files, tests run and they're read. Does anyone so far get to this stage? I have no idea how complicated this is if you have never done it. Okay, did these steps make sense? Do I need to go over one of them again? Okay, all right, try to catch up. The thing I didn't meant yes, because we don't have to make it a mountable engine. Thank you for asking this question. Why is it not a mountable engine? Why is it just a gem? The question should be the other way around. Why, Stefan, have you been so nice to me that you didn't give me all the crap that is connected to an engine? Why can I live in this wonderful world that is only a gem? Because only I screw up my world engine. So many dependencies come with an engine. Don't add it if you don't have to. And we had this discussion earlier that the predictor feels like it doesn't need to know about Rails. It seems to care only about a few things about these things called teams and things of games. So we're just trying right now to make this thing just a gem and not include everything in Rails. If you have a lot of gems, components in your app and they don't depend on Rails, do you know how easy it is to update Rails? None of your app cares about Rails, so it's gonna be really easy to update it. There were two questions, yes. Yes, great question. So are we moving every... Yes, are we moving everything of predictions into this new component? When the comment was made over here that the prediction engine feels like it is a separate part, I should have clarified that I considered that to be the prediction, the thing that makes the prediction, that drives the prediction. So what I want you to extract, and what I just did extract, we're only predictor and prediction, not prediction controller and not its views. We'll get there later. Also, if you get lost at any point in time, look at the Git history of this project. You'll find a branch called Workshop. It has 10 steps. We're trying to do step one. If you just wanna kind of see what it will look like, check that out while we're going over the rest. Yeah, sorry I didn't clarify that. I wanna do that later. So this is only kind of, as I said, the core of this application is to predict the outcome of a game. The delivery mechanism in Uncle Bob's words is that it has a controller and that you can see it. So let's separate those two first. It's kind of the intent of this first refactor. Yeah, yes. Are we expected to put our spec into the dependencies of this component? Yes, make it a development dependency in that gem spec, which will make sure that you have, when you try to build just the tests of that component, that you'll be able to access our spec. Oh, sorry. So again, in a gem, you don't get the Rails auto-loading and you, since Ruby 1.9 something, you can use require relative. So you load those two files to get access to predictor and prediction. Okay, is there at least someone who's making more headway now? Yes, good question. Yes, another pitfall. So you can see that the folder structure between an engine and a gem is not quite the same. Engines are the core of every Rails application and they tend to, it's automatically supported that you put stuff into app and in the module they're under. Gems, on the other hand, are libraries so they historically put all their code into lib. Now you can make the reverts work with either one, but yes, the easiest thing is to take it out of app, models, prediction and predictor, and put it into lib predictor in the gem. So with these moves that I did, so I had to slide up earlier, I moved the two classes, I moved the tests, I made sure RSpec was there, I ran the tests, I changed a couple of module names from app to predictor. By the way, people will say that I'm doing Java when I do this kind of stuff and then you could answer yes, and in Java this would be easy because with static typing it's much easier to rename all these modules. So that's why we have to do search and replace and don't have those niceties. But we have a different problem right now here up on the screen. So just these renames led to the problem that we don't have access to this create team. This is something in the test. If you go find the source to that thing, I'm not a huge fan of factory girl and those other object mother libraries. I find it's such an easy pattern that I kind of always write it myself. So create team is a short method that saves a team that we've nued up. But as you can see here, so first off, the gem didn't find this method. So we could add it, but then we would have the problem that you see in line six because that's actually creating an app team. And I was saying earlier, when you create components you have arrows and they point in one direction but they don't point back. And remember, we're extracting the predictor. App is gonna depend on it. Predictor will not have access to any of the things that are in app and it shouldn't. But we also said that prediction doesn't really care about anything about team and game. Let's look at what it exactly cares about. Predictor cares about, so you give it teams when it initializes. So here's how it learns, right? You initialize it and you give it a bunch of teams. That's kind of, as a setup, as a lookup. As you can see here, it kind of makes a hash. In fact, it's really crappy code. So it cares about the team ID. That's how it does the lookup. And then it stores the team in line six, but we don't know if it does anything with it. And then if you look at learn, it takes games and it cares about the IDs of those teams again. So it cares about the IDs. And then it cares about the winning team. This is line 17. Here, this stuff. Right, it cares about who wins. And then it cares about the ID when it predicts and that's pretty much it. Then it has some internal stuff, but it doesn't care about these things being active record. So I suggest that we can change this test to be something like this. As I said, I think what the test cares about is that there is an ID on this team. So why don't I give it something? This is duck-tapping. Why don't I give it something that has an ID? Does anyone fundamentally disagree with this? Cool. So there is, of course, more. Here is a new game. So I'm gonna do the same thing for the game and in fact there's a couple more down here. Let me throw that in there and just run the test again and then go into the spec helper and require O struct. Okay, error changed once again because I can't spell. So, and to your question, right, if you can do something like this where you now got rid of the entire dependency that is active record and you never have to worry about instantiating a database, again, predictor doesn't have a database, you don't need to run migrations, nothing gets stored. These tests are gonna be really, really fast just from this one change. So you could argue, I asked about if someone fundamentally disagrees with this because you could argue, but now you're just assuming that open struct, this thing is the same as a game or a team and that's not really safe. Well, we have tests around it, right? App tests what it's doing with these teams in games. So that's one argument for it and if you're really uncomfortable with it, look up bounded context objects. What we could do instead of this is we could create a prediction game class and a prediction team class that get newed up with something that has an ID and with something that can kind of show if you win or lose and you use these two things and new those two things up in the test and the app will change its usage of this predictor to create these bounded context objects and passes them in to when it uses the predictor. So that's an alternative you can use if you like to see more classes. The error we now have is that we actually had a dependency in app that we currently didn't give predictor and that was the actual prediction engine, the external one that we currently use. So it's also this nasty thing here. So as I said, we can't remove this dependency line from app because it is not a published gem. So I'm gonna throw this into the gem file but as we said earlier, we also need to have this in the gem spec. This looks really nasty, but there are no runtime dependencies in this gem but now there are, namely this true skill thing. Let's run the tests again, nothing changes. Gems don't auto load things. We need to add this true skill thing and in fact we can probably also remove it from app which should be loading it in its gem file. So if you've never worked with gems, all of this is really, really weird. If you've done gems, this is all just normal kind of gem behavior. We're just refactoring a little bit faster over these gems. So I can remove the require from app because app in fact can also remove its dependency on true skill. True skill is now an implementation detail to the predictor engine. We can, yes, the others will depend on it transitively but they won't know about it directly. And once I add the require up here, I have some app somewhere. So let's see where that is. Line 28 in predictor. There's app. So app prediction obviously wrong. This is that moment where these two colons here at the front are needed because of me naming the module predictor and the classes name predictor. And there you go. Has anyone got these green now? Oh, I put the require for what? Oh predictor, the require for the external prediction engine I put into the libraries. I don't know what you call this file actually. This is kind of the file that you require that gets required first when you require this gem. That's where I put the require. I don't like requires kind of splattered out all across files. So I put them, I kind of up front load everything. Okay, any questions up to this point? We basically just extracted a gem. Yes, predictor gem spec. Oh, thank you. This is not gonna work, correct. Yeah, that's funny because we didn't bundle. So yes, that was, my gem spec didn't compile, parse, run, whatever. The test passed because we didn't bundle again. Had we bundled, it would have blown up. Now that we're bundling, we're pulling in the dependencies. And so one thing I suggest we do to kind of clean things up is we take this test here. I'm just gonna copy it because I'm lazy. We dump it into the gem. And now obviously we need to adapt it because we're not gonna, we're gonna maybe do bundle install. Yeah, that would be nice, but we don't wanna do anything database. So if I now come back out to the main application and run build, what did I say? Test outward. So we're going to the app component and running the tests here. They're not gonna pass because obviously we never renamed predictor in the app component. So it's gonna be in the controller. We could test draft this or just find all the places real quick. So in prediction controller, we're referring to predictor, that's no longer found because it is now predictor, predictor. That's not the only thing that's going on because we have a new dependency as app and that is the predictor. So we need to add it to app the component. We need to add predictor and we need to also make sure that the app component knows that the predictor component is in the path. And I was showing you earlier that you can do that with path as an option. There is a nicer way to do that, which I suggest you use all the time, which is do block. So you can say path do and I give it the path where it will find components and I say gem predictor. And that still is not enough because gems don't auto load anything. So I need to require the predictor in the app component. We'll see if I got it right. No, I didn't because there's tests that are also using app predictor. So I have one test failing because it still refers to app prediction. That is right about here. So we're working our way outward, right? Move everything into the gem, run the tests. They pass inside of the gem, move one level out, make sure we require it, make sure we rename what needs to be renamed, run the tests again. And if we did it right because this is otherwise a truer factor and the tests on the app level are high level enough, I think I can run the build and all of the test suites pass. And you see it just added the tests for the gem. It added it right there. Whoops, what happened here? So this is where, yes, the test status age for the gem file, sorry for the new predictor gem, I just copied it from app and removed everything that referred to migrations. Yeah, it's the same test status age because we're just using RSpec really. But sometimes it's helpful when you have RSpec and you have maybe feature tests that run slightly differently and then you have Jasmine or something that tests front end. So you have like multiple things so I wrap them all in this test status age. All right, this is dangerous. What happened here just now is that we installed predictor 2.3.0. Oh, I shouldn't use that, should I? So this might be the most problematic thing about doing component based rails. Bundler is all set up to pull everything from Ruby gems. So if you forget to tell the application that what you're looking at and what you're wanting to use is a local gem and you're not pointing to it, because remember, I never went into the main applications gem file and informed it about the fact that we now also had a predictor. So it just went out to Ruby gems and funny enough because this name is generic enough, it found a predictor and we've now pulled that into our app. And in fact, you would not find a single breaking test, actually you would, you would. There is a test breaking, ooh, happy about that. So there is a test breaking because that predictor happens to not define the class predictor predictor. And because it doesn't do that, the main app, which I had said, oh, I'm covering this glue code here when you looked at AppSpec. If you look at the AppSpec in the features of the main app, it actually does test a little bit more than the glue and this is the reason why I added that because I knew I was gonna miss it today. So it's testing a little bit more to verify that we have the right kind of predictor. So to fix this, we can do this, but I just showed you the path block. So it's much nicer to give it the block than you can remove these paths. We're going to uninstall the predictor, we can hopefully bundle. And we see here, it takes apps from our components and here it takes predictor from our components. And in fact, as I said, with the path, you don't need to specify transitive gem dependencies. So we can just bundle this and it will still pull predictor from the right place. How long is this session? Does anyone know? 520, okay, pretty much. Cool. Questions, this one? I removed the predictor gem from the main gem file because the path do block is smart and it knows to look for any transitive dependencies in that path, so we can remove this because the main app doesn't depend on predictor. The main app, let me go back to my slides, the main app depends on the app and that depends on predictor. So you wouldn't specify, if Rails depends on active record, you don't specify active record, you'd specify Rails. And the same thing with the do block is possible for components. All right, so path do require the gems. There's no kind of, no changes to the production code. I think this is wrong here. Oh, sorry, in the module app, the only production change we have is that we need to refer to the right predictor. Open struct this little trick, which works in this case, might not work in yours, but getting rid of dependencies when you don't need them is a great idea. All right, so I don't know if anyone feels this is not relevant for me at all. I hope you will see it is. If you have good tests, so you can refactor, if you have unit tests, functional tests, if you have a couple layers of tests, you will be able to perform such refactoring. I've done it in code bases that were hundreds of thousands of lines long and you just find a smaller component that you can tear apart and you just tear it apart. And when you've done it a couple times, it will feel more natural. I've seen teams that start having templates for their components because they're kind of always the same so they kind of use the template and go off of that. We keep running the tests, right? You saw me doing this, I didn't know what was gonna happen. You'd keep running the tests and ask the code what's wrong. You run them inside out because if you don't run them inside out, you're just gonna get so much test problems that you're just gonna be overwhelmed. Start with the gem, go to the things that depend on it, so go in the reverse direction of the dependency tree, go up the dependency tree for your tests. And then where you can remove dependencies. We didn't need Rails for this prediction, we shouldn't have it. So the predictor is now completely free of it. If you wanna update, in fact, we could extract predictor, publish it to a gem server and use it in multiple applications and they would indeed not care about Rails. It's also kind of this single responsibility principle on a higher level, right? The least amount of reasons to change. When we can achieve that, well, if we don't have Rails, there's a lot less reasons to change. So what else is in this application? Is there more structure in this application? And if you're feeling, oh, this is all very academic or very labsy, yes, because it's a small example. But you can apply the same thing to larger applications. There is more structure in this application. If you look at what is actually going on, you see that the team controller doesn't need to know that games exist. It cruds teams, anyone disagreeing? Does it need to know that games exist? Because it doesn't. In this application, it does because this blue box is currently the truth, right? It's not these little boxes in there. The game UI needs to save teams in a way because it's storing their relationship as it relates to games. The prediction UI kinda needs both of them, right? Because it needs to show you the teams, it needs to access the games to make the prediction. The predictor, however, is now kinda out of this game. Given the time that it took to do these refactorings, we'll do the other ones just up here. We'll check out code and take a look at it. So the mantra is if it doesn't have to be connected, if it doesn't have to be together, it shouldn't be. If you don't have to entangle it, don't entangle it. In fact, that's my opinion is whenever you have code that can't touch each other, it will. There were a couple people who raised their hands and said I have very large Rails app. When you look at your app and you go into the lib folder, have you ever checked whether you can delete the Rails app and delete all the tests that are not pertinent to the lib folder and run the tests for the lib folder? Do you think that's going to work? It's not going to work. The reason it's not working is because your lib folder is entangled with your application. If you truly wanted to have a library, you put it into some component like this predictor and force that the tests run on their own. Even if you have no active record or whatever in there, try it, you will have, I don't know, hash within different access in there or something and suddenly you have a dependency on Rails. So what can be separate should be separate. You also saw something that was very interesting. It's frickin' painful to rip these things apart. So if you can create something new and start immediately with a new component, you don't have to go through this pain. It's super easy to take two components and smash them together because all you do is search for that one namespace in the one and merge it all into one namespace. It's really, really easy, trust me. If you're doing components, it's super easy to do separately two components right off the bat. It's really hard to extract them like we just did, which is also why that took so much longer. But I said this wasn't going to work at the outset. So if we wanted to make these internal arrows that are kind of put in there, if we wanted to make those happen, we needed to make every one of these things into a component and this is where truly, I believe the predictor is a finer factor and I would probably do it in many applications these days. The next ones I would probably not do because they're a little bit small. But you could argue there might be such a thing as a team admin. The only thing they do is scrape the newspapers and admin teams. I actually want to give them a separate UI. How cool would it be if this here were one application and it didn't know about all the other stuff? Sorry for you guys on the left. So if teams UI and teams were separate and didn't have to know about the other stuff, less reasons to change. So this dependency here, what if we could actually make this happen? And we can. So when we try to do this and we push persistence down, which is the first step. So we take teams and games and move them out of the app component, like so. This is where we will have to use an engine. So Rails plug in new. And I realize that it takes too much time. So let me just check out a version of the code that has both teams and games extracted into components. Maybe this is the reason it takes so long. All right, if you check out tag number three, you should see four components inside of components. So, and if we look at games, let's just take one of the two and look at them. They're structurally very, very similar. The only thing they have is a models folder because the only thing that these engines need are the models. I said I wanted to extract the persistence down. All right, this game is otherwise unchanged. Same stuff. There was a reference to the team's class name here. So that needed to be changed to point to the team's module name now. Again, we do have the migrations in this engine. There's another cool side effect if you leave the migrations in the engines. As I said earlier, you can move them around. So this migration is just the moved file that was earlier in the app component. It's just moved over here. To be able to do that, you should adopt the rule of one migration per table because if you have multiple active record models and you're not sure whether they're gonna end up potentially in two different components, you need their migration separate so that you don't have to go through a whole hassle of migrations back and forth to separate out the migrations. You don't touch the old migration. You can't, right? Remember in production, this code's already running. So you just add a second migration that does the rename. And when you know, in fact, if we look at teams, it has exactly the same thing in its migrations, right? So make one migration per table and this will always be possible no matter how complex your app gets. What changes with engines is that we get, well, the engine code. It uses isolate namespace so that anything inside of games is in the module games. The lines five to nine are the same that we had earlier. They will show up in every engine that adds migrations to the application. I also, I had showed you before that I don't like to use factory girl and I showed you my hand rolled object creation method. When I extract teams and games, in fact, how are the controllers that are now in a different gem, how are they gonna new up teams and games if they need them for their tests? Those object creation methods shouldn't live in those controllers because that's not their domain. But you've seen this in other gems. Why not expose a games test helper? So the games test helper has an object creation method inside of games, which only can create games, right? New and create game. And if you look into app and its specs in the spec helper, here in line 10 and 11, sorry, 11 and 12, it requires the test helpers from teams and games. So it now, in effect, the games gem and the teams gem, they were nice enough to provide their own test helpers. Something that is good libraries will always provide you something like that. So here we're doing it for ourselves. So let's see what else we need to talk about for these. So the process is otherwise very similar. So you're creating an engine and these, these bgvsjt means skip bundler, skip git, skip, what's v? Skip views, skip views, skip sprockets, skip JavaScript and skip test unit. I'm skipping basically everything because this engine only cares about models. And I believe if I remember correctly, this engine doesn't care about rails. So I actually made it dependent on active record because it doesn't need active support, active, all the other active things, all the other adequate things either. So only thing it gets is active record. Again, as few dependencies as you can have, create them. All right, there is a dummy app that gets created with engines. That dummy app makes people say this is a really bad approach because the dummy app is so large. And that's true, the dummy app is very large and it gets auto created. There are people who try to avoid it. There's a gem I think that gets rid of this dummy app. You can see that this dummy app is massive. What it is is, you can see it's in spec dummy and the spec helper in fact does not load the engine. It loads the dummy app's Rails app which in turn has a requirement onto the engine. It's just the way you test an engine. For the tests, you put it into a skeletal Rails app so you can run tests against it. You can get rid of it, I often don't do it because the dummy app never kind of hurts you. It just kind of sits around and it's just obnoxiously big but that's really, it's only problem. Yeah, I already showed you the renames that we had to do. I talked about the test helpers and how they're used, how they're exposed by the engine that has the content and exposed to other parts of the application that need to use those parts. So if you have checked out, tag three, then you have this structure, right? In fact, so what's in the blue box is only obviously one thing but conceptually we're still thinking there is more there. So because we couldn't do this in practice, there's two ways to do these engine extractions. Again, it's nicer if you start creating the fresh, best thing. Second best thing, decide whether this engine is gonna be very different or very similar. If it's very similar, instead of going through the motions of creating a new plugin, if you already have a component, it might be easier to just duplicate the component, delete anything you don't need from the new component and the old component and start renaming in the new component. It's sometimes just easier. It's basically kind of a blueprint approach where you just kind of, this is all the stuff I need anyways. Don't mess with migrations, you need to move them over and then you add new migrations so that you can change the names as appropriate. The test helper approach I think is a really nice one. And also, had I shown this, I would have tripped up on trying to refactor away from this name app the entire time. So let me get on the soapbox again for a second. If you feel like you need to name your engine common, base, miscellaneous, general, lib, your company's name, anything like that, just take everything, craft random, don't care, don't know, or duh. It's about as good. Sorry, these are proper module names. Give anything you extract into components the most specific name you can think of. A good name, one that will start a good conversation. Continue to refactor it. Once you're in the habit of doing this, it's not that hard to refactor these things. And then continue to talk about it. I was once on a project that was one of the coolest projects I was ever on, and it ended up with 40 components. And there was continuously one pair, we were, I think, four pairs, and one pair was continuously working on just refactoring stuff. And we still had one of the highest velocities I've ever, it felt like one of the fastest projects I've ever been on. Because the refactoring were good for the health of the code base, and we were able to create a lot of code despite, or rather maybe because we did so many refactoring and kept these concepts clean. And this section, I'm sorry, I skipped over a couple, app is a terrible component name, it's time we get rid of it. So if you check out number four, this is the slide I'm looking for, it's renaming app to web UI. I know it doesn't, it's not a really good name. It's still web, what the hell does that mean? At least it captures, however, that this thing is concerned with the delivery mechanism, like again, this Uncle Bob thing. This is the UI as delivered by the web, and it contains the controllers and the views to do so. So if you check this out, you'll see we're now calling it web UI, and that actually makes a lot more sense and you could stop right there. But again, I said we'd keep going and we already found team UI, game UI, and prediction UI. Without looking at the code, does anyone see a reason why we wouldn't be able to just kind of vertically split team UI, game UI and prediction UI, out of web UI? Say again, which ones though? So the question was, aren't there pages that need to reference multiple? Multiple of what and which ones? So what is true about your comment is that the pages we're looking at contain a reference to teams, top left, games, center, and predictions. So these things are kind of connected. How did I do that? So they're somehow tied. Also, there's something even more obvious. They all share the same UI. They share the same application layout. So you can see that doesn't change. If you look at the source code, you cannot currently extract these things from each other because, essentially because this file exists in the app component. And if we would try to extract, say, games UI out of the web UI, how would it get to the layout? It couldn't. So before you can extract any of those UIs into separate ones, you have to extract layout and style. This is a very common place where people will start calling that base styles or something like that. Layout, just very generic. Don't do it. Try to be more specific. I'm not much better in this case. I called it web style. If we check that out, it's a really easy refactor. Web style only contains the application layout, a couple assets, CSS, JavaScript, and that kind of stuff. And it doesn't contain tests. Check out five. All right, we've gone over the basics. Now it's not a web style. It contains only assets and views. So there's that image, that logo that we've seen. There's application.js, application.css. And here's application.html, right? So now we have an external component that if we split something off of app, we can depend on. Because in fact, app already depends on web style itself. Oh, sorry, this is just a, I meant web UI. So web UI depends on web style. And the big change there is that in the controllers, sorry, in the application controller, we're saying please use the layout from the web style component, all right? So, and to go into web style again, there's only the engine code that loads it. There are the assets and the views. And there are the dependencies that are Rails, use slim here and jQuery. Right, very small engine. And it's literally just moving out, renaming all the folders that you're moving from web UI to web style. So that everything inside of web style obviously needs web style here, here, here, and so forth and not web UI. And the dependency graph looks like this. Now that we have that extraction, now we can take prediction UI and move it out of app, sorry, web UI. And it can depend on anything it needs because it doesn't depend on the other controllers. And that, so this was web style. So we have remove everything but assets and layouts, remove assets from obviously the web UI. It's always the same pattern, take whatever you need, either you move it out or you duplicate it and then you delete it in the old place. In this case, no tests to write. What is important to verify is if you get all the loading right because one thing that I just kind of showed for a second, obviously the application layout is now referring to the web style application, JS and CSS. All right. So in the spirit of ripping all of this apart, we can in fact do this and move all of these to be separate because they live next, they can live next to each other because, oh, that's what I wanted to show you. The trick that has happened for this checkout is this here. So web style uses these paths and that will only work in the main application. In fact, I don't even know that I ran the tests for this because that looks to me like it won't work. We should check it out later and verify that this works. So the problem is when you extract web style, it has this navigation section in the layout that suddenly is no longer in its dependency path, right? Web style contains application, the template, but it doesn't know about team UI, game UI, prediction UI existing. So these couple lines here, they break the dependency pattern. There should only be dependencies down and this thing makes assumptions about the world that it shouldn't. Let me jump to the very end of the refactorings. How did everything we could do 10 refactors? So at the very end of these refactorings, there's a very simple way to fix this and it's a very important one to know about for components. If you cannot depend on something, how do you get information about it? This is the final state of the refactorings. It turns out when you split these three things off, there's actually one thing left namely the first page you ever get to, which is just an empty page, but you could split it off too, so I did that here. So if you check out the workshop branch, you will find team UI, game UI, prediction UI, welcome UI, and all these other things. And the problem of how to glue together this navigation menu, you solve with what is essentially dependency injection. So the web style has the application layout, but it doesn't know what things are gonna be part of the app that need to be shown. So in the main application, I just set a config variable here, main nav, and it gets a nav entry from everything that wants to show itself, if you will, in the nav. If you look in here, this is just on the module. It's a name and it uses the route helper to find one path. This is for teams. Again, the config variable gets set in the main application in this case, and web styles, where does it make use of that? Right here. So web styles just goes over the configuration and shows the links. Now, web styles here says rails.application.config. So web styles expects and will actually blow up if the main application does not define a config called main nav. And that's why I call it dependency injection is because you're configuring something over here and it's just expected down here. So it's not really injected, but it's expected, so you should write a test around verifying that this works. So the way to test web style now is to create an empty dummy app, but make sure that this configuration variable gets to something, so you can test in isolation that the navigation construct at least works, and when you pull it into the main app, you should test that everything still pulls together correctly. And I think the app spec never really changed for this application, so it still navigates to all those pieces, so throughout the entire process, this test made sure that the entire application worked as expected. When you have done this, your test suite looks a lot different from what a normal test suite looks like. It's gonna continuously create migrations because there's a ton of things that need to create migrations. All these separate engines, you know the dependency path now, or the dependency tree, everyone that is above teams and games needs to run migrations, but they all just run a couple tests, and those tests typically run pretty fast. Incidentally, when you run this test suite on Travis, it's very nice, because you can do this. You can tell Travis, you can use the gem file option that you have in Travis to point it to every single gem file of every single component that you have, and it will just run all of your test suites in parallel. So this is a build of this state that we're looking at right now, this checkout of workshop for this application with Travis right now. It has a build for every single one of these components, and we can see that for some reason welcome UI, that's hilarious because welcome UI doesn't have any tests, it takes five and a half minutes, but it's because of this Travis problem with loading gems so you have to do a retry and probably get stuck there. So all the test suites themselves are very small, and now we have to kind of, I changed this whole workshop up a little bit because the actual refactoring takes too long, but what in your application are these parts? I wanna talk about that for a little bit. So if anyone kinda would like to share, I have this or that, right? Please go ahead, but I'm just gonna kind of make stuff up that I've seen time and time again. If you have, in fact I was talking to someone over lunch today. So he was looking at TV channels and content for it I think. What he will have is someone who can admin people so that those people can admin channels and maybe other people that can admin particular shows. And then there is logged out users who can see this content. Super admins, channel admins, show admins and logged out users. Those are perfect verticals. The user interface, which is the website or whatever you wanna call it, will probably look totally different than those admin sections. But those admin sections could also be separate from each other so you can make verticals out of them. Similar to what we have here, team UI, game UI. I tried to say this earlier. What if there's a person, an intern that just inputs all the teams? They may never touch the games because that's way too complex for them. So it's a separate UI. So in your app, these things will be larger. They will indeed be something like a channel or they will be accounts or tenants or whatever. Those UIs, you can split off vertically with components. And then you'll find as soon as you try to do that, just like we found here, that you find common things like teams and games. They're used by multiple of these verticals. So at first you kinda think about your applications. How can I split this thing up into vertical pieces? And then you realize you have something common and then you need to slice it horizontally because you need to pull out what is common in our case teams, games, and web style so that everything that gets vertically split can depend on that if it needs to. And as you keep doing that, you'll find it's really easy to slice and dice in those things. If you get into the groove, you make more and more of these and in the end you can create really large component structures. Yes, that's a very good point. You could argue, so the argument is why not leave the web styles in the main app? And you could in fact do that. You could create the layout that the engines use, I believe anyways, it should be possible. You could create that in the main application. Yeah, I've never tried it. In the components then, you would have a weird situation where you don't actually see what the component is gonna get rendered into once correctly gets rendered into the main application. But you're saying much like the application glues together just the mount points of these engines, it could glue together the UIs of those engines. I think that's a very good point. You could say the layout belongs there and then what you would test on the main app layer is that this application layout in fact does pull together the components like we would like to pull them together. And if you can in fact run any of these engines, you can just fire up a Rails server. The dummy Rails app inside of an engine, you can run to see just that component in action. You know, it's self-contained so you can run it and that would look different than the main app because it didn't have access to web style. I think, I don't know, I'd have to try it if it looks nicer but I think it just might. It's a good idea. Yes, but now you're adding one layer of complexity. So in his product they have extracted styles into a gem and they have a constant struggle to keep all the applications using the same styles and keeping that up to date. Obviously that's something I recommend not doing as for as long as you can because this is still one application. If someone finds this predictor terribly interesting, I might entertain the thought of extracting it and actually publishing it to a gem server. Web style, I would probably never publish to anywhere. Now, if you have multiple apps and they should use the same things, then you start having these conversations. But you see why, even if it was kind of painful to try and do the first for factoring, it took me on stage nervous and typing at the wrong angle 15 minutes to extract a component. And why is that possible? That's only possible because you keep this code in one code base. You don't mess around and muck around with publishing to gem servers, updating gem versions, and pulling down gems and pulling down all these applications and components from different repositories. It's one repository, so it stays one application. Yes. So he has a very large application. Did I get that right? And a couple of engines and those are all separate repositories. So here's a, I'm a consultant. So here's a free one. Take these repositories for every single engine. And if they're not used in multiple applications, which these large engines typically aren't, download them, put them into a components folder, delete their Git folder, commit it to the main application, add the path, change nothing else. Just add a little bit of the test suites as we were doing. And tomorrow you'll just be able to actually manipulate engines within one application. So you can refactor this thing from that engine to that engine rename this engine over here. And you don't need to check out five things. And when someone new comes onto the job after the first day of the like, shoot me now because there's so much stuff you have to remember. That's the first and easiest thing to make you much more productive if you want to split up applications. And I think it's one of the things why we believe Rails applications can't be large because we do stuff like that. It's completely unnecessary to make, to put these things into different repositories and I highly recommend you don't do it. Pull them back together, it'll be much easier. And that's just the first thing you can do. As soon as you then find stuff that you want to refactor, you have one code base. You've just seen how easy it is to extract a gem. So you extract a gem and it won't even affect deployment. That's something I didn't mention. Deployment for an application like this to Heroku, exactly the same. There's no change whatsoever because you have a gem somewhere down there. Heroku completely, transparently deals with this path command as you push. Not a big deal, yes. Yeah, are there performance implications of this? You're using Ruby, so good luck. No, so a Rails app is an engine. A Rails app is an engine plus essentially what you see in the dummy app. Now you have multiple engines so you have a bunch more classes, yes. So you generate a couple more classes. How many classes is that per engine? Five classes, that's the overhead you're loading. You're loading five extra classes. On the flip side, however, we have a dependency graph like this so we can start playing around with it. I'm trying to see if I can find some samples. Don't look at the company names, please. This is the largest, whoops, the largest component graph I've ever created. It was very, very successful. It looks terrible. That's because you can see how complicated this application is, which every Rails app tries to hide from you. Because this structure is present, you just can't see it and you can't talk about it. Now what you can do with this, if you wanted to deploy your application to this server over here, in fact, this is also this thing where you have multiple admins. Sorry, I'm talking to the wrong person. What you can do with an application like this is deploy it and you can change your gem file. Obviously the gem file for this has tons of gems in it, right? You could put a little magic in your gem file and deploy only or load in one context only one of the admins and load only the other admin on the other server and load only the web UI. So it's a very easy way also to kind of get tailored applications. If you need to scale one part of it, just make something where the gem file only loads that part and scale it really big. You don't need to load the rest. I think the performance implications are really small. And then there's also another comment I should make, Enrico Teati, may I wrote a blog post a couple of months ago about building that in? So using bundler and environments to basically reduce the footprint so that you support this just through the gem file. So I think, yes, there is an overhead to this. To any structure there's overhead because you need to create and maintain the structure. The benefits from that structure will outweigh, I bet you, in most situations, their cost. So I'm writing a book about all this. I keep running around for the last couple of years believing that this is the way you should write Rails applications. And so I said I would write a book about this. If you go to this link, I'm publishing it as it's in progress. You basically just, the refactoring is that you just heard. I hope to be used for the next chapter to show sample and standard extractions. So a gem extraction of a domain, the extraction of the persistence layer, the extraction or separation of UI components. So if you're interested, this is a coupon to where you get it for 50% off. It would be great if you could support me in that because it's a lot of work and a lot of people think I'm crazy for doing this. So one page that kind of collects most of the information that I know about writing applications like this is this URL. And that's it, that's it, yeah.