00:00:15.360
Hi! So, we may not need an introduction, but I'm giving you one anyway. Okay, so we are the Ruby Rogues. I'm Josh, and this is David, Chuck, Katrina, and James.
00:00:22.080
We are almost ready to get started here with slides, but I'm not going to wait for them because the intro here doesn't really need them. I don't know what the Ruby Rogues are. Hey, who listens to us?
00:00:32.800
That's great! How many people here are on the Parlay email list? That's really cool. I'm going to make the announcement now rather than later. One of the things that we've been working on for a little bit is we had Jeff Atwood on the show a couple of weeks ago, a month or two ago, something like that. We talked about Discourse, which is this really cool open source project that they're doing to reinvent forums.
00:00:47.120
Built on Rails, with Ember as the front end, we're going to be moving the Rogues Parlay list from a Google Group email list to a Discourse forum. So we'll have much more flexibility in managing conversations. We can have plenty of off-topic stuff, and there will be a place for recruiters to send their spam. So it'll all be wonderful.
00:01:03.120
Okay, so we have slides up now, so now I'll talk about what we're discussing today. I'm going to do a quick intro, then we're all going to take three minutes each to tell you our positions. After that, we’ll have a fun debate, and you can ask us questions too. The topic today is best practices. Many of you may have heard of the book "Smalltalk Best Practice Patterns." We had Kent on the show a while ago to talk about this book, and everybody said we really need a Ruby version of this book. So, we're writing one.
00:01:51.759
We're going to be working with Pearson, who does the Professional Ruby series—the books with the red covers that you’ve probably seen a lot. Tomorrow, we're going down to the south of the river here in Austin, spending a few days at a retreat to start the book. Sometime next year, you'll be able to buy this book. Now, we're going to tell you why you should care about best practice patterns or best practices. So, James, can you start us?
00:02:34.959
Yes, but we have to switch slide decks because there are a lot of us. By the way, how do you like the hats, or headwear in general? That's not the right question! Who wins best hat? Okay, that'll come at the end. I'm sorry, I'm sorry. I couldn't bring myself to wear a hat; I've spent too much money on this hair.
00:03:05.519
Okay, so if you listen to Ruby Rogues at all, you know I’m the super talkative one, which is why they made me put in this slide. I want to show you my one point here; it is, if you can't see that, it's a period right in the middle. Okay, so that's it; thank you. No, I'm just kidding! It does tell you a lot about my personality that they give me a really tight time limit, and I would still burn three slides on a joke.
00:03:50.000
So I'm going to tell you why you should care about best practices. To use an example, this is a discussion I had online recently with several people. These are the different ways you can use struct if you want to add custom methods. A lot of times, people use the top form where they just inherit from what struct returns and put the methods in there. But you can also choose to pass a block to struct, and I use that. I like the latter one, but that's not the point.
00:04:07.840
Try not to get hung up on that as we talk about this. One of the reasons I do prefer the latter one is that if you use the latter, then a struct with custom methods looks similar to a struct without custom methods. There’s this concept that Josh talks about a lot—about code malleability—where it's easy to transform code. So in this case, you can just add or remove the block to turn it into the opposite one.
00:05:30.000
Another complaint I have against the inheritance one is that it actually uses two classes: the one that gets returned from struct and the one that you inherited from. This can cause problems if you're using any kind of code reloading because if this code gets executed a second time, you'll get a different parent class, and Ruby will complain that the parent classes don't match.
00:05:41.039
But my way's not all roses. Aaron Patterson pointed out that if you do use the class inheritance one, then you have access to super. You can write methods like you always do because they're at a higher level in the class hierarchy, so you're fine to super up to them or include some mixins. The super's up to them, whereas if you use my way, you need to use this alternate syntax to access the hash version of it.
00:06:17.520
Ruby 2 kind of solves the mixing problem since we can just prepend it instead of include it. But the point isn't that I'm trying to tell you the right way you should code your structs; I'm just saying that this is interesting, right? There are a lot of interesting concepts around this, like why code malleability is important. By having these discussions, we learn from each other and get to a better style of programming, and to me, that's the point.
00:06:40.640
That's all I have.
00:06:48.400
I’d like to talk about naming things. I started a project recently. Sandy mentioned it; it's called Exorcism. The first exercise there is a simple class named Bob. Bob is a lackadaisical teenager and he has a very limited set of responses. One of them is if you shout at him in all caps, he responds, "Whoa, chill out!" To everything else, he responds, "Whatever."
00:07:03.120
The simplest, absolutely simplest form of this usually looks something like this: you get a string, you check whether or not the upcase version of that string is the same as the original string and if so, it's "Whoa, chill out!", otherwise, it's "Whatever." I found that some people suggest that you should name this piece of logic 'is equal...' etc. as an implementation detail and that we can hide it. If we go back to the README, it says that if the string is all caps, then do the one thing.
00:07:32.000
But some people then say, 'Is it all caps?' and name that concept. I’d like to say that that is still an implementation detail. What we’re actually talking about here is someone shouting, which is interesting in this context because it's telling the story of Bob and his interlocutor. I’d rather see this thing called shouting. There’s more here: shouting right now means passing the message into shouting; Bob is determining whether he’s shouting or not. We can move this; we can say that shouting is a phrase and we can check if the phrase is shouted.
00:08:18.679
This leaves the logic of determining what is shouting, what is yelling, or whatever, to the phrase itself. There’s more! The variable 's' is very, very generic and in this context, we're actually talking about drivel. It's a conversation; it's meaningless. Choosing the name 'drivel' tells the reader that this is meaningless, this is drivel; this isn't a serious conversation. So this is a lot more complex than what we started out with, but there are names here that tell us the story of Bob and the person he's talking to.
00:09:05.040
And that's all I've got.
00:09:49.110
Let’s talk for a minute here about best practices. One of the issues I have a lot with some of the best practice discussions out there is when somebody says you should always do this, you should always do that. There's usually a situation with most of these best practices where there's a trade-off. Now, the interesting thing, if you listen to Katrina's talk earlier, is that she talked about the Dreyfus model and how beginners and novices, advanced beginners, often follow the rules.
00:10:10.800
There are things that they do simply because someone told them to, and they don't completely understand the trade-offs. I think this is an important thing to consider with best practices because they don't have the information to determine what the best practice is given a certain context. Under those circumstances, a list of best practices or a set of rules pulled together into a style guide can be really helpful for beginners.
00:10:50.000
So that's a lot of what this book is about. When we start putting together a list of best practices, it will help people start the conversation if they have the experience to have that, but for beginners, it tells them where to start and how to avoid some of the issues they don't even know they're going to have. I started looking at the style guide for GitHub. GitHub has a style guide for Ruby, and I just picked a few examples that I like for various reasons.
00:11:33.280
Now this example shows the difference between using 'if condition then' versus 'if condition'. It’s not a major thing, but if you've listened to Katrina, she gave a talk a while ago about refactoring and she mentioned code junk. In this case, the 'then' doesn't mean anything; it doesn't do anything or add anything to the conversation; it's just clutter. Some of the best practices are simply about readability, about focusing on what's important. You're probably going to hear a lot of that as you ask questions, read the book, and talk to us.
00:12:30.320
Some other ideas out there—I'm not going to go into the why on a lot of these—like doing flow control with exceptions. How many of you have done something like the top example where you've rescued some exception? Really, what it was was a fancy 'else.' I think we've all made that mistake; I've done it, and other people have done it. The other one that's my favorite is where you do a rescue and then return nil, so you have rescue nil at the end of the line. It relates to flow control because you're saying that you don't care that something bad happened.
00:13:40.240
But you know it can save them these troubles, and if you give them a style guide like this, then they won't make this mistake; they'll just do it that way because you told them to. And then, as they move ahead, they can start to build context around why they should or shouldn't do something. You know, the single quote vs. double quote debate—how many of you've had that debate? That's one of my favorites. It was on Parlay!
00:14:30.240
You can go look at the benchmarks; it doesn't make any difference! But anyway, GitHub's recommendation is to use double quotes because there's no penalty for using double quotes for a string. The whole idea is that you can avoid some of these issues and make sure people are doing the right things, at least to start with, by giving them a set of rules to follow. Then, as they gain experience, as they move up that Dreyfus scale, they can start to use that experience and knowledge to make their own best practices.
00:15:22.960
And that's all I have.
00:15:54.399
Okay. So I stressed over this talk and ended up throwing everything out last night because I had this huge epiphany! Oh, that's not good. Okay, we're not doing the slides because the disk appears to have corrupted them. Awesome, alright, that's fun! So, I have three code examples and three minutes to give them.
00:16:06.640
I was joking about how they needed to be so simple that I could give them without needing slides. If I want to do this in three minutes, so alright, broken slide deck rocks. I want to talk about patterns, why patterns, when you should use them, and where.
00:16:15.960
And what I realized was I wanted to talk about cognitive load, which is the amount of information you have to keep in your head when you're trying to debug code and understand stuff. Then I realized late last night—no, what I want to do is just come up here for three minutes and beg you to please write readable code.
00:17:06.000
The first example I have is an array: 1, 1, 2, 3… dot inject (: plus). This is Ruby's method; if you've got a functional programming background, this is reduce, or if you're from Haskell, this is fold. In Ruby, this will add up all these things by injecting the plus thing. From a functional standpoint and talking about primitives, that's great. But from an OO standpoint, what I really want to do is ask this array, 'Please, Mr. Array, what is the sum of your elements?' So, I want to say 'array.layout.sum'.
00:17:53.760
The problem is that if you’re new to Ruby, you’ll look at 1, 2, 3 dot sum and go, 'Hey, that should be six!' But if you’re advanced at Ruby, you’ll go, 'Wait a minute, there is no sum method on array! How do I debug this?' You can get into a big argument about whether this is valid and if it's a monkey patch; I’ve inserted the sum method into array, guess what—it's self.inject plus. If you’ve worked with me, you know that you can go to ‘lib/patches/array’ in this case (in Enumerable) to find the patch.
00:18:38.560
But if you don’t know this, cognitive load increases. So where do we make things more readable? It’s a discussion about trade-offs, and what I’m saying is it’s always a trade-off. The second example I had was I wrote a quick little bit of code: 'Send me this sandwich.' If sandwich has bread, do this thing; if sandwich has mayo, do this thing; if vending machine includes sandwich.
00:19:24.540
Wait a minute! I was having a lovely conversation with the sandwich, and now I have to talk to the vending machine? Here’s another monkey patch that I love to write, which is 'object.in_question?' It just says 'collection.include me, little self.' Again, an entry-level Ruby person will read through this and go 'sandwich.in.vending.machine,' and that makes total sense.
00:20:10.640
But boy, if you don’t know what it does and you’re trying to debug it, you won't find this method. So there’s cognitive load, and you have to have this discussion with your teammates. You kind of need to know where this stuff is coming from for this to work. The last example: you want to make intention-revealing names, but this can go horribly, horribly wrong.
00:21:20.240
We had users and groups: groups have users to send a message to people that are in your groups. You can imagine the code it becomes: user.group.members.map(flatten).unique. I had a co-worker who wrote the method user.all.users.in.groups this user is a member of, and I want to point out that if your method name has a dependent clause, you are revealing your intent to violate the law of Demeter.
00:21:49.100
Everything's a trade-off. I argued that this should be renamed to 'peers.' I lost the argument. User.peers lost because that was not a domain name in our project, and our designers were like, 'What are peers?' So it actually would have increased cognitive load across everything.
00:22:52.560
Best practices. We've been talking about style; it's a big part of that in code style. In any conversation about code style, oh, where’d my notes go? I moved them! Someone always plays the aesthetics card.
00:23:16.640
And geez, what happens in your life? Okay, I know you have my slides; I want my notes! Aesthetics are something; it’s about a sense of beauty or the appreciation of beauty. It’s not just something that’s about gut feel. People can look at code and say, 'Oh, that looks better.' They can compare sets of principles to evaluate something. If you have experience, you can look at something and intuitively know, 'Oh, that's bad code!' But for someone without that experience, gut feelings can be misleading.
00:24:05.920
So when somebody plays the aesthetics card and says, 'I think that code just looks better,' that’s an excuse for not really doing your homework to understand why your code is doing what it’s doing. Let's get concrete. In Ruby on Rails, the core team prefers to use symbols over strings. In almost all cases in the API, you can use symbols and strings interchangeably, and symbols can be more convenient. I can see that there might be an argument for using symbols.
00:24:46.400
Maybe a symbol looks a little better to some people. But that can go wrong. Here’s a migration from Rails. This is the standard way that the Rails core team prefers migrations to be written. The generators work that way, but the schema dump task still uses strings, and that got me in a bit of an argument recently. This is how I prefer it to be done with strings. I prefer this way because you can actually tell that the strings have a different role than the symbols.
00:25:34.960
Symbols are the names of options you pass to the type methods, whereas the strings are the actual names that you pass to the SQL DDL to tell the database what to name the column. So it says, 'Okay, great. I don't need to compare these things.' Identity is not that important.
00:26:00.480
This is the point where style can actually get you in trouble because it's not well reasoned out. That's all I got; thank you.
00:26:35.090
It's not there. It's definitely not.
00:26:36.090
Hello, if you have followed my blog at all, you probably know that I get a kick out of clever solutions for things. It’s kind of a hobby of mine, finding clever things to do with Ruby. I thought that my little addition to this would be talking about when it’s not such a good idea to be clever.
00:26:46.240
Quick pop quiz: this code uses standard libraries only. What does the true argument mean to transaction? If you’re like me and you use PStore all the time, you know that true means read-only. Except, actually, that’s not right! Every single time I use that method, I have to look it up.
00:27:14.200
Pop quiz number two: here's something similar. In this case, what does the false passed to instance methods mean? It means, 'Don't include the superclass methods.' Again, I have to look it up every single time. These are style problems because those booleans are meaningless to the reader.
00:27:43.480
There’s a solution to this, an approach suggested by several people, including some of my fellow panelists, which says symbols are truthy. So rather than passing a literal true to that method, we could pass a symbol to stand in for that true value and give that symbol a nice, readable name, like 'read_only.' Clever, right? Well, unfortunately, there are drawbacks to this approach. First, if I didn’t know anything about PStore and came to this code for the first time, I might think that 'read_only' must be a special flag.
00:28:36.640
I might want to know more about that, so I’d go searching through the documents or through the code for that string, 'read_only,' and I'd come to a dead end because it’s actually not part of the API. I could just as well have passed ‘chunky bacon’ in there, and it would work the same way, so it’s suggesting an API that isn’t actually there.
00:29:25.600
Another little problem in my opinion is when we get to the negative case. We want to pass false here, and there’s no false version of a symbol, so to simulate that, we have to negate a symbol. I’m not a fan of the 'not bang' thing in this context; it’s just not that readable to me.
00:30:09.480
So there are drawbacks to this clever solution. I want to suggest that the cleverest solution is not always the best solution. Let me show you an alternative: I assign a variable with a meaningful name called 'read_only' and assign it the value true. Then I pass that variable in. Wait, can you go through that again?
00:30:58.800
I can go through it again. Here's the same technique applied to the other method call. This time, I assign the false value to a variable with a meaningful name and pass that in as the argument. It’s an extra line of code, but I feel like this is probably a better solution even though it isn’t as clever.
00:31:47.600
Now we can take this a little further. We can actually do an inline assignment within that method call, so we’re assigning the variable and using the value right there inside the method call. But I think you know what I’m going to say next: don’t get clever! I’ve watched programmers look at code like this or write code like this, and they think that they’re using keyword arguments.
00:32:31.600
They see 'false' and still have false expectations about the code they are writing, and it works by accident. Then when they try to change it, it doesn't behave the way they expected, so, yeah, don’t get clever! Sometimes the simplest solution is the best solution.
00:33:04.280
If you’re not a RubyScreamer subscriber and you want to get a month free, there’s a URL you can visit. Thank you very much!
00:33:30.000
Can we talk about the actual problem there? If you define a method that takes one true/false flag, we will send David Brady to your house! I will send Christmas carols at you!
00:33:43.840
Actually, I don't know why you didn't reopen the read-only or not include the super symbol and have it return nil to be true, so you could just say include renault and it would be false. It cannot make arbitrary objects false in Ruby; it can't be done.
00:34:12.960
Okay, by the way, a quick note: that whole problem is caused by APIs where a keyword argument would have been better. We're going to take some questions because we have lots of people here. So we want to talk to you.
00:34:52.360
Question time! Bring them on, good ones!
00:35:11.200
Yes, Josh, why did you take your head off? It hurt a big head as a kid's toy!
00:36:00.000
In many ways, it sounds like the book that you're planning to write. There seems to be some overlap in the conceptualization of the book you're talking about writing or at least at the surface. There seems to be a book like 'Eloquent Ruby' or other books that intend to teach beginners the usual patterns. How's your book gonna be different?
00:36:44.400
We actually figured maybe we could just take 'Confident Ruby,' change the name, and put all our names at the bottom! I was hoping for a cage match with Russ because I think I could take him!
00:37:35.820
Josh describes this best: I actually have a serious answer to this. There are things you read and learn when you’re leveling up to master the language, and those things change as you get better. The things you do as a beginner will differ when you become more proficient. The patterns in Kent Beck's 'Smalltalk Best Practice Patterns' are things you do throughout your whole career, things you use multiple times a day when working on code.
00:38:23.240
Those patterns are, in my opinion, eternal; I will use them throughout my entire career of object-oriented programming. Russ's book is a good book, I like it, but I think it has a limited shelf life in terms of utility to me as a developer. Next question.
00:39:45.520
If it's more for a larger shelf life and a more career-driven approach, why would a Ruby-specific version help if there's already spelled out best practices?
00:40:11.320
That’s a really good question, actually. So I learned Smalltalk to read Smalltalk Best Practice Patterns. Has anyone else done that? Seriously, learning Smalltalk was interesting. It would be even cooler to have one in Ruby not just because it’s learning the language but it gives us an opportunity to address Rubyisms.
00:40:50.440
Ruby is not Smalltalk; it’s close in some ways and different in others. One of the things we've been looking at as we take the table of contents is that Ruby needs an entire section on its scripting capabilities that Smalltalk doesn’t even have. It gives us an opportunity to address things that are specific to us.
00:41:36.680
So, my question was a very good follow-up to what you just said about having sections in the book addressing things that are unique to Ruby. I think you have a really great idea bringing over that book to an audience for whom Smalltalk isn’t really an option.
00:42:15.000
I’m in that group where I wanted to read it, but I did not have time to pick up on the language to do so. It would be great if in the new version you could include sections on using things like metaprogramming and dynamic languages. As you said, you’re trying to make it something that’s not just about Ruby, but here are career things.
00:42:57.040
I think we’re seeing a lot of languages pick up on these features, and I think it would be great to see examples of doing things like that, practices that might not have existed in the same form back when the original book was written. I guess that's not a question, that's a comment, but what do you think about that?
00:43:38.920
We do have a section in the outline on metaprogramming, so we agree.
00:44:14.368
So I think I’d like to point the conversation towards best practices rather than just the book we’re going to write. Yes, there’s a case where you might have a conflict between your two different ideas. I suspect you’ve had discussions about how to resolve differences of opinion for style guides and how other people can use them to resolve their style differences at their offices.
00:45:30.240
I think the first thing is to say that we know we have different opinions because we get into it all the time. The interesting part is discussing the reasoning behind it. This is what Josh is talking about; it’s not just a gut feeling. We all have our reasons. Usually, I see that Obvi will always have a really good reason I hadn’t thought about, and I'll yield to that.
00:45:46.640
We can have these discussions and kind of see the light from each other.
00:46:10.640
Like, I just want to add that if we lock horns over it and we can’t bring the others around, maybe that's not a best practice. If we can then those are the best practices. If we made a good argument, like Obvi often does, and then there ends up being a reason why we think this is the best practice, that’s great; we’ve reached consensus.
00:46:41.760
And if not, then maybe that's not a best practice, or at least it's a signal that we need to show it both ways and explain the trade-off in the discussion.
00:47:19.560
The phrase 'best practices' is a term of art in many professions. If you follow best practices—if you're a lawyer or a doctor, an electrical engineer, or a plumber—and you do these things, it shields you from liability. You say, 'I did the thing that everybody does and that everyone accepts as the proper thing to do.' If somebody sues you for malpractice, you can say, 'But I was just doing what my entire industry says we should be doing.'
00:47:50.340
It’s harder to create such a baseline in programming because of the variety of problems we solve. The whole point of best practices is, potentially, a misnomer. We’re going with the name because there’s some tradition there, but just because it's a best practice doesn’t mean it’s the only practice.
00:48:30.240
Somebody said this yesterday, forgive me, I can’t remember who, but it was one of the conference volunteers about the book. He asked if I was going to blend all six of our voices into one homogenous thing, because if you do, please God, I don’t ever want to read that book! Yeah, there’s going to be a time when five of us stand against Josh, and Josh kicks our butt.
00:49:16.080
Here’s the thing: if we can create a book with six different voices in it and still have a coherent narrative, then I think we win. We win all the things! Are you going to publish the discourse on GitHub so we can fork early and often on that discourse, or what’s the mechanism?
00:49:54.040
Well, this course is an open-source project, so you can already fork and contribute to it. The application Discourse is open source, but the content will be private because it’s specific to our Rogues Parlay list.
00:50:43.600
Are you familiar with the Rogues private list? Last year, we set up an email list so the listeners of the show could support the production. You can pay anything from $10 a year to what, 50 bucks a month? We've had a few people do that, and we mentioned their names on the show.
00:51:24.720
All of the guests who come on the show get instant membership to the list, and many of them come on the list and talk with us—from Martin Fowler to Kent Beck. Recently we did an episode and Martin Fowler chimed in about all the things we got wrong, which was awesome.