083 RR Decomposing Fat Models with Bryan Helmkamp
The Rogues talk to Code Climate's Bryan Helmkamp about decomposing fat models.
Special Guests:
Bryan Helmkamp
Show Notes
The Rogues talk to Code Climate's Bryan Helmkamp about decomposing fat models.
Special Guest: Bryan Helmkamp.
Transcript
CHUCK:
Let’s get your best evil laugh.
BRYAN:
[evil laugh]
JOSH:
OK that’s going into the show opener. [laughter]
DAVID:
Show opener? That's going as my ringtone! [laughter]
BRYAN:
It’s already my ringtone. [laughter]
JOSH:
I wanna like… something… there's going to be something on Code Climate that when it throws a red flag, [chuckles] it does that.
CHUCK:
Oh there you go.
[Hosting and bandwidth provided by the Blue Box Group. Check them out at bluebox.net]
[This episode is sponsored by JetBrains, makers of RubyMine. If you like having an IDE that provides great inline debugging tools, built-in version control, and intelligent code insight and refactorings, check out RubyMine by going to jetbrains.com/ruby]
[This podcast is sponsored by New Relic. To track and optimize your application performance, go to rubyrogues.com/newrelic]
CHUCK:
Hey everybody and welcome to episode 83 of the Ruby Rogues Podcast! This week on our panel, we have Josh Susser.
JOSH:
Hey everyone!
CHUCK:
David Brady.
DAVID:
In the unlikely event of a water landing, your headphones can be used as a floatation device.
CHUCK:
Avdi Grimm.
AVDI:
Hey from Pennsylvania!
CHUCK:
Katrina Owen.
KATRINA:
Hello!
CHUCK:
I'm Charles Max Wood from devchat.tv and this week, have a special guest – it’s Bryan Helmkamp.
BRYAN:
Hi everyone!
DAVID:
We are missing someone today.
CHUCK:
Really?
JOSH:
Yeah. Where is James?
AVDI:
Haven’t seen him.
DAVID:
James can’t be here today because he’s tutoring the pope in the scientific method.
CHUCK:
All right.
JOSH:
We’ll go with that.
DAVID:
Yeah.
CHUCK:
All right. So we have a couple of announcements before we get going. The first one I'm going to let Katrina give us the Best of Parley.
KATRINA:
This week you guys missed an awesome thread on Code Quality. It talked about everything from people problems to cowboy coders and dramatic saves and how to bring change to a team. All in all, it was very, very informative and very -- let’s just say there was no trolling.
BRYAN:
Wow.
DAVID:
Oh I can fix that. I'll be right back. [laughter]
CHUCK:
All right. The other announcement is that Code School has offered their Ruby Bits 1 course to all of our listeners for free. You can go and pick it up by going to… They have this big one URL. Just go to rubyrogues.com/rubybits and it will take you to the right place. The long URL is mktg.codeschool.com/ruby-bits-1-rubyrogues/. So we will shorten that for you. Just go to rubyrogues.com/rubybits.
JOSH:
Well, hi Bryan! How are you doing?
BRYAN:
I'm great. How are you Josh?
JOSH:
It’s just lovely here in San Francisco. I love when the rain washes all of the pee off the sidewalks. [laughter]
CHUCK:
Oh dear.
JOSH:
What?! You obviously don’t live in San Francisco. [laughter]
BRYAN:
Yeah. We have the same phenomenon.
CHUCK:
In New York?
BRYAN:
Absolutely. Yeah.
DAVID:
Oh my gosh.
CHUCK:
All right, well-- [crosstalk]
DAVID:
“Petrichor” is the smell of when it just first starts to rain, and so in San Francisco, petrichor actually means the smell of pee being washed away.
JOSH:
Yeah basically.
DAVID:
[laughs] That's awesome! I officially love this show again. Let’s go.
CHUCK:
[laughs]
BRYAN:
Now that we got everybody wanting to move to either San Francisco or New York…
DAVID:
Yes. Yes.
CHUCK:
[laughs] But only when it rains.
DAVID:
Well, New York is just as far away from San Francisco as you can get, right?
BRYAN:
[chuckles] Yeah.
JOSH:
Well, as far as you'd want to get anyway.
DAVID:
Well, yeah.
CHUCK:
Anyway, so—
JOSH:
Bryan, you seems like you were just on the show.
BRYAN:
Yeah, not too long ago. Thanks for having me back.
JOSH:
Yeah. That's what happens when you do awesome stuff and James demands that you come on the show and tell us about it.
BRYAN:
And who am I to question James?
JOSH:
[chuckles] Yeah, so the Code Climate blog -- it’s just kind of blowing up with awesomeness. Tell us how did you get to be so awesome.
BRYAN:
[laughs] I don’t know if I can answer that question. I don’t think I can acknowledge— [crosstalk]
CHUCK:
Tell us!
BRYAN:
--that I am awesome. But if I lock myself in a room for a long time, then I get a lot of other stuff done while procrastinating writing a blog post and then eventually sometimes a blog post comes up.
AVDI:
So, what were you doing when you were procrastinating writing a blog post?
BRYAN:
Coding. [laughs] There's a hierarchy.
DAVID:
There’s a really good article on procrastination that basically talks about avoidance behavior and you can actually hook that for the power of good. So if you get like three things that you don’t wanna do pick the worst one and then do the other two as like avoidance for the third one.
BRYAN:
Oh. Absolutely that's how with my writing. [laughs] My apartment gets cleaner when I blog post to write.
JOSH:
[chuckles] So you are saying that in order to write a blog post, you need to schedule yourself for a deadline for doing something that you don’t wanna do more than you don’t wanna write a blog post.
BRYAN:
That's right.
JOSH:
So you mostly write blog posts right before April 15th.
BRYAN:
[laughs] Yeah. There's going to be a flurry of them in that--
JOSH:
OK. [crosstalk]
BRYAN:
--file for an extension. [laughs]
DAVID:
The Code Climate blog is heating up – they he must be about ready to ship a new version. [laughter]
JOSH:
OK. So— [crosstalk]
DAVID:
Market indicators.
JOSH:
So this started back in October you had this 7 Patterns to Refactor Fat ActiveRecord Models and everybody looked at that and were like, “Whoa! There's like actual specific things in this blog post!”
BRYAN:
[laughs] Is that what they said? I hope that's what they said.
JOSH:
Yeah.
DAVID:
I think that was actually the most re-tweeted tweet about it, yeah. It was like, “Whoa! This list of words!”
JOSH:
So, where did these specific things come from? Is this just in your position as Code Climate being able to look at all the metrics for all of these different things? Is there like some numerical basis behind choosing these seven things or that came out of working with all these different pieces of code and looking at metrics of them? Or is it something else?
BRYAN:
No. Those primarily came from just patterns, that in various forms, I've applied knowingly or unknowingly over the last -- I don’t know, 5 years or so -- doing Rails app development. I sat down and write it. And some of them kind of presented themselves and others were sort of refactorings that I’ve applied but I hadn’t really worked out exactly what the best way exactly to refer to it was or even thought through the trade-offs between the different patterns. So actually in writing the post, I kind of clarified for myself some of those different trade-offs and ins and outs about which patterns to use when and what the difference is between the different concepts.
JOSH:
OK. But just to satisfy my curiosity, there hasn’t been some occasion where you are looking at the operation of Code Climate and all the different people’s code that it’s looking at and getting information about which metrics or using metrics to identify the most common problems in code across all these different projects.
BRYAN:
Yeah. I mean I don’t look at anybody's code that's on Code Climate. There's you know, I use it for my own code and the metrics I get back in aggregate, but the refactorings aren’t really driven by metrics. And it’s kind of an interesting thing because obviously Code Climate produces all these metrics about every class in your whole code base but, there is a fair degree of abstraction to go from looking at a metric and “OK, this class is really complicated,” to “OK now what do I do about it?” And the tool can really sort of go that far along those lines. So yeah, it’s basically just out of expense. And then one thing I guess that I have fortunate to do is sometimes people set up their apps on Code Climate and they want to show me sort of the results that come out of it. And there's been a few times where that’s happened and people got like really, really, great Code Climate scores right off the bat on a Rails app of size, which is pretty uncommon. Usually if the Rails app is big enough, there's going to be enough kind of and big enough sprawl and big models and stuff like that where it’s going to drag down the scores a bit. But a few people had apps that were scoring really well right off the bat and they wanted to show me that, so we did and I was just kind of like blown away by that and started thinking, “Well, what are these people doing differently that's causing them to have code that seems to be more well-factored that the average Rails app?” Actually one of those people was Matt Wynne. He had set up and app on there. It was getting like all A’s right off the bat. It was like, [chuckles] “That's amazing! I’ve never seen that before.” And then he showed me some of the things that he was doing. It’s really cool.
JOSH:
OK. Cool.
CHUCK:
So one of the things that I noticed -- looking at the blog post here -- one of the first things you said was, “Don't extract mixins from fat models.” And it kind of made me think for a minute because it is something that… it is handy to do when you extract mixins from models, but it’s handy when it’s shared behavior. It’s not handy when you are taking one giant jump to making a whole bunch of little ones.
BRYAN:
Yeah. I think that was one of the analogies I used in the post. It’s just the idea of pulling out bags of methods, I think people do it a lot of times just because it’s very easy. You know, you have an established a strong domain concept and you know, any of those methods can call other methods like it mixed in to the same class. So you are very unlikely to break things in the refactoring but you've kind of made it harder to see what's going on and what the extracting the right concepts from that model would look like. So I think that's the biggest problem is if you caught things the wrong way, then it can make it harder to be able to visualize how to cut things up in a better way.
CHUCK:
Mh-hm.
JOSH:
And I love that the lead in to all of this was use composition, not inheritance.
DAVID:
+1 to that.
BRYAN:
I did not invent that. [laughs]
JOSH:
Well, no but you used it incredibly well.
DAVID:
And there are a lot of Ruby programmers that tend to treat mixins like composition. That I'm mixing in this thing, so obviously I'm composing it. And no actually, you are not -- you are inheriting. When you do include module, that's in the freakin super chain -- you can call super to get to those module methods. And so it’s a little subtle that people don’t realize that that's inheritance guys.
CHUCK:
Yup. Gets it into the ancestor’s tree. It’s there.
DAVID:
Yeah.
AVDI:
It means that you have in fact expanded your object’s API.
BRYAN:
Yeah, that's exactly right. And I know some people get hung up on the difference between inheriting from a class -- like ActiveRecord base -- versus including a set of functionality like I use MongoMapper and that's based on an include.
AVDI:
Yeah.
BRYAN:
You can do the same thing in a way. The only difference I think is just basically on how you’re communicating about the class is, right? Functionally, they do the same thing. You can say, “OK this class is a type that descends from this other type,” versus, it has an aspect of behavior which is this module. I don’t get too stressed about for libraries like ActiveRecord or DataMapper whether are inheriting or mixing things. I think that's a debate that gets kind of blown out of proportion, but I think the key is to understand that inheritance in Ruby is basically the same thing as including things in Ruby and you have to treat it the same way, apply the same concepts.
DAVID:
Well and it’s pretty firm -- not ever, ever use right? I mean, it’s not a binary decision.
BRYAN:
Yeah absolutely.
JOSH:
And in fact there is a linear combination of the two that I think is a pretty good approach sometimes. And I'm actually seeing some opportunities for that in this 7 Patterns to Refactor post. (Let me clarify what I mean.) You can have a module that mixes an incredibly narrow API that makes doing a composition easier. So like when you have your authentication thing here, your service object with the user authenticator class, that you can do that and have this other object and then there can be a small mix in to your model class that gives it a nice API for talking to that authentication class.
BRYAN:
Yeah, I think that's right. You can use a mixin to patch together objects which are working together through composition.
JOSH:
Yes. That's a good way to summarize my… rambling.
DAVID:
That's… I'm sorry; I'm just sitting here kind of mind blown. Avdi, you mentioned that, “Hey you mix in a module and you just expanded your object’s API.” I’d never actually considered it that way, but holy freakin crap. Yeah.
AVDI:
Yeah. I mean the way that I usually put it is you know, some people when confronted with a class that's too big, think, “I will split this class into a class and five modules.” Now they have six problems. [laughter]
CHUCK:
So one other thing that I noticed -- and its very closely related to this -- is that, of your seven strategies or patterns or whatever you wanna call them, I like strategies better personally but we can use the word pattern if we want. Five of them end in object and five of them are extract x object. One is introduce view object and the last one is extract decorators -- and decorators are effectively another object that just had a different job. And so, I think it’s interesting when you are saying work with composition as opposed to inheritance. I mean, that's really what we are doing here is we are composing these objects that do the job we need them to do.
BRYAN:
Yeah it’s no coincidence that none of the 7 patterns I covered in that post actually involve including modules. [chuckles]
JOSH:
Interesting. Do we have more to say on that blog post? Or can we talk about class methods?
KATRINA:
We have more to say on that blog post.
JOSH:
OK. What do we have to say? Tell us Katrina.
KATRINA:
I was wondering what are various heuristics that you used to recognize -- for example that you need a value object. Like how do you recognize a value object that's hiding in the big class or a service object -- or policy object?
BRYAN:
I think that's a great question. You know, at the highest level one thing I do is look at the Code Climate scores for these things and to see, “OK looks like this ActiveRecord is getting pretty fat and the rating is getting pretty bad.” But more practically, once I'm diving into a class looking at it, there's a few things and my favorite is to look for methods that have repeated words -- in either as a suffix or prefix -- that is usually a sure fire pointer that you are missing an object. So if you’ve got methods in an ActiveRecord class that all end with underscore rating -- so you know rating better than, rating greater than, rating from room mediation costs -- those are some examples from the post then it’s really telling you want a class named rating. So that's my favorite -- by far – way to recognize concepts. It’s actually something I love to do on Code Climate automatically in the future is detect like you got 30 methods in the class and 7 of them have the same prefix. Are you missing blank or blank whatever that prefix is?
KATRINA:
Nice.
DAVID:
So, group up all your handler methods and say, “Are you missing an on class?” [laughter]
CHUCK:
Actually in my case, they just got the letters reversed -- it’s no class.
DAVID:
“No.” [laughs]
BRYAN:
One of the things about value objects that… probably one of my favorite refactorings and I think they are often overlooked in Rails apps and I read this post that kind of expanded my mind in thinking about value objects. It’s a little bit controversial, so if you indulge me. It was written by someone who used Java primarily and their point was, basically you can have a value object for everything, right? So if you have a name, you can have a name value object -- you can have one for an address, you can have one for a rating and age – anything you want. And so the question is really like in what cases is that appropriate? But there can be use in adding value objects for just a heck of a lot of primitive concepts in your application. And everything starts as generally starts as a primitive, right? You are creating a string column or a numeric column in your database and ActiveRecord pulls it out and you end up with a string or fixnum or a float in Ruby. But all of those are opportunities for introducing value objects. It’s just a question of which ones make sense. It sounds simple, but it really kind of expanded my mind in thinking about like, “OK, there are tons of applications to apply this. It’s just a matter of picking the right ones.”
CHUCK:
Yeah it seems like in the case of like name and address, I mean, if you are just putting it in the database and then pulling it out to display, you don’t probably need one. It seems like especially in your example with the rating is that there is always behavior around it. That it does all these different things you know, it does comparisons that you know, it puts it out to a hash and all that stuff. So there's value in putting that in there because it has some behavior on it whereas with name, I mean it’s just a string and that's all that you’re using it for.
BRYAN:
Yeah I think that's right. I'm certainly not advocating that anybody create classes for all those different types of values. In the case of name for example, in the Code Climate code base, I do have a human name object and it is for splitting the name into different parts -- so after the first name, the last name or the full name or the first name with the last initial – all of those things. So I think the interesting thing is just as soon as you start to hang behavior around a value, you can consider doing a value object. And there actually tend to be pretty simple refactorings, because you can make them quack a lot like the primitives in Ruby. And you have the duck typing so it’s just works really well if you are defining like to_s then you can basically define the default representation for a person’s name in your application by way of the person name to_s method. And that will just work when you are interpolating that into your views. So that's all what I was getting at there.
CHUCK:
There's one other thing that this kind of brings to mind and that is that most of the time, you have helper methods in Rails that do these kind of thing on the views to display things in a certain format or things like that. So, where is the balance between those and when do you wanna use one over the other?
BRYAN:
Yeah, that's a great question. You know, I think for me, I would not want anything that is specific to the delivery mechanism to creep into value objects. So by “delivery mechanism”, I mean for most people it’s going to be your HTML concerns, right? But, in the case of the person name example, that is something that is used both on the web and in email. So that I think that's a clear win for the value object. But even if it wasn’t going to be used in email at all, even if it was only for the webpage, I think I would still prefer using a value object and encapsulating the behavior in the value object if that behavior does not have presentational concerns. So getting at the idea that I'm not crazy about helpers [laughs] I think they are useful for small bits of temple functionality. It’s just kind of why they were written, but they get abused a lot. And so I would look at encapsulating into a value object first for anything that is not presentation specific.
JOSH:
The thing that I always run into around value objects is that when people start calling it a value object, I think that that sets up an expectation that it’s a passive piece of data. And that I mean the pattern… I don’t know if it’s a pattern or principle or what but using value objects, you are really thinking about these things as passive entities -- they’re just repositories for state and you pass them around and other things figure out what to do at that state. And that can be, I think a great way to like loosen the coupling between pieces of code, but I think it can also get you in trouble in that it distributes the behavior related to that data in the various places that… and that putting that behavior into the value object’s class can… sometimes you wanna do that instead -- and in fact a lot of times, you wanna do that instead, I find.
BRYAN:
Yeah I think that's right. You are saying by creating a value object, it allows you to centralize the behavior around that concept?
JOSH:
Yeah, absolutely. And that's like 80% of the reason why I create what people call value objects is a place to put that behavior. But I think when you call them “value objects” in like the GOOS’ approach to value objects, I think is very much they are just passive objects their state. I don't know, maybe I got that wrong because I didn’t get the chance to read all of GOOS [chuckles] but am I off base there or is this… Is calling them value objects a way to trick people into not taking advantage of the behavioral aspect of them?
BRYAN:
Well, I think that you know the value object distinction is primarily around the fact that they are usually immutable and their identity is sort of derived from the data that they are holding and not sort of an external system. So a property of value object is usually if you initialize two of them with the same variables, then they are usually going to be equal to each other. That's a good distinction. But you know, I don’t think that it necessarily means that they can’t have behaviors I think you are just looking to create behaviors that are sort of fine-grained, like used in multiple places are kind of the things that… just like Ruby has a very rich set of value objects built into it, right? You've got everything in fixnum and string, and arrays which called multiple objects and they have all these behaviors that hang off of them and that's Ruby is very general purpose. You can establish behaviors that are specific to your application domain and hang them off of value objects that are just in your app.
JOSH:
OK.
AVDI:
So, can we talk about class methods?
CHUCK:
Yeah.
BRYAN:
Absolutely.
AVDI:
So why do Ruby class methods resist the refactoring anyway?
BRYAN:
[laughs] Good set up Avdi.
AVDI:
[laughs]
BRYAN:
Why do you ask?
AVDI:
I don’t know. I heard something about that.
BRYAN:
Yeah, so this is something that has come up for me a number of times over the years and I have had this discussion with programmers on the teams I have worked on and finally I was like, “You know what, I think I wanna clear up all my thoughts about this and put it on a blog post so I don’t have to keep remembering.” But the idea of the post is sort of ensure that class methods are global, and as a result of them being global, they tend to resist object-oriented approaches to decomposition. There was a great comment thread on the post where people talked about doing functional decompositions of class methods -- and that kind of work. If you are doing more of a functional style, then you are just going to be passing data down this chain to the different you know, decomposed functions in the finer, finer level of detail. But I usually prefer to start with a base where I can do object-oriented refactorings. And if you have your behaviour siting in a class method, you can’t do simple things like extract methods without starting to run into friction. You can’t use instance variables. So the post was primarily response to people who might look classes, but don’t have any state and that only have one method and say, you know, “Why did he do that? You could have just written a class method.” And in some cases, I do use classes and I covered a few edge cases in the post, but generally the reason I gave you that is it just makes it a lot easier for me to see opportunities for refactoring and then take them, whereas if I have everything bunched up in a class method, it’s harder for me -- at least when I'm looking at code -- to extract those and decompose those. And I have seen anecdotally that on team’s behavior that starts simple in class methods general just becomes complicated class methods rather than getting pulled out and neatly decomposed.
AVDI:
Yeah and you even sometimes see stuff where you know, they’ve started sort of simulating having instances, you know, classes might be start to keep track of the multiple sets of information or something like that. I actually saw an example of where this can go bad. Recently I have someone come to me a -- pairing client -- come to me and say, “I've got this redesign that I wanna do. Well, refactoring a redesign.” We have this program that we wrote as a one shot command line thing that would collate a bunch of data. I forgot if it sends out reports or what but you know, it’s a one-shot thing that it would run and then be done. And so it was written with a whole lot of class methods, a whole lot of class level data you know, so the classes had class instance variables. And it worked great as this command line program but they wanted to turn it into a – something that would sit persistent memory and periodically do this task that it was supposed to do. So suddenly, you have this problem where you had classes with data specific to a single run and that data would just sit around and not be cleared out the next time it wanted to send stuff out because it’s siting there in memory. You know, if they had just created instances in the beginning, there wouldn’t have been an issue; it could have sat there in memory but each time it actually kicked off the batch job, it will just be starting up a new instance and it would create whatever instances. And so we went over various strategies for tackling it but it was one of those things where I probably wouldn’t have that pairing session to begin with if you know, the program had been written with instances in mind.
DAVID:
So bad code brings us together is what you are saying?
AVDI:
Yes.
CHUCK:
[chuckles]
DAVID:
There's a favorite pain point that I have in Ruby. I came to Ruby through Python, through Perl and so, I write a lot of command line scripts. And it’s really tempting to say you know, #! /usr/bin/ in Ruby and it just start writing do something, do something, do something -- nice procedural code. I love it. And I reached this point where my script is now 120 lines long. I really have to extract this to a method. So I extract it. Oh, but now I can’t use any variables that I don’t pass in because they are no longer in scope. And God help me there are few times when I would just start making global variables with the actual dollar sign so that they can be used inside the method. And then I kind of grew up a little bit and then I realized, “OK there's this point when your script hits somewhere between 25 and 100 lines but after 100 lines, you definitely past this point when your script should begin with class application or class my script and your script file should end with if $0 == __FILE__ script new run. Now you've got instance variables and now all the OO stuff that you want works. That's a favorite pain point of mine is when you extract something to a method and you can’t get to the variables that are outside the method.
AVDI:
The obvious solution to that is to write your initial script with nothing but globals.
DAVID:
Yeah.
AVDI:
I'm actually kind of half serious about that.
DAVID:
[chuckles]
AVDI:
If you are going to write just a straight line script without any abstraction to start with – which is a perfectly reasonable thing to do – write it like Perl and then stick a $ on front of everything. And then you know, that refactoring process is a little easier because you still have access to that variable. You can still just go through it one by one and start turning them into instance variable.
DAVID:
Well, I joke about it but I mean if you'd open that script and read it, the first thing you would go is, “What the?!” And that's the correct response.
AVDI:
[laughs]
DAVID:
I'm serious. I'm serious. You now know that you have left the script. We have departed the text. This is not going to be clean object-oriented Ruby, this is a glu file and, “OK, all right. We are including it.”
BRYAN:
So variables with dollar signs are my favorite global variables. And the reason for that is it’s really freaking obvious they are global variables -- so you can’t get that confused. On the other hand, you got things like class instance variables, class variables, their module equivalence -- there's a lot of different ways that you can have hidden global states in Ruby. So I love the dollar variables because it’s just like there's no way to miss the fact that this is global variable towards the entire program. End of story.
DAVID:
There's an analogy that I like to make here -- and this is kind of unique to my personality -- but when I was a kid growing up we have a cat and a dog -- several cats and several dogs. The cats would poop behind the aquarium, and the dogs would poop in the middle of the floor – and we loved the dogs more. [laughter] The dollar sign is pooping in the middle of the floor.
AVDI:
Well then there's the constant that is always hiding in plain sight -- the global that is hiding in plain sight which is the constant. Any name of a class or whatever.
JOSH:
Yes exactly. OK so David when you are talking about like, “I got this big messy script. There's no object in it or no classes,” it often strikes me that a good way to approach that sort of thing is like a first step refactoring is to turn it in to the equivalent of a method object.
DAVID:
Yeah. The class application def run [pbbbt] contents of the entire script, end to end.
JOSH:
Yeah you pull all the locals in the instance variables and then just start breaking it down into small methods that use those instance variables -- the same way you do on extract method object refactoring.
DAVID:
Yup.
CHUCK:
So I don’t think we've taken this head on, but when is an appropriate time to use class methods or globals? Because it seems like class methods and globals are mostly the same thing just the scope is a little bit different.
DAVID:
I think if you've already got a bunch of fat models hanging around then it’s great. (OK I’ll work on funnier stuff later.) [laughter] I don’t have a good answer. When is it good to use class methods?
JOSH:
I think that the obvious use case for the class methods is factor to create instances.
CHUCK:
Mh-hmm.
BRYAN:
Unless those factors get complicated, yes.
JOSH:
Oh yeah. Yes there's always a for you to follow.
CHUCK:
And when it gets complicated— [crosstalk]
AVDI:
That's when we need factory factories.
JOSH:
Yeah. [chuckles]
AVDI:
[laughs]
JOSH:
I want a factory repository.
BRYAN:
I was reading --- new book recently and he talks about how Rubyists love factories, they just *hate* the word “factory” and all these factory things. But if anyone goes so far as to call it a factory -- or god help you -- put factory in a method name or a class name, everybody flips out.
DAVID:
Yeah. Over here we call it builder. Over here we call it builder. Yeah.
JOSH:
Yehuda wrote a blog post a couple of years ago that basically said Ruby classes are factories.
AVDI:
Yup. Accurate.
JOSH:
[chuckles] And that was basically the whole post. Bryan, do you wanna talk about some rule of thumb for noticing when your factory got complicated or is it just an exercise left to the reader?
BRYAN:
You know, talking about if you have a factory implemented as a class method when it’s complicated and you wanna look at abstracting it out?
JOSH:
Yeah.
BRYAN:
For me, that's on the order of a few lines. Just for the same reason that I wrote the post about not using class methods for most things, I think I generally just use them to convert, maybe take an object and build up an instance with it by calling a few methods and passing state down. But as soon as you start to get into there's any cyclomatic complexity, I think that would probably be one tipping point. So anything beyond just a few method and locations for me on this look at extracting into a class. Now, I might keep a class method as a convenience. This is something I think I mentioned in a post a little bit that I don’t mind using class methods that are really just a short way to make it easier for programmers in the rest of the code to build up instances even if there is a factory that's being used under the hood, but at that point they are really just sugar. So I think that use can be OK and you can have your cake and eat it too almost.
JOSH:
That sounds good. Yeah I think that's in keeping with my clients.
CHUCK:
Now I'm hungry. We are just talking about sugars and cake.
BRYAN:
[chuckles]
JOSH:
Can we get some pie on there too?
BRYAN:
I'm fresh out of pie analogies.
CHUCK:
So, I think it’s interesting -- and I kind of wanted to talk about this just a little bit too -- I remember when I got into Rails at first it was just, “This is way cool!” And then I start talking to people and they are, “Skinny controllers, fat models. Skinny controllers, fat models.” Do you think that that mantra in the Rails development community has heard this at all and--
BRYAN:
Yes. [laughs]
CHUCK:
Or do you think that this is just the next evolution of the skinny controller, fat model and now it’s skinny controller you know, skinny model and composed intelligently.
BRYAN:
Yeah I mean you just don’t want anything to be fat is what it comes down to at the end of the day. I think what was meant by that anecdote is keep your controller layer thin and simple. Like, use your model layer to encapsulate all your domain options -- which is right. And so in the end, you will have a lot more meaningful code in your model layer than in your controller layer but I think that you know, in passing, it almost sounds like what's expected of you is to have ActiveRecord classes that are more than 500-lines long -- which I think anyone who’s maintained apps that have those classes over long periods of time can tell you what callback hell feels like, at least. (I'll work on being funnier next time too.)
DAVID:
[chuckles] You just sit here over by me.
BRYAN:
You are supposed to laugh at the guest.
DAVID:
Yeah. Oh, we are. [laughter] It’s just at the back channel but you can’t hear it.
JOSH:
Bryan I think you need— [crosstalk]
BRYAN:
Is there another call going on? [laughter]
JOSH:
Bryan, I think we've seen that fine tuning your ADD meds has a huge effect on humor level.
BRYAN:
I'll get on that.
JOSH:
OK.
DAVID:
Full disclosure: I'm freebasing Ritalin with cough medicine this morning…
CHUCK:
Oh dear.
DAVID:
And so, that may be why I'm laughing a little bit too hard at some of the jokes. [laughs]
JOSH:
OK we have one more blog post in the series of awesomeness right now which is Objects the Unix Way.
BRYAN:
Yes.
JOSH:
Can we move on to talk about the objects that you can snap together like LEGOs?
BRYAN:
I'm good with that.
JOSH:
OK.
BRYAN:
[laughs]
JOSH:
Is that a strained analogy? I'm really good with the strained analogy as it turns out.
BRYAN:
Yeah. So this post, I just wanna call out, it was written by a fantastic guest post author named John Pignata who I have had the pleasure of working with in the past on a project -- in a couple of projects actually -- and he sort of distilled this kind of comparison between the way Unix is built and the way object-oriented system gets built into this post is great. So all of the credit goes to John on this one.
JOSH:
OK. So what is the take away from this?
BRYAN:
I'll give you the take away. The take away is that creating small building blocks that have clean interfaces and have specific names where you are encapsulating functionality, gives you the foundation to start composing them in ways that weren’t originally intended. It’s just like you can pipe things together with bash on a Unix command line if you have objects which are decomposed and small and fine-grained, you can start to get that benefit of reuse in the small that it doesn’t work as well for re-use in very large cases but you can end up with classes that are actually reusable. And I think a lot of this is in Open/Closed Principle, which is one of my favorite objectoriented principle when I'm able to pull it off.
JOSH:
And for our listeners at home, Open/Closed Principle is?
BRYAN:
So the Open/Closed Principle says that objects should be closed for modification, but opened for in its extension. And basically it’s like you write a class, it does one thing, it does it well, so there's not much of a need to go back in and modify the class later at all. You can create substitutes; you can extend that class and build more things on top of it. But once it does the job that you defined for it, why do we need to reopen it and go back and modify it 34 more times?
JOSH:
So I have a counter to that -- that I'm actually curious to hear your thoughts on that. There's a couple classes that Smalltalk programmers are really used to that Ruby programmers don’t see much of because Smalltalk was built as a graphical programming environment, so there's a lot of stuff in there for dealing with things being painted on a display and user is interacting with that. So you have a lot of geometric classes within the system and point in rectangle are very commonly used objects within Smalltalk; and we don’t get them in Ruby. They are not part of the standard library that you see all the time. So, one of the things that you often do when you are building something like a graphical interface or a graphics editor or anything where you are doing stuff on the screen is you do hit detection to say, “Oh well, did this click happen in this rectangle which is the bounding box of the view or I'm driving this button around,” or what have you that you wanna know if that click is inside the rectangle. And if you are approaching things from, “OK, I have this rectangle class and it knows what it’s shape is and it knows how to paint itself,” and then I'm in some controller or what have you where I want to know if a click is inside the rectangle, I can write that code in the controller to compare the location of the point with the extent of the rectangle and see if there is an intersection or if there is containment. Moving that piece of code into a method in the rectangle to see if the rectangle contains a point means that you can now use that code everywhere in the system that would care about, “Is a point within the rectangle?” And so it’s really clear to me that putting that method inside the rectangle class is the right place to put it.
BRYAN:
Yeah it sounds like – if I can sort of like concept this to Ruby code -- it’s similar to re-opening core classes and building more functionality on them. Is that accurate?
JOSH:
Yeah I’d say you have this rectangle class that everybody in the system uses and all of your application use it; and you have this new thing… and I mean the rectangle class in Smalltalk knows how to tell if a point is inside it. That's everywhere. But there must have been some moment in the history of Smalltalk where somebody came up with that need to have that method and decide that the rectangle was the right place to put it and if they had been building a controller at the time. And the Open/Closed Principle is like, “OK we are going to have extend this class but we are not going to open it for modification.” At some point, you have to figure out what goes in this class and--
BRYAN:
Yeah. OK I think I get you. I mean so this is I think the interesting part about applying the Open/Closed Principle; I guess I don’t feel so much like I apply the Open/Closed Principle -- I end up. When the design is going well, I end up with objects that happen to follow it. So I don’t really ever take the Open/Closed Principle and look at the change I need to make and say, “Well, I'm not going to open this class and put this here because of the Open/Closed Principle. Instead, I’ll kind of ignore, like I don’t think… that's not one of the principles I usually tend to think of one actually making the change, but then I'll notice -- if my design is going well, I'll notice that it seems to be following the Open/Closed Principle because I have these classes that I haven’t had to make modifications to that seem like they are finished. And if the design is not going so well, I’ll notice that I have to reopen let’s say the user class in an application and continuing to put stuff in there.
So it’s kind of one of those things where it’s more of an end than a way to get there for me.
KATRINA:
It also seems to be a matter of like how much. Like opening it once and then adding that thing that really belong there is really different from churning from ending up reopening it every other day, fixing bugs, adding new behavior, taking it back out -- that's a whole different thing.
BRYAN:
Yeah. And I would say that the simplest modification that you can make to an object -- let’s take this rectangle for example -- would be just adding a new piece of behavior like collision detection; that doesn’t change the behavior that's already in there. You are just saying, “OK rectangles didn’t need this at the time, but collision detection is a core concept to rectangles in our system. We are going to add that.” And like Katrina said, it’s one change but it’s not going to 6 other methods that have been in the rectangle for the last three months and rewriting them for the 17th time.
AVDI:
So when I think of the Open/Closed Principle, I usually think more in terms of objects that I can make my own extensions to easily without actually putting anything, without reopening a class or anything like that. One of the examples that I see a lot is the idea of callbacks. And not like heavy duty callbacks, where you tell some class, “Hey on this event, please do this,” but more like I guess internal callbacks. Like when you have a class who’s named go method, you know, primary method is follows the composed method pattern where it’s basically like just a series of method calls one after another. You know, might be like the first one is like set up and then the next one is do some work and then the next one is tear down. And then I can inherit from that class. You know, it’s got its own setup and tear down and do some work methods but I can inherit from that class and I can just override any of those methods in my inherited class very simply. I don’t have to replace like that main go method in order to get my own stuff in there. If I wanna do a version that has a little extra set up, I can just inherit from it and replace the prepare method and nothing else.
BRYAN:
That's really interesting. I hadn’t thought about it in that sense so much. I think primarily because I don’t do a ton of inheritance. So I think it’s interesting because the principle is it should be closed for modification and open for extension and you know, usually extends is it will often kind of closely related to inheritance. But, I think that when I'm thinking about the Open/Closed Principle, it’s almost closed for modification and reusable in different ways. And sometimes that's inheritance, but for me usually it’s not.
AVDI:
Mh-hmm.
KATRINA:
I often think about extensions in terms of decorators.
AVDI:
Right. Is there like an example of that that you can think of?
KATRINA:
Off the top of my head?
AVDI:
[laughs]
KATRINA:
No.
AVDI:
Did you like my highly specific example of like do some work and set up?
KATRINA:
Yeah.
AVDI:
Because that was a really specific example.
KATRINA:
Yeah I mean you are— [crosstalk]
AVDI:
Concrete.
KATRINA:
I find myself using the delegator or delegate from standard library a lot just to… I have one of the projects I am working on has 60 or 65 sort of base objects that are really tiny -- they are all like instances of the same thing. And instead of having a PostGres database and ActiveRecord, I just kind of hardcoded 65 hashes. And then I used these objects and several different applications. And then these applications they might need a little bit of extra logic. Like in some of the applications, they do one type of work or back to doing work and setting up. And in other applications they'll do other types of work and I'll use delegate just to have the little object in sort of at the center of things and just delegate all of the usual things to that and just have 2 or 3 methods that do that custom stuff.
JOSH:
I like that when what you are doing with that object it doesn’t need to know about it. If you are just like wrapping another layer around the object, that's perfect. It seems as you get into where the guts of the object need to care about what you’re adding to it, then that starts working so well.
KATRINA:
Yeah one of the examples from this thing is that I need a geo location for these things. These objects knew who they are, but the geo location will just take who they were and then do a bunch of calculations based on that.
DAVID:
One of the things I love about Ruby is that you can take these decorators -- and for lack of a better term -- mix them in, monkey patch some in to the class. We talked about limiting that a little bit, but what I love is that like if you would take the array class and it really, really, needs this method that's going to add a geo location to it or something like that. If you call map, you are going to get back a bare array in any other language and you are going to lose that long behavior mixed in like in Java and C#, you have to write an array to class or a C string to class if you are going to modify these things by extension. But all the library methods give you back the base class that you extended. And I love in Ruby that you can modify that base class and it stays modified so that your API stay consistent.
CHUCK:
All right. Well we are getting pretty close to needing to do picks, are there any other areas or aspects to this that we wanna talk about?
JOSH:
Bryan, what should we keep an eye out for? Are there going to be more of these things on your blog?
BRYAN:
Yes.
JOSH:
Can you give us a preview of coming attractions?
DAVID:
Yeah tell us what you are going to tell us about the next time we have you on the show.
BRYAN:
[laughs] Yeah so you know, one of the things that I just started kicking around today was writing a little bit about some of the functional core imperative shell stuff that's been talked about a bit. You know, Gary Berndhart and Michael Feathers talked about that stuff, so I think I'm going to start kicking around some ideas for that sort of thing. I also like to cover some different ways to structure controllers that are maybe a little bit newer that people might explore to try to decompose their controller layer in a more factored way than what Rails kind of gives you out of the box kind as kind of an exploratory conversation. I have… I don’t know I have 30 sort of rough concepts in the queue, but yeah if people check out the Code Climate blog, then we are trying to beef up the posting on that so there should be some pretty regular stuff coming out just really along these lines. If you are interested in the intersection of object-oriented design, Ruby, Rails, all of that stuff, that's where it will be.
KATRINA:
I have to say I've really been enjoying the Twitter feed from Code Climate.
BRYAN:
Oh thank you.
JOSH:
And do you wanna take a moment for a shameless self-promotion to talk about like how things are going at Code Climate. You know, how’s the business going, customer happiness.
BRYAN:
Yeah sure. Thank you. For people who might not know, Code Climate is my business. It does hosted automated code reviews for Rails apps; and it does that using static analysis. So if you hook up your app to it, it will give your team feedback over time about how the quality of your code is changing, what you can use to get a better handle on technical debt and improve your quality. Fortunately, it’s been going very, very well. And in fact, it is entirely my full-time thing these days. it’s really just me at this point but yeah, the business is going well and we've been launching a lot of features now that I am not doing any freelance work. So we just launched I guess this is Ruby timely a compare view which I'm really excited about. And if you think of GitHub’s compare view where it will show you just the commits between any two points in time, we can do the same thing except we are showing you specifically the changes of the quality of those classes between two end points. So if you have new duplications, fixed complex methods, all that stuff will show to you in a very sort of clear red/green sort of view, where you can see what the new problems were reduced and the ones that are fixed. So that is the newest feature that I will plug.
CHUCK:
Nice. It sounds good. Sounds like something that could definitely be put to good use. All right well let’s get into the picks. David, what are your picks?
DAVID:
Two ones -- pretty quick and easy. two books that I'm reading right now that I'm really enjoying the heck out of; one is The Happiness Project by Gretchen Rubin, which she just spent a year experimenting to find the things that just made her happier. And she made a lot of surprising things that challenge can be a source of happiness, money can buy you happiness if you spend it wisely, being organized, outer order contributes to inner calm -- that sort of thing. It’s a really compelling read. I'm really, really enjoying it. The other book that I'm reading right now, we all know about Dale Carnegie’s How to Win Friends and Influence People and its 76 years old. It was written in 1936, so it’s pretty skiable, right? Well actually it’s not. The reason why nothing has replaced it is because it’s awesome. If you have not read this book, you need to go read it. Some of the stories he uses, he talks about Teddy Roosevelt going on Safari in Africa. So yeah, some of the stories seem a little distant, but when he talks about big oil scandals, that still seems kind of relevant. You know, basically Carnegie’s if you want the tl;dr of the book, you can win friends and influence people by not being a butthole is kind of his approach to it. It’s how to be a nice person and still get people to be influenced and to want to basically be part of your tribe, so that you can influence in that way.
So those are my picks actually.
CHUCK:
All right. Katrina, what are your picks?
KATRINA:
I have two picks today. The first is the 24pullrequests.com. It’s the secret Santa for open source. By the time this show airs its actually going to be half way through December, don’t let that get in your way. Go read issues on open source projects and just read through 40 or 50 issues and then read through them again trying to understand them, read some source code, try to reproduce it, create a failing test. Especially when you can understand what's going on, document that confusion by asking clarifying questions. I had some nice successes last week just by asking, “Hey, is this still relevant?” And I think like four issues got closed just by not being relevant anymore. So that's cool. The other pick that I have is sort of not technical code wise. It’s how to test for proper pan heat when you are frying food. So there's this online cooking school rouxbe.com (R-O-U-X-B-E).
DAVID:
Nice.
KATRINA:
There's a video for how to test a frying pan for the correct heat and I really like it. So those are my picks.
CHUCK:
Super. Josh, what are your picks?
JOSH:
I'm still reeling from rouxbe.com [chuckles] That's awesome. OK. Geez. I always look at my picks and like, “Yes this is what made my life awesome in the last week,” but I they are not very technical right now. But they are still pretty awesome. I discovered a blog in the last week that just sort of like changed my whole outlook on life. It’s called Fashion It So and this is some very fashion conscious folks -- a pair of them -- talking about how awesome fashion is in the Star Trek Next Generation show. And it’s actually like scathing commentary on the shows themselves. It’s not like they start at season one, episode one and worked their way through the show, they go through the shows that have the most interesting fashion to look at. And they talk about like what Troi’s dress is and Deanna Troi’s funky jumpsuits and what the hell was Picard wearing this week. [laughter] You know, why the hell was he in a jacket and everyone else is in--- [chuckles] and where did Ensign Ro get this zipper on her jacket. It’s actually really smart fashion. And if you have any interest in fashion, its actually kind of educational. I learned about dolman sleeves -- I never heard of them before. But it’s sort of like the ultimate confluence of I guess Star Trek geekery and sarcastic drag queen commentary. So if those things are appealing.
DAVID:
[laughs] That's awesome.
JOSH:
It will make your day. [laughs]
CHUCK:
I'm trying to figure out exactly where that intersection puts it.
JOSH:
Well, go take a look at the blog and you’ll see. By the way, it’s definitely got some adult themes going, so I wouldn’t share this with your kids. But if you’re a grown up, it’s pretty funny. The other thing that I have is given that it’s December and we are coming up on the winter solstice, and one of the things that I hate about this time of year is all of the Christmas carols that they play pretty much everywhere you go. Everywhere you go. So an antidote to that is the wonderful Lovecraftian site, Very Scary Solstice Carols and that's at cthulhulives.org/solstice. It’s just amazing. They have song like it’s beginning to look a lot like fish man. [chuckles] But you know, the most horrible time of the year, anyway. And they have not one but two CD collections of these songs. And you can download the MP3s to check them out. It’s just you know, you have Death to the World on Holy Night, The Carol with the Old Ones. [chuckles] It’s a good for a laugh especially if you are into filk singing and if you don’t know what that is, go look it up. That's it for me this week. Hopefully someone will have something more technical to amuse you with for the next picks.
CHUCK:
All right. Avdi, what are your picks?
AVDI:
Well, I was looking over the list and I was very surprised finding I never actually picked my mic. So way back when I realized that I was podcaster now, you know, shortly after I got the Wide Teams Podcast started, I picked up a Blue Snowball microphone, which I guess is kind of almost the default starter podcasting microphone -- and for good reason. I mean, I got a lot of compliments for how I sound and I totally all that to the microphone. I have a little pop filter on it. And it’s a mic that you can get for, you know, relatively inexpensive as mics go. You know, definitely under $100 and it will make you sound great. You just plug in to the USB port; you don’t have to worry about getting interference on your analogue cables because it plugs in to your USB port and acts like a sound card. You know at this point, I'm thinking about… I'm like drooling over fancier microphones and thinking of upgrading, but I don’t actually have to because this thing just works. So if you are thinking of getting in to any kind of broadcast or just wanna sound good while you are on remote meetings or something like that, pick one of these up – Blue Snowball – they are great. And for a not at all technical pick, there seems to be a spate of like Snow White inspired movies and things lately. One that just crossed my radar recently is actually from I think 2004; it’s a German movie called 7 Dwarves. It’s in German. It’s subtitled. You can find it on Netflix. And it’s basically like a spoof almost or a very silly version of Snow White -- and it’s hilarious. I would say its kid-friendly, but it’s also not very definitely not kid specific. I mean there is like an 8th dwarf that you know, actually like normal person size that the dwarves won't let in to their club. And it turns out that the dwarves were actually just a bunch of guys that all were unlucky in love and decided to start a household where they would never count on a woman again and then you know, Snow White stumbles into their lives. It’s a lot of just like silly slapstick humor. It’s cute and adorable and fun.
DAVID:
Sounds like a sitcom. It’s like 7 and a Half Men. [laughter]
AVDI:
Yeah.
CHUCK:
Awesome. Well I'll go ahead and go next and then we'll hear Bryan’s picks. My first pick my wife -my birthday is not for another week -- but my wife got me this birthday present and then I wound up having to use it because I replaced the alternator in our minivan. The first one is it’s a Low-Profile Creeper which is what you lay on with wheels that we get under the car with -- just terrific. It has this big honk and wheels on it. Like it says it’s low profile, so you can get under pretty much anything you are working on and not get your shirt or pants or whatever dirty. The other thing that I got for myself for Christmas and wound up using was a 2.5 Ton and 2.5 ton is what it will lift, not it will weighs. And it got our van up the ground pretty easily and it only took me a couple of hours to get the alternator out and put it a new one in. So I recommend those. I don’t know, that was kind of my highlight for the week was working on my car. Anyway Bryan, what are your picks?
BRYAN:
Yeah. I've got two picks this time. The first pick is brakeman, which is static analysis tool that finds security vulnerabilities in Rails applications. It’s actually maintained by Justin Collins and some of the folks that he worked with at Twitter. It’s a great easy thing that you can do if you have a Rails app and you deploy it into production that you just run on the command line and get potential security vulnerabilities that you have in your app, and it will actually rank them by the competence the tool has that it’s actually a real issue. So it’s something I recommend everybody run at least once against their Rails app to make sure they don’t have any sort of security vulnerabilities creeping in. And then it’s something you can obviously run on an on-going basis. The second pick is a save the date for GoRuCo -- which is Gotham Ruby Conference is New York City -- and we are going to be having that on Saturday, June 8th 2013 and we just announced that date. So mark that off. There will be a CFP at some point, so if you are interested in speaking, you can start thinking about that but we would love to have you and all of the listeners in New York on June 8th for the Gotham Ruby Conference.
CHUCK:
Awesome. All right well, let’s go ahead and wrap this up. Are there any announcements or other things that we want to go over before we end the show?
JOSH:
Let’s mention our Book Club book.
DAVID:
Oh yes. I was going to pick that again.
CHUCK:
Practical Object-Oriented Design in Ruby.
DAVID:
Yup.
CHUCK: by Sandi Metz.
JOSH:
Do we have a deal or anything for our listeners?
CHUCK:
So I contacted the publisher; you can get a deal on informit.com, but it is basically a 40% discount on two books is what they gave me.
DAVID:
That's not bad.
CHUCK:
So if you buy two books, you can get a 40% off both books.
JOSH:
Do they need a code or they just go in— [crosstalk]
CHUCK:
There is a code. It’s on the Ruby Rogues website.
JOSH:
OK. Cool.
CHUCK:
I think that's everything. Go sign up for Ruby Rogues Parley if you want to be involved in awesome discussions about code quality and other things. And other than that, I guess we'll wrap up. We will be back next week with another show.
DAVID:
Bye. Thanks for coming.
CHUCK:
Yeah. Thanks for coming Bryan. It was awesome discussion.
BRYAN:
Yeah, great being here. Thank you!
083 RR Decomposing Fat Models with Bryan Helmkamp
0:00
Playback Speed: