Zen and the Art of the Controller
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Part Number | 42 | |
Number of Parts | 89 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/31576 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Software developerProjective planeProcess (computing)Computer programmingPresentation of a groupGroup actionSubject indexingTrailGoodness of fitProduct (business)Multiplication signLine (geometry)Control engineeringSlide ruleTotal S.A.BootingComputer animation
02:00
Group actionLine (geometry)BitCodeBoilerplate (text)
02:33
Group actionControl engineeringBitPartial derivativeArtistic renderingAutomatic differentiationClient (computing)Multiplication signDifferent (Kate Ryan album)Line (geometry)SoftwareCountingImplementationContext awarenessMobile appLogicObject (grammar)Structural loadDebuggerWeb pageNatural numberProcess (computing)Software developerFacebookTwitterTerm (mathematics)CodeStandard deviationVisualization (computer graphics)System callMathematicsLatent heatCode refactoringInformationElectronic mailing listTotal S.A.TouchscreenGUI widgetEndliche ModelltheorieOperator (mathematics)Product (business)Subject indexingSingle-precision floating-point formatFood energyCASE <Informatik>Computing platformSurgerySet (mathematics)Shape (magazine)
08:41
StatisticsGroup actionNumberControl engineeringObject (grammar)Block (periodic table)LogicElectronic mailing listArithmetic progressionCalculationProcess (computing)Web pageOnline helpStatement (computer science)Escape characterComputer animation
10:11
LogicControl engineeringInformationCodeGroup actionComputer fileEndliche ModelltheorieMathematicsProcess (computing)Functional (mathematics)Context awarenessMultiplication signQuicksortFlow separationSoftware developerRevision controlView (database)Standard deviationOnline helpAutomatic differentiationComputer fontReal numberNatural numberSubject indexingRow (database)Latent heatAuthenticationLine (geometry)DebuggerObject (grammar)Cartesian coordinate systemDifferent (Kate Ryan album)Ocean currentFacebookStatisticsControl flowAuthorizationSystem callFunction (mathematics)Form (programming)Sheaf (mathematics)Landing pageEncapsulation (object-oriented programming)Mobile appData managementFluid staticsBit
16:55
Group actionParameter (computer programming)Control engineeringBitPoint (geometry)1 (number)Product (business)Object (grammar)Standard deviationLine (geometry)Multiplication signGraph coloringSet (mathematics)Different (Kate Ryan album)Computer animation
19:06
DebuggerProduct (business)Student's t-testBookmark (World Wide Web)NamespaceChemical equationMathematicsSystem callContext awarenessObject (grammar)Different (Kate Ryan album)AbstractionFlow separationPoint (geometry)Group actionModal logicCodeElement (mathematics)Code refactoringMobile appFunctional (mathematics)Hydraulic jumpDecision theoryTotal S.A.Control engineeringSoftware developerLevel (video gaming)UsabilityMultiplication signBitSoftware testingPerspective (visual)Computer fileGUI widgetLogicAutomatic differentiationStatisticsDisk read-and-write headVelocityLine (geometry)Parameter (computer programming)Standard deviationComputer architectureWeb pageLattice (order)CASE <Informatik>Software maintenanceLocal ringProjective planeGoodness of fitPosition operatorRight angleView (database)Natural numberPartial derivativeSpecial unitary groupInstance (computer science)Lecture/Conference
27:50
Computer animation
Transcript: English(auto-generated)
00:03
Okay, so let's go ahead and get this thing started. My name is Michael Kelly. I'm a senior Rails dev. Been doing this for, doing Rails for about five years.
00:23
I've been a dev for on and off, maybe 15 total. And as you can tell from the slide in your program, we're gonna talk about Rails controllers today. So before we get started, I know this is in the junior track. Let me see a show of hands.
00:41
Who here is actually a junior developer? Somebody newer to the technologies? Okay, good, good. How many people here are senior developers who are here to judge me on my presentation? Oh, good, okay. So I only have to impress a few of you. All right, good deal, good deal.
01:00
So I'm gonna start off, like I said, we're talking about controllers. You've all seen them, you've all dealt with them in some way or the other. And a lot of times, what you see, especially for new developers, coming out of boot camp or maybe some, you know, a run on tutorials online,
01:20
and the slides went away. So that's me. I apologize for that. Okay, so we've all seen them. We've seen index actions, we've seen show actions. You've all seen the standard CRUD.
01:44
But what I'm kind of here to talk about is what actually happens out in the wild. Controllers you're gonna see when you step onto a new job or, you know, pick up a new product, something like that. And actually, the first slide I have is one such controller.
02:03
No, you're not supposed to read that. We're not gonna go through that line by line, nothing like that. That's atrocious. That's over 350 lines of a single controller with over 18 different actions and 100 lines of just boilerplate code.
02:22
That's wrong. If anybody's wondering, that's wrong. What I want to talk about a little bit is how we get this nastiness out of what is essentially our standard controller, the one we've all seen. And it actually happens a lot easier than you think it does.
02:45
So what we have here is the standard. You have your index, show, new, create, all the standard actions. But a lot of the times, what you'll hear people say is that they blame controller bloat on things like rapidly changing requirements,
03:06
uncontrolled feature growth, maybe changes in your team, or even, in the worst case, refactors elsewhere in the app make you add things to your controller. That's all wrong, every bit of it. Every time you read something like that, it's incorrect, because that's not what happens.
03:22
These are all controllable things. They exist in every business, every software product. These things happen, all right? There's a way to maintain a good controller. So what actually causes this? How do we end up with 300 lines of garbage?
03:42
Well, main thing is, is it's a misunderstanding about what your actions are actually doing, what resources they're actually working on. So I want to walk us through a small example. It's a little bit closer to real world, but I've glossed over a few things for time here.
04:07
So we're going to take, for example, and this is actually something I've done a few times. Let's say you're working somewhere and your company is in charge of running ads for its clients on Facebook, Twitter, any of these social platforms, okay?
04:24
So the fairly standard requirements that you'll see, you need a way to kind of browse, edit, and deal with the ads, create them, edit them, things of that nature. We also have to have a way to control those ads, pause them, activate them,
04:44
basic on-off style features. And the last, users have to be able to see the performance of their ads, see how many people are seeing them, see who clicks them, how many times they've been clicked, what the impressions are. So in this example, I'm not going to build a whole app.
05:03
I'm only going to deal with one controller. Call this the ads controller. So first thing we do, we come in, okay, we have an ad object somewhere, and we want to be able to do the standard CRUD operations, or you'll see there,
05:21
I actually prefer the term bread because it includes the index. That's browse, read, edit, add, and delete. That's just a nomenclature thing, and I'm weird that way. So you saw this before. This is just reimplemented in this context as an ads controller. Pretty straightforward. I don't even have any features in there like paging or search or anything like that.
05:44
Just straightforward actions. So we've implemented this. We've pushed it to production, and everything's fine. But the next thing... Well, I'm sorry. So we have a total of seven actions out of the game, okay?
06:02
Every controller has these, or most do. So we're already talking about something that has seven different contexts that you have to keep mentally. But like I said before, we have to control these ads, and we want to be able to do that in a nifty kind of way,
06:23
maybe some JavaScript button on the front end. We click it, and it makes some Ajax call to our app and pauses an ad. So we come in, and this is something you'll see a lot of times. I won't say naive, but eager developers will take these actions and go,
06:43
okay, well, they're Ajax actions. Let's just stick them in the ads controller. That's what we're dealing with, right? All right, so this is that same controller with those actions added. And don't worry if you can't read the code. It's on the screen there. I actually want you to just kind of keep a mental model of how that controller's shaped
07:01
and how much information is there, okay? We're not looking at specific implementation today. But you see there, our action count has ballooned up to 10, all right? So that's now 10 different contexts and different ways this controller can be used. Now, that's great. You know, we have these three new actions that maybe kick off a background job
07:23
that talks to Facebook or Twitter or something like that. Then one of your execs walks in, and this is a phrase I've literally heard multiple times. So we want more of a responsive UI. We want some way that our page can load and then load individual visuals for our ads asynchronously.
07:47
So we come back over here to our trusty ads controller, and maybe we need a pane to preview the ads so the user can see what it'll look like on Facebook. Maybe there's, we want some little widget that'll show them generic stats
08:05
in like a list of their ads. Something of that nature. So we come in, and we add two more actions. Now, these actions, I don't know if you can see it there, but all they do is render out a partial, something for Ajax to process and insert into the page. Again, our action counts back up to 12.
08:24
So the controller doesn't look so bad. Now, I'll let you know, each one of those actions is as minimally implemented as possible. The rendering of partials is a single line. The background jobs are a single line. So any logic added here is only going to complicate the matter.
08:42
Oh, wait. Now we have to be able to deal with our stats, our statistics about these ads. So once again, we come back. Let's say we want a page that'll show our, the audience, the people who have clicked our ads. These are people who have interacted with our ad, and we want to maintain a list of those.
09:04
Maybe we want a dashboard to kind of see how I as a user am performing in general. So we come back, and we add a few more actions. And these actually deal with maybe some helper objects to do some calculation.
09:21
Maybe there's some aggregation or processing that goes on in this data. And here we are, now we're up to 14. Now, I don't know if, I don't remember if I mentioned it, but that original controller I showed you only had 17 actions. So we're not that far off. And you can see how, again, if any kind of logic gets inserted into these actions,
09:44
if you add any if else blocks, if you add any escape statements, anything directly in these actions, all you're going to do is turn it back into that. Okay? So we're not that far off. It's actually pretty easy, and it happens without you thinking about it.
10:02
You know, you're thinking about that one feature, adding a preview or adding a statistics page, not your controller as a whole. So the answer to this, to keep yourself from getting out of control like that, is to break it up, break your controllers up into pieces.
10:21
Now, I've talked to a couple of junior developers before about this topic directly, and a lot of times people will have trouble breaking the context. You know, in every one of those actions, we were dealing with ads. So it makes sense mentally to stick in the ads controller.
10:43
But what I argue is we're not actually dealing with ads in all those actions. So to kind of help you out a little bit, these are some different ways you can think about what kinds of controllers you're going to build. So in that example, we had actions that dealt with static or view layer data.
11:08
This is processing a partial or dealing with maybe a static page, it's a landing page, things of that nature. We had actions that dealt with things that are composite concepts,
11:24
concepts that aren't mirrored directly by like an active record model or something of that nature. And then finally there we have aggregate actions, things that collect a bunch of data together and pipe it down to the front end. So let's take that example.
11:41
We're actually going to break it up along these lines. So, excuse me, looking at your static and view style actions, these are those same actions from that previous controller. You can actually read those now. That's a font you can read again. These are the preview and stats actions.
12:02
I've separated them out and I've stuck them in a different controller because what you're dealing with here is a view of an ad, not an ad itself, okay? That's a different resource, a different quote model that you're dealing with. You see the same thing over here with your composite controllers.
12:21
Now these are things you'll see a lot of times. Devise is actually a good example of this. If any of you have used Devise for authentication or authorization, anything like that, there's all sorts of session controllers that you can override and add your own functionality. And nobody that I know of has ever built a session model in a Rails app.
12:44
Or like in this example, jobs. So those Ajax actions that would pause and activate your ads, actually what we're doing is we're starting a job on the back end, starting something that's going to call out to Facebook,
13:00
tell it to pause a specific ad. So I've broken those up as well into this ads jobs controller or ad jobs controller because that's what we're dealing with is jobs, not ads. And the same thing goes for aggregate data. Our audiences, that's something that's pulled from every ad we have.
13:21
The dashboard, that pulls in information about every ad we have. So it's an aggregate resource. It's not a single model. And then of course, we have our standard crud controller with the index show, create, add, delete, blah, blah, blah.
13:46
So what we've done is separate all these pieces out into different controllers. And it's not immediately obvious what the benefits to doing something like this are.
14:03
But one, it's easier to debug your controllers. It's easier to navigate them mentally because if you have a problem with say the previews that we talked about a minute ago, maybe we need to render them differently or something about that is breaking, well we know right where to go
14:20
and there's two actions in that controller. So we can make those edits without having to build the entire mental context of that ads controller that we talked about earlier. The other is, and you'll see this a lot when you step out into the real world, learning and onboarding.
14:41
This is one of the toughest things that a developer does and that's step into a new application. So when you're learning a new app, do you think it's easier to take 350 lines and dig out the maybe two lines you need to deal with or two lines you need
15:02
to learn how a specific action works? It's a lot harder when you have to dig through that big giant mess we saw a minute ago. The other is you localize your changes and this is something that a lot of people talk about, encapsulation, you know, separation of concerns and whatnot.
15:23
But ultimately, like I said before, if you're dealing with say stats, you need to calculate something differently, your changes are going to occur in that controller, not in one big giant mess. So if you go and you add say a before action, you now want to, I don't know,
15:41
authenticate this call or do some object setup before the action comes in. If you've got 20 different actions in that controller, you now have to explicitly control which actions that filter is called on. You know, do a before action, accept all these others or only these two.
16:01
So now when you make a change, it only affects the pieces of functionality that you want it to. And the last here, this is one that I haven't seen talked about quite a lot and that's that it's actually easier to coordinate working on a larger team. If you've got multiple people working on this code base, maybe you're working
16:25
on improving statistics, adding more information and data there and you over here, you're working on making the previews look slicker, look cooler. Maybe you over there, you're working on, you know, improving,
16:41
maybe there's some business logic we need to add to creating an ad, some kind of check we need to include. We can do that now. You're not all making changes to a single file that then conflicts when you go to merge it in. So I kind of blasted through that a little bit faster than I expected,
17:00
but the main things I want you to take away from this talk are that you need to actually look at actually what your actions are dealing with, okay? You're not, they're not all dealing with an ad resource or a product resource or a user resource. These are different conceptual ideas that it's dealing with.
17:24
And your controllers, I want to see a lot of controllers, okay? I don't want to see big giant ones. I don't want to see 300 lines with 20 different actions. In fact, I actually challenge you that controller we saw in the beginning,
17:41
the standard CRUD controller, make that the most complicated controller you have. The one you see in the tutorials, the one you see online in the perfect examples, make that the most complicated controller you have. It's challenging. It can be. But ultimately, that was the most complicated one I showed you outside of the trashy controller.
18:08
So the main point here, like I said, and you'll see I actually had a couple of people ask me how this relates to RESTful design, which is a fun buzzword in our world.
18:22
And I make the argument that this is RESTful. To create a REST resource, an object that can be created, that can be destroyed, that can be changed, you have to actually define what that object is. And it's not, I promise you, it's not the same as all your other actions.
18:44
Dig in and take a look at that. So I've gone way under time here, but I think I over-caffeinated. But does anybody have any questions?
19:02
I would love to go back through and talk about it in more detail. The question was if I have any examples of where it's permissible or okay to add some actions on top of the standard CRUD actions. And in some cases, it can be.
19:21
I mean, as developers, we kind of play a balancing act between the right way and the achievable way. You know, if you have a feature going out or it needs to be in production in 20 minutes or a fix that needs to be in production very quickly, it can be very hard to do a refactor
19:43
into three different controllers and test all of those controllers and push them up, get them reviewed, the whole nine yards. And so what you'll see is a lot of times earlier on in a project, it's very convenient to add small actions like that,
20:02
like possibly the Ajax actions or maybe the Vue actions. And it's all right early on. You know, if you have a total of maybe three controllers and you want to tack some small functionality on there, that's fine. Get it in there and get it pushed out. Not a big deal.
20:21
The main thing, though, is understanding when you introduce a new concept. So for instance, the previews that I mentioned, if that was a very small feature early on, yeah, we'll stick that in the ads controller. Not a big deal. But if this is a concept that you are approaching in the app a lot,
20:43
you're going to come back to it with previews and maybe some stats widget or further widgets along those lines. If this is something that's going to keep happening, separate it out. You know, don't just tack it on there extra because it will grow uncontrollably.
21:01
Actually build like an API name space and that's, I'm sorry, the question was, so with the, like the ads previews where you're rendering out partials, after you've created a few of those, it starts to appear like a kind of a private API. You know, your front end app or your front end page is making these private calls
21:24
to get these partials and at what point do you look to refactor that to say like a slash API name space. And again, just like everything else, it's a lot of judgment calls, but it tends to,
21:40
a decision like that tends to matter more when you foresee, it's a little bit about looking to the future. So if, you know, you're writing your first couple that render a partial and then, you know, a few more are coming along, maybe you're even dealing
22:01
with some JSON elements, you know, serializing your objects in a certain way. Really it comes down to when an abstraction like that, excuse me, when the elements you're adding justify the work necessary for the abstraction.
22:23
And you'll see a lot of times, it's an interesting balancing act because, like I'll jump into an app like that and, you know, I've done that so many times creating, you know, this internal API that I can crank that out in a few minutes. So the level of difficulty is different based on the developer who's picking it up.
22:44
If you've ever dealt with agile development, you've estimated that way. So it kind of depends on the velocity moving forward, you know, where you're headed. And to kind of further your question a little bit,
23:02
at what point does that stop being an API embedded in your app and a dedicated API launched at a different URL? You know, those levels of abstraction in the app itself really kind of depend on where you see the app going from a design perspective,
23:22
from an architecture perspective. And that's something that I to this day still get into arguments with my own team about. You know, I'm like, hey, let's go ahead and abstract this thing up there. And so I'm like, oh, it's not worth it. It's not worth it right now. And, you know, you make the debate back and forth. And ultimately, you know, when you can go to your team and say, hey,
23:41
I'm going to create this API, and they go, hey, okay. You know, when you all kind of come to that same decision, it's time. It does. It does. And did everybody hear that question? So his question was, in a lot of the designs that he's dealing with,
24:01
the designers, you know, don't follow RESTful. They, you know, they view the design of an app and how users are going to interact with it. And that's actually the crux of my point here is that designers don't deal with controllers. Designers will never deal with a controller. The controller is your side of that. So in that situation, I would make the effort to maintain the separation, you know,
24:29
a customer's controller, a, I'm sorry, a question's controller, a student's controller, and then leverage your front end to combine that data if necessary, whether that's through Ajax or something of that nature.
24:42
Now, if it's extremely heavy, so you have lots and lots of places that these two objects are rendered or serialized together, then you start to think, start to look at it more like a composite resource, kind of like I mentioned earlier.
25:01
So if you're always rendering those together, then create a controller that encapsulates that concept. You know, this is a, I'm not sure what your context is, but call this a student questions controller. And it renders them out as a grouping of that data. The decision to go one way or the other on that tends to matter more how much you're doing
25:25
and how closely tied they are consistently throughout the app. But I tend to, designers don't like me because I tend to force my code into a good design. And you know, it'll add a little bit of work to kind of converge it on the front end.
25:46
But in my mind, the benefits of ease of development and localized changes really outweigh that. Because a lot of times, if those two things are rendered together at the same time,
26:01
a lot of the changes you want to, say the logic associated to creating a student or viewing them will apply to the student itself, regardless of if it's rendered together or separately, in which case you want that separated into its own controller. So a lot of times, it can be hard, especially in a meeting room, but you know,
26:25
your code is your code. The designer's design is their design and there has to be a line of separation there. You know, what I'm going to implement will achieve that design, but I'm going to do it in my way so that my code is still understandable, still maintainable.
26:41
Otherwise, you're going to get into fights with them later on when you can't change something to the way they want it because you've kind of locked into this marriage of the two resources. Favorite zen philosopher. Well, considering I stole the title from Robert Persig and his Zen and the Art of Motorcycle Maintenance, I'd have to say him.
27:05
It's a lot of the same concept, though. It's about looking at what's in front of you and kind of seeing it from both the functional and the aesthetic perspective. And that's one of the reasons why I said don't try and read the code. Just look at the file as a whole.
27:22
You'd be surprised how much an eye for aesthetics on your code will actually do for it functionally. And vice versa. You know, as long as you kind of apply a much more whole mindset to it. All right. Well, I am now out of time. I managed to kill the rest of that.
27:43
Thank you guys very much. I really appreciate it. Thank you very much.