 Welcome to a relentless pursuit of rest. My name is Derek Pryor. I'm a development director at Thoughtbot. I work out of our Boston office. And I'm super excited to be here, kicking off RailsConf with you all. And I do feel like I won the lottery because I get to go first, and then I get to enjoy the rest of the conference and see all the great talks with you guys without having to worry about this presentation I have to give. My Twitter handle's right up there on the screen, so if you have feedback during this talk or after the talk, you can tweet me there. Meet me in the hallway. Later on in the week, I'll have some Thoughtbot swag to hand out so you can find me later. Give out some cool notebooks and potentially some t-shirts and things like that. So, ostensibly, this is a talk about rest, right? So, what is it? I'm kind of just kidding about that. I'm not gonna recap Roy Fielding's dissertation here. There's been plenty of conference talks on this. In fact, it was alluded to at the opening keynote that this is the 12th RailsConf. And at every single RailsConf, there have been at least one presentation about rest. So, why should this RailsConf be any different? In addition to those conference talks, we also have probably hundreds of blog posts developed by the community. Probably, I don't know, half a dozen books or so and hours of podcast material, some of which I'm responsible for. And a common refrain among a lot of that content is that somehow we're doing rest wrong. And this can be any number of ways that we're doing rest wrong. As programmers, a lot of us tend to love semantic arguments. And rest seems to have plenty of them for us to have. So, we do. Rest is all about the power of HTTP on some level. So, there's plenty of arguments for us to have there. Do you have an API? Do you version that API with a slug in your URL, perhaps? Should you instead be using the accept header and content negotiation? If you were gonna be more restful, maybe. A few years ago now, there was a pull request to Rails to change. Or to add support for the patch verb, because that was more semantically correct than how we were using put. And I remember at the time I had really strong feelings about that one. And I was like, yeah, we really should be using patch. And so now we can, and nothing has changed. And then there was this, hypermedia. It was kind of, I alternately read as like an extension of rest or a rebrand of rest. And it was all about providing additional URLs for clients to use to get more details about your resource. The big idea being that clients shouldn't be building their own URLs, you should provide them for them. And then, whatever this is. I think it has something to do with hypermedia, but to me, it just always sounded like mean Cheerios. Thank you. So, the secret is that none of that stuff really mattered as much as I thought it did. Those really strong feelings I had about patch, like I alluded to, nothing changed when that became a reality. None of those things make me a happier or more productive developer or make my application more successful. Most of us in this room, I'm guessing, work on a single app or maybe an API with a small number of clients that we control because we also write those clients or we work with the people who do, or we have a business agreement with the company that does or something like that. And in those cases, in all cases really, it's consistency that matters. We can make those decisions that I alluded to before and make them consistently and we can move on. The rest of those decisions are rather academic. And that's not to say that you shouldn't care about them at all. You should have opinions and you should discuss them. But I believe that the importance we put on them is outsized to their benefit. So, this is a talk about rest. So, what am I gonna talk about? I wanna talk about how rest makes my code better and my job easier. How it makes my code easier to read, easier to maintain, easier to extend, and how it can ease Rails app growing pains that we all experience. To that end, I'm actually gonna spend a lot of the talk talking about nouns and verbs. In REST parlance, that's resources and actions. So, resources being your domain objects and actions being the things you can do with them. This is the very, very basics of rest and certainly it lacks the marketing appeal of something like hypermedia. But we're gonna use our focus on resources and actions to drive us towards creating more small things. Because it's small things, small controllers, small models, small views, small objects. Those are the antidote to all of the Rails anti-patterns that I encounter on application after application. So, yes, in this talk, we're gonna talk about some common anti-patterns that I do encounter on a regular basis and I'm guessing that you do as well. And we're gonna talk about how restful design helps us to avoid and correct those anti-patterns. So, first I wanna talk about custom actions, right? There's nothing special about the seven default actions that a Rails scaffold controller gives you, right? You've got new and update and create and all those other ones. Fielding certainly didn't bless any set of actions. Maybe it would have been best if he did, but he didn't. But this is a case where strictly following that convention over configuration that David talked about is gonna help us to avoid many of the pitfalls. It's the first step of avoiding a lot of these anti-patterns. Before I get started with that, I just wanna take a quick second to talk about how I get started in a Rails application. So, if I'm coming on to an established Rails application, the very first thing I do is open up routes.rb. Because a good routes file can tell me a lot about your application, right? It can tell me all of the capabilities of your application and do so very succinctly. And it can tell me about the language of your app, not Ruby, but like the business language of your application. What are we actually working with? A bad routes file also tells me a lot about your application. Tells me where I'm gonna find large controllers or which code I may need to read in order to understand what it is your application does. So, I thought we could start with a slice of a routes file that obviously a simplified slice of a routes file that I commonly see in Rails applications. So, here we can see on the first line we have a singular user resource. So, right away, just from experience, that tells me the singular user is likely the current user and we support an edit and update action which is them editing their user record or updating their user record in some manner. Perhaps their user profile, something like that. And then there's these two custom member actions that are just sort of hanging out here. They seem relatively harmless, but I immediately wanna know how to edit password and update password. How do they differ from editing and updating a user? And why are they on the user's controller? So, I immediately wanna dive into that user's controller and see how things, why those choices were made. But before we do that, I wanna take just a quick pause and look at what those routes, those member routes in the middle there would look like if we wrote them out longhand without the use of the resource as helper. So, it looks something like this. And I just wanna focus on the controller mappings there. If we break those out, we have users edit password. Users update password. And there's a pattern there. That pattern is noun, verb, noun. So, that pattern's a dead giveaway that we're missing a resource. And I see this in a lot of applications. And then the resource that we're missing is actually right there in the action name. The resource is passwords. So, we have passwords edit, passwords update. And that pattern is the more familiar noun, verb pattern. We're manipulating a password in these actions. We're not manipulating the user. The fact that user is, the fact that password is ultimately stored in the user's table is inconsequential to the resource that we're dealing with. So, we can fix this in our routes file by creating a password resource, another singular resource. We don't need to create a password model for this to work, as I was saying. And here we've put password in its proper place of prominence here. It is the thing that we're working on. It is the thing we're editing. It is the thing we're updating. And we get to use our conventional edit and update actions. Here's what our passwords controller looks like. All we've done is copy over what used to be edit password and update password into a new controller and call them edit and update, move some views around, updated some helpers so that our tests pass, and we're ready to submit our first PR. And at this point, there tends to be some questions or possible objections that come up. A common one is that this can't be worth another controller, right? And I always wonder what is it that a file costs us? It's very lightweight, right? And ultimately, my response to this is that I've never once, in the many years I've been doing Rails development, regretted extracting a controller. I've never wished my controllers were bigger. I've always wished I had more of them that were smaller. So any chance you can take to create another controller, you're often wise to do so. And it's also often a first step towards larger refactorings, as we'll see throughout this talk. The second objection is more of a realization usually, not so much as an objection is that password isn't a model here, as I talked about. We're so used to there being a one-to-one correlation between active record models and our controllers that it can feel wrong somehow to stray from that. And as we talked about the Rails scaffold generator earlier, and that certainly encourages this one-to-one correlation. You generate a scaffold, you get a model, you get a controller, you get some views, things like that. And the books and tutorials written about Rails often reinforce this. When you want to introduce new functionality or change functionality, you just either generate a new scaffold or generate a new controller and a model, something like that. But the key takeaway here is that our controllers do not need to map to active record objects one-to-one. Our controllers can map to a single column on an active record object, or to just a plain old Ruby object that isn't persisted anywhere if that helps us for whatever reason. And this realization was a light bulb moment for me, and in speaking to countless other developers, they professed it also was for them as well. It's often highlighted as the one realization that really improved people's Rails code. Because what it does is it frees us up to build small objects to bridge between our controllers and our models, and lets us not expose the specifics of our model implementation at our controller level. And we can see an example of that in our next anti-pattern. So complex actions, what I'm talking about here is really long controller actions, perhaps calling out to several private methods, something like that. They tend to be very difficult to read, very difficult to test, and resistant to refactoring, because it's hard to break off pieces of them without messing up some other part. But the key thing to realize here is that oftentimes we can rethink complex actions into several smaller actions across several resources. And this is often appropriate to do. The first thing I wanna call out is that nobody comes into work thinking like, I really can't wait to write some controller code today. Right? It's just kind of a thing that happens. We write complex controllers over time. In the beginning, our controller actions are just the way we like them. They're super boring. For example, we may have a controller action that allows users to update the caption on a photo that they've uploaded, and that action might look something like this. You don't need to read every line here, right? You can just kind of get a sense for what it's doing from the shape of it, because you've written a controller like this before probably. And it's boring. It's wonderful how boring this code is. Right? Every one of our controllers. Unfortunately, it seems as if no boring controller like this survives first contact with our users or our product owners if we're not diligent about it. Before long, a new story might come along. Right? This seems pretty harmless. As a user, I want to mark a photo as featured so it's displayed prominently on my profile. So my immediate thought here is, okay, I'm gonna add a featured column on photo, and it's just another column that gets updated just like we had with caption. I don't really need to change much at all. No problem. So we do that and we submit that and the product owner tests it and comes back and says, oh, I forgot to tell you, I should have told you you're only allowed to have one featured photo. Okay, so I guess we have to have a conditional for when you go from something that wasn't featured to something that is featured and we need to find the previously featured photo and update that so it's no longer featured. All right, cool, we can do that. So we submit that and then there's one more thing that comes back and it says, oh, I was able to unfeature my final photo and I really don't want that to happen. We should just pick the first photo to be featured. All right, that seems arbitrary, but these things happen. This is a real feature that I have developed. So, you know, and you might think long and hard. In best case scenario, you come up with a controller action. You update your update controller action to look something like this, which is kind of intentionally hard to read here. It's interestingly shaped, right? And if we zoom in on the new method, that update photo private method we've extracted, there's a lot going on here, right? We've got an active record transaction. We've got a compound conditional. We have an ELSIF, which just the spelling alone in Ruby makes me kind of go, hmm, I probably shouldn't be using this. And it's not bad. That whole thing is not bad by comparison to what you're probably thinking of when I say complex action. But as a spoiler, if you're paying particular attention to the code and the requirements, this is also not correct. And how could it be, right? Business logic like this in controllers and conditionals in controllers like this are often not tested. And as such, have bugs in them. So how did something, so how did a feature that seemed so simple end up putting us here? Which again, probably not as bad as your complex actions, but it fits on a slide. The problem is one of differing perspectives. We wrote our original controller thinking that we were updating attributes on a model where each attribute was of equal importance. The new feature requires us to treat a certain attribute differently, right? We have to kick off a process. One attribute is now more important than the others. And the behavior differs based on which way that attribute changes, whether it's going from false to true or true to false. The dissonance in those perspectives is what results in the complexity we saw. And we can resolve that complexity, resolve that dissonance by getting back to being boring with rest. We'll introduce a featured flag resource to represent flagging and unflagging photos. Our photos controller goes back to what we previously had. It's super boring form that we saw earlier. And we're gonna make sure our new controller is equally boring. And here's how that might end up looking. It's deceptively, it's kind of a deceptively small font here because now it's two actions. But if we zoom in on create, we can see it is almost identical to the boring photos controller we showed before. The finder changed because the parameter changed. And now we're using a service object. If you see here, the add feature flag service object to get the work done. But we maintained that familiar boring shape of our controller action. And the same thing is true of our destroy action. We're using a different service object or object. Sean doesn't like service object. So here's what one of those service objects might look like. So here's our add feature flag service object. So you can see this is also pretty boringly shaped. It's not perfectly factors as I might write it, but it's also not very interesting. All of the complexity in the original controller, or the controller that we showed that have that complexity in it, all of that is now gone, right? The compound conditional, the if else, it's all faded away into the routes, right? Those decisions were all made before our controller code even ran. So as a result, this is also going to be significantly easier to test. I can unit test this or model test or whatever you wanna call this kind of test that you're dependent on the database for, right? I can test this in isolation somehow, and then I can have an overriding feature test and have pretty good confidence that this is correct. The lesson here is that we need to be boring. Your controller should be boring. Your model should be boring. When I first started in Rails, it was common to hear fat model skinny controller, and we don't say that as much anymore because we really want small things everywhere. What we should be saying is boring model, boring controller, boring object. There's something really satisfying in taking code that was complex or perhaps clever and breaking into component pieces that are boring. So as we add complexity to the application, we wanna make sure that the code we're adding is as boring as possible. Now, when I'm reviewing a Rails file or controller actions, I often encounter the third anti-pattern of ambiguous language. So what do I mean by this? How many of us out there have an orders controller with a process action or something similar, right? What does process do? What does it mean to process an order? That would likely depend on who I ask. If I ask somebody who works in the warehouse, what does processing an order mean? They're gonna tell me it's how they, maybe they're gonna tell me that's how they go and pick an order and they prepare it for shipment. If I ask somebody who manages inventory, maybe they'll tell me it's, no, it's actually dispatching that order to the proper warehouse to be fulfilled from. Or if I ask somebody in finance, I can bet they're gonna tell me it's processing the charge or the credit card charge, collecting the money. This might seem trivial. After all, we're talking about the code, so as long as those involved in the development of the project agree what process means, does any of this matter? Ultimately, having ubiquitous language throughout your application and in the business is what's gonna ensure ease of communication about everybody, among everybody who cares about that system, right? What we wanna do is avoid translating between the business terms and the terms used in the code because that results in confusion and it results in people making the wrong change because the product owner asked for it in the language of the salespeople and you implemented it in the language of the code. So process doesn't tell us anything. To figure out what it means, we have to go source diving. So okay, let's open up this orders controller which I'm sure is very boring. So again, I need to fit stuff onto a slide here. So the real action in the real controller would be a lot harder to decipher than this. But these controllers tend to begin with an indecipherable entanglement of conditional before actions at the top. And those before actions are possibly order dependent. In worst case scenario, which I've seen several times, they mutate some sort of instant state along the way. So we pick our way through all of that and then we drop down into the process action itself and that calls out into several private methods which we have to kind of unwind and those depend on instant state as well. Okay, so we go through all this process and then we discover that what we're actually talking about is shipping and order. Processing and order is equivalent to shipping it. So why didn't we just say so? So let's just say so. We didn't say so because this is awful. This somehow reads worse than process. Where are we putting the ship? I don't know. The problem is we don't need a better verb. What we need is a resource. So let's do that instead. We're not putting a ship, we're creating a shipment. This language works even if you're not ultimately inserting a database row somewhere. You can use create, it's fine. We don't need to change the underlying implementation. So here's what our shipments controller looks like. Now we pull over all the before actions we need, they're no longer conditional, that's kind of nice. And we take the process action and we make that our create action and then we pull over any private method that it needed. Just by pull over, I literally mean copy and paste it. Private methods and all, everything. And then we submit this as a pull request to say we've improved the language of the application. Isn't this great? At this point, we tend to get a little bit more heartfelt in the objection category. Yeah, I just told you I copy and pasted a bunch of code so I did duplicate a lot. We duplicated a lot of the private methods to make it work and some of that private method is just duplicated typing, like the finders and things like that, I'm not gonna worry about that. But some of it may actually be duplicate knowledge that would be nice to have housed somewhere. If breaking out that new controller is what it takes to highlight this, then it's worth it every single time. And I wouldn't rush to go ahead and make those extractions now, like let them sit for a little bit and make sure they make sense. This change stands on its own. As our friend, Sandy Metz likes to say duplication is cheaper than the raw abstraction. So I'm gonna live with this duplication for a little bit and see where it takes me. But this change does stand on its own. And the larger objection is that I didn't actually improve the code here. The ubiquitous language improvement I made, though, is worth a PR and a merge. No one else is gonna have to wonder what process means. We're all gonna be talking about the same thing consistently, as long as we wean all the developers off of saying process. But there's something else of value that this has done for us. I like to call this breathing room, right? It's the best way, the best way I can explain it is to draw a comparison to writing something, writing a blog post, or a paper, or a conference talk, something like that. And you may, in your writing, you may have a sentence wedged into a paragraph somewhere that isn't sitting right with you. It feels important and you want to say it, but it's just not working. So you try to reword it a few different ways, but it's still not great. And then you pull it out into its own paragraph and suddenly you realize the problem was that you had a lot more to say. And you can start to expand on that. And it starts to make more sense in itself and it starts to strengthen the things that are around it as well. That process is akin to going from something like this in our code, right? We've got this tangled mess and we're trying to figure out where this teal string goes. And so we're pulling at it and then we realize, oh, this is a totally separate piece of teal string over here, right? And we're trying to make an improvement on something like this to something more like this. Where we're holding a single piece of thread and we're still not quite sure what to make of it, but we can pull at it and we can see where it goes. So we want to get to this state because that's where we can really start to impact the rest of the application, right? So where do these things often go? State machines. Calling state machines themselves an anti-pattern is harsh, right? State machines happen somewhat naturally in a lot of our work. Orders kind of transition among various states of fulfillment pretty naturally. My problem isn't so much with the idea of a state machine. It's more how they're commonly implemented in Rails applications. They typically are made up of hundreds of lines of DSL tacked onto a model and that DSL expresses all the possible events and transitions and validations and potentially methods that are overridden in various states. And that all gets tucked into our order model for instance because that happens to be home to the state column. And so we just shove it all in there. A sign that I might be in store for something like this would be seeing this in the routes file. When I see this, it's a good indication that I'm in for a large controller backed by a state machine. So I might go ahead and take a look at that order model and try and figure out what's the state machine doing. So here's the beginning of it. So I came here trying to figure out what happens when an order's authorized. So like, okay, here are our events right here at the top, right? Nope, nope, those are transitions. Oh, nope, nope, they're not transitions. They're callbacks for transitions. Okay, all right, but all I care, again, all I care about is authorized. So okay, I see on the third line, that's authorized. Oh, but that's going authorized to captured. Oh, the fifth line is going from pending to authorized. That's what I care. All right, and it authorizes a payment, but it does it after it transitions. I don't know what that means, but and should this class know how to authorize a payment? Right, I don't think so. So here are the various events we support as we scroll down in the state machine. And this one's not so bad. There's less than a handful of them. Great, and yeah, okay, here's our transition. When we authorize, we go from pending to authorized. Another nice thing about this is like, they're very, it's a very linear progression among the states, so it's not too bad. And here's some custom class behavior based on the state. We have different validations based on whatever state the thing happens to be in. And we may actually, as I alluded to earlier, have methods that are overridden or methods that only exist on a certain state. Here's what the whole thing looks like. This certainly isn't a pathological case, but it's also not boring. It's extremely dense. When I'm reading through this code, I'm usually trying to answer a specific question about one particular event. And I'm left much like in our orders controller example to try and piece together the entire thing by going through a thing of callbacks, and then the transitions and the states and all that other stuff. And is this all tested? Probably not, because somebody went, I'm sure they tested it in the state machine. In the state machine term. State machines are magnetic, right? They attract more states and more transitions and more events and more callbacks, and they accumulate conditional logic, which is where our bugs hide. States and transitions that fall out of use are almost never cleaned up, because no one's clear that they're no longer in use anymore. Many of the recent projects I've worked on have 200, 300, 400 line state machine definitions. And this is not uncommon. I don't work on extremely pathological projects all the time. So how is REST thinking going to save us down here all the way in our model? Still this one from my friend, Sean. Pretend for just a moment that update didn't exist. What associated records would you need to create in order to properly reflect the state of your order? Those are the resources you need to care about. So if we did that in our example, we might end up with this, right? We've created resources for authorization, capture or refund or rejection, and we support creating those resources. Thinking resourcefully here gives us that breathing room I alluded to earlier to reconsider the interactions between our controllers and our models. Each of these state transitions can now be encapsulated by an object if we want, and the objects may still well update that state column on order. That's perfectly fine. Or we may actually insert some associated records instead. Also works fine. So in addressing each of these anti-patterns, oh, sorry, let's talk about this one. So what used to be an encoded mess of transitions and events is now just like this boring object here. So this is what it would look like if we extracted that authorization thing into an object or service object or process object or command object, whatever you want to call these things. And what used to be a bunch of DSL is now just a boring Ruby class, and it's boringly shaped. And if I want to know what happens when an order is authorized, I open up this file and it tells me. And if we want to change what happens when an order is authorized, this is so lightweight that there's no sunk cost fallacy to overcome. We can throw this away and start again without worrying about losing some sort of context that we didn't understand about authorizing an order. So anything that drives us towards these types of small plain Ruby objects is going to be a win for our Rails application. So in addressing each of these anti-patterns, we've thought about our application from the outside in. We've been preaching outside in, or a lot of us have been preaching outside in development for quite some time. Generally when talking about test-driven development. But outside in development works equally well when designing your domain object interactions. And it results in similarly better design and better code. What we're doing is changing the language of the application from what does this application store and how does it store it, to what is it that this application does at the highest level. And there's still an overriding objection that I often hear to much of the advice in this talk. And some of you in this room may be thinking it. And that's, you know, this is fine for crud apps, but my app's different. My app's a snowflake. It's much more complicated than what you're letting on. This is fine for base camp, but my project's a lot more complicated. Or pretend perhaps maybe I just want to use RPC or whatever, you don't really like REST that much. So what I found in app after app is that in most cases, what's keeping any app from being just a crud app is a lack of imagination. You haven't gone back to rethink the problem from the outside in to identify the resources that you're actually talking about. And you want to be a crud app, believe me. Believe me. Because crud apps are boring, right? And I hope you know by now, when I say boring, I mean that in the most sincerely complimentary way. You want to be a crud app because Rails is really, really good at crud apps. And you've chosen to write or have been stuck with maintaining a Rails application. So when doing that, be boring. Be RESTful. Don't worry about response codes, right? Identify the real resources and use in your application and design your entire system from that perspective from the outside in. Don't worry about the vagaries of your data model or your data store. Any questions? Yeah, so the question was, how would you handle the state machine exam, sorry, the example of a wizard, right? A multi-step process that maybe in the end creates or updates one record. It kind of depends on the specifics of what you're trying to get done, but I did recently do the same type of thing where people were going through this registration wizard and telling us a bunch of information about the facility that they were onboarding. And the way we did that was had them, in step one, they created the shell of the resource. And in step two, we modeled that as associated records, even though ultimately they weren't all associated records. Some things were like an address would then be associated to the thing in the next step. And there may be a state thing at the end that happens to say yes, they've checked all these boxes and the wizard is now complete, but trying as much as possible to think about breaking that up into different resourceful steps. And it is kind of dependent on the on exactly what you're doing in the wizard. So the question was, a common objection is that it's fine to do, it's basically fine to do this here, but you haven't addressed it everywhere else, is that like now we have a different pattern in use in lots of places. It is a common objection, you're right about that. And I mean, we have to be trying to make the application better. And if people can agree that yes, this is better and we should be moving to this, then I think you'd do it, right? And you update the other things as you have need to. But yeah, there does tend to be some pushback about, like, well, now this is a different pattern, we've never had a service object anywhere, now we have a service object, but if that's an improvement and the team can agree that that's an improvement, the option is just stagnating and a thing that you agree is not the optimal solution. So that's the way I would put it. So where I would put, so the question is where did all the complexity that was in the state machine go when I showed the example of the extraction that I had done. And I wouldn't extract it to a controller, what I actually extracted there too and I might have done a poor job of explaining it was that was the service object that controller is gonna use. And I would put all of, as much as I could of that there, right? And there was, if we go back, I don't know how far back that is. Oh, not too far. Okay, so there was this idea of whether or not something's authorizable down in the bottom and that would be like the conditional validations perhaps. And the before transition and after transition stuff, that all just kind of goes away in the middle of this process or call or whatever you wanna call that, the main method of this object here. Because it does the transition of updating the order and then it calls the authorized thing that we were doing in our state machine at that point. So moving that as much as possible into a service object or a collection of objects that work together and testing those individually. And I think this really does kind of encapsulate a lot of what was happening in that state machine and I think it's read significantly easier. I haven't thought about it from that perspective. I think the generators are super useful. And I still use them on occasion like when I need to remember what a vanilla controller looks like. And I don't know that it's a solvable problem if you're gonna generate code. I don't know that like the generator could know exactly what the best thing to do was. Oh, and for the video, the question was, should we update the generators to be more in line with an approach like this somehow? So the question was about alternative action names like reorder if you have a collection of something and you wanna reorder them so that we create a reorder action. And how would I handle that? And yeah, I would typically like going back to our photos example, if you were reordering all of the photos, I might create an album instance, right? And you can update that album instance with an ordering of its component photos or something like that. Or a positions controller like you alluded to in your question, something like that. But there's no reason the collection of objects can't be a resource in and of itself. So yeah, that's how I handle that. So the question is, I'm making a Rails engine, is that what I'm doing? Okay, so I'm making an API for an engine and the action is start the engine. How would I make a nice resource for that? Boy, on the top of my head, I don't know, engine start request, create. And engine update, sure. I mean, that's up to you and your team. Like I think, I'm not trying to say that you should never have a custom action, but I'm saying it should be your default to never have a custom action. And you should agree with your team that like here, a custom action makes sense for me. I just happen to think that answer is almost never, but you may differ slightly. I just hope that you don't differ to the point of creating custom actions in your orders controller all the time, that kind of thing. They're laughing because that's my favorite question. So the question is where do you place these things, and I always ask that question because it's so nice when you open up app and you have like models and you have this and you have that. Wherever. You can put them in app services, app objects, app boring. I don't know, but just pick a thing. I've worked on a project before where we actually broke them down based on the types of things they were. If they were like a command object or if they were a query object, that's a common thing, like app queries. I'll see for like query for complex query objects. Just agree with your team what you wanna do and use a fuzzy finder like control p and vim to find the file anyway, and you're all set. Cool, anything else? All right, thanks everybody.