DAVID: Got me a breakfast sandwich, stuffing it in my face. It’s made of sourdough and cheese and eggs. So good. My wife is awesome.
[This episode is sponsored by Rackspace. Are you looking for a place to host your latest creation? Want terrific support, high performance all backed by the largest open source cloud? What if you could try it for free? Try out Rackspace at RubyRogues.com/Rackspace and get a $300 credit over six months. That’s $50 per month at RubyRogues.com/Rackspace.]
[Snap is a hosted CI and continuous delivery services that goes far beyond letting you do continuous deployment. Snap’s first class support for deployment pipelines lets you push any healthy build to multiple environments automatically and on demand. This means with Snap, you can deploy your staging environment today. Verify it works and later deploy the exact same build to production. Snap deploys your application to cloud services like Heroku, Digital Ocean, AWS, and many, many more. You can also use Snap to push your gems to RubyGems. Best of all, setting up your build is simple and intuitive. Try Snap free for 30 days. Sign up at SnapCI.com/RubyRogues.]
CHUCK: Hey everybody and welcome to episode 158 of the Ruby Rogues Podcast. This week on our panel, we have Avdi Grimm.
AVDI: Hello, hello.
CHUCK: David Brady.
DAVID: Even a broken clock is right twice a day, or on the international space station, 32 times.
CHUCK: I’m Charles Max Wood from DevChat.TV. And this week we have no guest and no topic. I’m just kidding. So, we had a half-hour discussion about what we were going to talk about. And I guess we’re going to air some of our dirty laundry and talk about some of the things that at least are generally accepted to be not the best things to do, or that we’re not doing that are the best things to do, and talk about why we do that. In my case, it’s all laziness. I don’t know about you guys.
DAVID: I like it when a confession becomes heresy. And then you find out that everyone around you is also committing that particular heresy. And you realize that, “Oh, you know what? Maybe the emperor really does have no clothes.”
CHUCK: Yeah. So, before we get going too far, I want to just briefly announce that Midwest.io is a conference in St. Louis, I believe.
AVDI: Kansas City.
CHUCK: Kansas City.
AVDI: Kansas City, Missouri.
CHUCK: That’s what I meant.
AVDI: Going to get some angry emails. [Chuckles]
CHUCK: So anyway, they’re putting on this conference…
DAVID: Those people all look the same to me.
CHUCK: [Chuckles] Yeah.
CHUCK: They’re putting on this conference and they are offering a ticket to one of our listeners. So, in order to get the ticket, we’re just going to give it to one random listener who does the following: if you tweet with the hashtag #RRMidwest and you tell us what your favorite episode of Ruby Rogues is to this point, and also mention just a quick thanks to the Twitter account for the conference.
AVDI: Midwest, which is @midwestio.
CHUCK: There you go. So, if you tweet with all of those things, then we will put you in the drawing and will pick a winner. I’m not sure when we’re going to end that. So, do it soon.
AVDI: And if you win, you’ll get to see me talk about Bash at Midwest.io.
DAVID: Oh crud, are we eligible to enter?
CHUCK: I don’t think so.
DAVID: Aw, man!
AVDI: So, I’m biased because I’m speaking at this conference, but it does look really cool. It’s a multi-disciplinary conference. So, it’s bringing together people from all different programming backgrounds, which I think is pretty neat. It’s neat to see more conferences like that.
CHUCK: Yeah. So, if you’re an Avdi fan-boy like Dave and I are, then get on that.
DAVID: Right on.
CHUCK: So, I’ll go ahead and start this off. Sometimes, I write tests without code, or code without tests. [Laughter]
CHUCK: Can I tell you how tired I am?
DAVID: I love TDD so much! Sometimes I just do the T.
CHUCK: [Laughs] Yeah.
CHUCK: And sometimes I actually will write the code and then I’ll go back a few days later and write the test because I know someone’s going to look at my code. It really depends. A lot of times, this comes out of a spike. So, it’s something that I’m not really sure even how it’s supposed to work well enough to write a test for it. And so, I’ll just wind up spiking it and then I’ll leave it. And sometimes, I literally am just being lazy. And I’m tired and I just want to write the code. And the tests feel a little bit onerous and so I just don’t do it.
DAVID: There’s a related pattern to that, when you’re actually doing TDD where, I call the pattern ‘red, green, eh’.
DAVID: Right? Where you get to red fast, you make it work, green, but it’s nasty. And then you go, “Eh.” You ship it. “I’ll refactor it later.”
CHUCK: Yeah. I have done the green refactor. I’m usually pretty good about going back and cleaning it up after I’m done. You guys never do the ‘red, green, eh’ do you?
DAVID: Never, no, not me.
AVDI: I don’t always get to green.
AVDI: Here’s one confession for you. Just a night or two ago, I commented out an entire test because for the life of me I couldn’t figure out why it wouldn’t pass on my CI server.
DAVID: [Laughs] I do that all the time.
CHUCK: Oh, I’ve done that too.
DAVID: There’s a point where you look at a test that just will not pass and you’ve been fighting with it for four hours and you can’t get your head around why it’s not passing. And finally, you just say, “You know what? This test is no longer worth it,” and you take it out and you write yourself a note to come back and understand the code maybe a little better at some point in the future.
AVDI: Yeah. And it’s a different thing when the test won’t pass and the code doesn’t work right. But you got to ask yourself what the value is if the code is working right and the test won’t pass.
AVDI: Because you’re probably just doing something wrong in the test. Or, you have some sort of state bleeding over in the test or something like that.
CHUCK: I just want that sound bite from ‘Lord of the Rings’, “You shall not pass!”
DAVID: The one I use more often in that case is “Fly, you fools!”
CHUCK: But yeah, I think the value proposition there is really valuable to think about. For me, I have this other thing going on where I’m working on something for a client. And so, I really don’t want to have to explain to them, “Oh, well I spent four hours trying to figure out how to make this test pass,” as opposed to building something that earns them money.
CHUCK: Generally, they prefer you do the earn-the-money thing. And so, if I spend a half hour on it, they’re like, “Eh, mm.” They’ll eat that and that’s fine and you made it pass and everything. But yeah. So, usually there’s that external force. If it’s on my own project and I really just think there’s some meat there that’s going to make me learn something, well there’s a value proposition for you. But otherwise yeah, I’ve done that.
AVDI: There’s a lot of code without tests lately. It’s kind of bugging me.
DAVID: [Gasps] So, I did a big spike yesterday where I was dealing with the Google Spreadsheets API, the Google Drive API. And I was just tinkering with it and just trying to see if I could make things work. And when I am tinkering in that mode, I never test. I don’t TDD. I don’t test after. I’m just fiddling around running stuff with the console, seeing if data comes back. I’ve got a web browser open looking at the spreadsheet to see if it’s getting its values changed. And it feels a lot like eighth grade where I’m just running stuff and seeing what happens.
AVDI: But you are testing. You are testing. It’s just that you’re manually testing.
CHUCK: Well, the other thing is that I like the test in the traditional sense like unit tests or acceptance tests, which a lot of people all integration tests, or actual integration tests where you’re just testing that two things talk nicely to each other. I like those in the sense that they’re repeatable. And somebody else touching the code can run it and make sure that they didn’t goof it. But if you’re tinkering, if this is stuff that isn’t going to go to production or you don’t know is going to go to production, heck yeah. [Chuckles] You don’t need to write tests for it. Just get your head around it.
CHUCK: It’s a mental exercise at that point. It’s not production code.
DAVID: Yeah. And actually, this one was I would almost say a political exercise because we needed to show that we could do it. And so, I tinkered with it. I got it running. And then I took a screenshot of it and sent it to the client. And the columns were all wrong. And I basically said, “This is napkin math. All I can do right now is I can prove to you that I can write to a spreadsheet.”
CHUCK: Yeah, that’s a little bit dangerous though, because I’ve had a few of those where it’s like, “Look, this is not for production.” And guess what
DAVID: Well fortunately, the client in question listens to this podcast. So, hi Lance.
DAVID: I really did come into this as fresh-faced as I told you I was going to.
AVDI: I actually have been pushing some of this code to production.
AVDI: I know. It’s shocking right?
CHUCK: Do you have anyone else working on it?
DAVID: I got to clean all the [inaudible] now.
AVDI: [Chuckles] It is just me. So, there’s that, although I don’t like thinking about it in those terms. I don’t like thinking this is always going to be just me. I like to assume that it’s an app that’s going to last long enough that it won’t always be me. What’s really funny is as long-time listeners of the podcast know, I despise debugger. And I’ve actually been doing a lot of development in RubyMine lately. And I’ve been doing a lot of trace, like place a breakpoint in and see if what I expect to happen is happening.
DAVID: One of us. One of us.
AVDI: Very strange. [Laughter]
AVDI: I remember doing that way back in the pre-TDD days, back in my C++ days. I haven’t done that for a long time. But I don’t know. I don’t like doing only that style of development. But occasionally, it’s an interesting change from the test-driven style. I will say, when it comes to some of this stuff, I’m pretty serious about TDD being a design discipline. I really do think for me, that’s the principle reason that I do it, which I realize is not the same as a lot of people. A lot of people think of the principle gain they get from it as being regression tests, which is fine. It’s a great way of assembling regression tests. But since I really do think of it as a design thing, I’m not as worried about growing my corpus of tests. And particularly, if I know something works, if I have experimental evidence that something works, I don’t feel super compelled to go back and write comprehensive tests for it.
AVDI: Obviously, that may well come back to bite me when I’ve got to refactor it. So, I might do just in time tests for it, right before I decide it needs to be refactored. But if I know experimentally, if I threw something together and I know experimentally that it works, well it’s already designed. And since I think of TDD as a design thing more than anything else, I’m not going to go and write some tests to design something that’s already designed. It might not be the best design, but it’s designed.
DAVID: I came across a really interesting ripple in testing thought, because I used to bang on all the time about yes, you should test your private methods. If it can possible break, you should test it. And counterargument is you should test your private methods through the public interface. And my reply to that is, “But that’s a level of indirection. That’s a level of impedance, impedance mismatch.” And you can go around the table and around the table and around the table. And I finally found out that the people that are saying never test your private methods are almost exclusively talking about regression testing, because they want to be able to change the private method and not break a fragile test framework.
DAVID: Where people like me that are talking about wanting to test private methods are people who may have no idea how they’re going to solve this problem. They’re venturing into the deep, dark jungle and they want to use their testing framework as headlights into the park right ahead. And boy, you do. You want to test private methods. You want to test every little piece, because you’re cutting gears out on the metal shop floor and you want to assert that that gear works correctly before you stick it in the machine that you don’t know is going to work at the end of the day
DAVID: But at the end of the day, and that’s how I finally spiritually reached this level of acceptance with myself of when I am driving the design forward, I will test private methods. And then at the end of the day, I rip those tests out and I throw them away.
DAVID: Because they are not only worthless as regression tests but they actually have negative value. They will cost you money if you leave them in the codebase.
AVDI: Yes. I think that’s a really important realization. And I feel very little compunction about throwing tests away.
CHUCK: Do you ever refactor them into the public interface tests?
DAVID: Sometimes. If a test, I get halfway through and I realize this isn’t just a private method, this is actually something that has to be exposed, then sure. That test will percolate up.
CHUCK: Yeah. I wonder those sometimes. Like if you are breaking a call that does a lot of things down into the smaller tests to get it written and then they have side effects, you could put those assertions into the assertions on the higher level public method and then just trash the private methods test. Does that make sense?
DAVID: It does. And I’m going to disagree with you just a little bit. I could be wrong. But to me, that feels like something fragile. It feels like now if I go in and change the way that this class modifies its internal state or has, if I modify its side effects, my test suite could break even though the class is still working correctly. However, I don’t run into that very often. And the reason why is because by about the 15th private method that you’re testing on a class, I start to realize, “I think this class is doing too much.”
DAVID: And I suddenly discover that I’ve been building a god object. I’ve been building… I’ve started with a script file, I created an application class that does everything, and lo and behold, I’ve actually caught myself on this and broken the class into extracted classes from it and actually extracted six other classes from the class that I was working with. And I’m like, “Oh. That’s why it hurts so bad. And once I extracted them, the classes that were extracted, they now have public interfaces. And those have to be tested.
DAVID: And so, those tests survived. And so, anything that was causing a side-effect or was having an intermediate result, I usually have been able to design that away by extracting it out and testing it in isolation and then testing the connection between the two.
AVDI: I think I do basically the same thing. I just come at it from a slightly different angle than you. I never test private methods as a rule. What that really just means is that I test public methods and I basically only extract private methods, with some exceptions. If I have a private method, it was extracted out of a public method that I had already tested.
AVDI: So, it’s tested by definition. But then I think I probably just extract things out earlier than you do because once I get to that point where it’s like, “Wow. I really want to reach into that private method to design something,” I’m like, “Okay. So that means it’s time to extract something out.”
AVDI: Which brings me to another interesting topic that I’ve been thinking about lately. So, I’ve already ranted a little bit about how things like extractions aren’t as easy in Ruby as they could be. I did that a couple of weeks ago, I think.
DAVID: When we were kicking Ruby’s tires? [Chuckles]
AVDI: Yeah. So, I won’t go into that too much again. But I will talk about one piece of that puzzle that I don’t know if I really talked about a lot, which is that we’ve settled on this convention, at least most people I know have settled on this convention of one class per file. One file per class. And one test file per class. And that sets the barrier to extraction a bit high, I’ve started to think.
AVDI: Because I hate creating a new test file for my classes, depending on the project. Even if I have some tools for creating that file automatically using the filename as a guide, usually it doesn’t put the right boilerplate in because this project has slightly different test boilerplate or something. And so, there’s usually some boilerplate I have to rewrite or some test helpers that I have to copy over or something, stuff like that. There’s also just the fact that a lot of times, I’ll extract out an object. And then I’ll go through three quick revisions of my name for it, because it takes me a little while to settle on a name for it that I like. And so, I’m constantly renaming the file. I’m renaming the file it’s in. I’m renaming the test file.
DAVID: You’re renaming the variable that you called it in the other tests.
AVDI: Yeah. And this is starting to make, I think it’s putting negative pressure on my writing separate tests for that separate object because I’m just like, “Screw it. I’m going to put this in the debugger and see, make sure it’s working right.”
CHUCK: You could just do what my mom did and that was just say all of the names so one of us would come home. “Chuck, Steve, Bert, [inaudible]. Chuck. Underscore spec.”
DAVID: All the things underscore_spec.rb.
CHUCK: I was thinking that too. Avdi just has one file. It’s the spec helper and it just tests everything.
AVDI: I watched an interesting talk recently that questioned the dogma of one test file per class. And it struck a chord with me because of this. I don’t know. It’s extra work. It’s extra noise.
DAVID: So, what direction would you move?
AVDI: Well, what I’ve just started doing, and don’t everybody all go do this because like I said I just started doing it, but I’ve started just creating a test file for a feature that I’m working on and actually mixing together in that file higher level and lower level tests. And some tests reference multiple objects. Some tests reference just a few. But it’s not like this is the test file for this class. It’s this is the test file for listing episodes. And there some acceptance tests in there. And I’m probably going to be throwing some tests against the data mappers in there as well and probably some other ones.
CHUCK: Yeah, I complete agree with DHH. I think the lower level tests just aren’t as helpful.
DAVID: I kind of like that idea.
AVDI: I do start with higher level tests pretty much invariably. But yeah, it just keeps me in one test file while I’m working on one feature.
CHUCK: Well, I think it’s interesting because then you’re dealing with the series of concerns. And I guess the interesting thing for me about this is that I find that there’s a disconnect sometimes between the feature that the client is thinking about. Or I’m thinking about if it’s my app. And then getting down into the concerns that the class has.
CHUCK: And so, by grouping everything together by, this is user management or user sign in if you want to get more specific into a feature or something, it could be an agile story, then I can think about the things that really matter to that. And I may be testing my classes across multiple feature specs. But in the end, I know that they all interact the way I expect them to because the feature spec passed.
DAVID: I like this notion that when we write a class file in Ruby, this is what I do and I see this a lot, you start at the top of the class file with the big meta stuff like modules that you’re including, Active Record DSL methods like belongs_to, has_many, validate, that kind of stuff. And then you have your initializer, your constructor. And then you have class methods. And then you have your public API. And then you have your private API. And you just move from this really high-level at the top of the class. By the time you’re at the bottom of the class file you’re dealing with very tiny things. Now clearly, I’m describing classes that are not written to conform to Sandi Metz’s idea of 25 lines of code for your class file, right? But if you’ve got this really big class you have almost this gradient slope of scope from broad to narrow. And what you’re describing Avdi sounds a lot like that. You do put the broad stuff at the top, right? Because otherwise, we can’t be friends.
AVDI: Yeah. I’ve got an acceptance test at the top. And one of the things that I like about RSpec is that I can do things like I can tag different. I tag one describe block with VCR true, db true, email true, so turning on all the various test helpers and features that are needed for that level of test. And then I can have other ones in the same file that don’t use all those features.
CHUCK: So, this leads to another confession. Dave mentioned Sandi Metz’s rule. I write a lot of long methods.
CHUCK: And so, I’m all for extracting a method. If I’m looping over a collection of something and I’m going to call the same thing against all of them, then yeah I’ll extract that into a method. But if it’s a list of files that I need to deal with or something like that and I have to do something slightly different with each one, I’ll put ten statements in there for all ten files. And I think doing an array or a hash.each and then putting the details in there like that, that’s just gross to me. I don’t like it because it just, I don’t know. It just doesn’t feel natural to me.
DAVID: It means your method name is awful.
CHUCK: So, if it’s extract file to whatever, then I just extract file to whatever and then I call it with file one and then I call it again with file two and call it again with file three, and then I’m okay with that.
AVDI: When I’m writing code that’s very exploratory, like spikey sort of code, and I’m testing it manually, a lot of times it makes the most sense to basically just lay it out as a script. Martin Fowler might call it a transaction script if you want to get fancy about it. But it might literally be a script, just one file with a series of statements in it. And the most logical thing to do with that once it works, if you’re not going to throw it away and write it over again, is to just throw that into a method and there you go. And I’ve been thinking a lot about rules for doing that well, because I’ve been finding myself doing that. And one of them is log a lot. So, when I’m developing that way, very exploratory, just running basically a transaction script over and over to make sure, and developing it one line at a time, I make the request and then check. Make sure that the request is a 200, then I take the body and then I parse the body. And then I try to extract something out of the body and so on. I’ll have a line a time and each time I’ll just see what the end product is and go forward if the end product came out right, just manually running it over again. And some of the rules that I’ve been coming up with for that are: log a lot, log everything so that I have a good idea of what’s happening at each step of the way, since there are a lot of steps, and put lots of assertions in. So, after each step, once I get to the point where it’s, “Okay, it’s doing something that I’m eyeballing it and it looks right,” now put in a little assertion at that point in the script that makes sure that I don’t then break it with a future change. And that basically serves the same purpose as putting a test in.
AVDI: But it’s straight in the code, just putting that expectation straight in the code.
DAVID: Nice. There is an old bit of advice from I think ‘Code Complete’. It might have been ‘Writing Solid Code’. Both of the books came out in the 90s. And one of them has a really good piece of advice where in C and C++ you can do, if def debug. And if you’re compiling the debug version of your code, this block of code will execute. And there was a pattern that a lot of programmers fell into where they would write the method to run one way in production and then they would write debug code that did something different to try and explore. And so, this book came out and basically said don’t do that. Always, always, always in production and debug, always execute through all of the production code. Then if you stick debug code in, all the debug code should do is crosscheck what the production code did so that when you take the debug code away, you don’t have a broken program. You haven’t actually removed something that, oops, you actually needed that in production.
DAVID: And that sounds a lot like that. This long script and I’m not going to test it. But yeah, I’m going to jam an assertion right here in the middle of it so that if I change my assumptions, there’s at least one fencepost that I will hit my, crack my knee on as I go past it.
AVDI: Exactly, yeah.
DAVID: So, I have a weird, weird confession. And I don’t know if this is a proper confession because I’m proud of it. Is that bad? But you were talking about logging. I am obsessive about making logs look pretty, to the point of I will drive my pairs crazy with, “Oh hang on. Hang on. Let me just align this text,” or, “Let me draw a box around this.” I don’t do the ASCII art so much anymore. But things like lining up all of the log statements coming out of this function must begin with the method name and a colon and a space and then the message. And I say I’m proud of this one because my job has entailed processing log files so often that it bothers the crud out of me when I see a log file where any object in the system can just blab anything it wants to, to the console or to standard error without any consideration for… the programmer who wrote it was testing something and they knew where that message was coming from and then they shipped it. And now, I’m running the program and I just see this message that says, “Got value 43,” and I want to take a hammer and just go smash the knuckles of whoever wrote that code. Is that bad?
CHUCK: Dave smash!
DAVID: Dave smash your knuckles. [Chuckles]
CHUCK: I think we all have our quirks that way, with different things. If you’re spending an extra two minutes to get it wrangled around the right way out of the hour or so that you spent writing the code, I don’t think it matters.
CHUCK: And it can pay off later on.
DAVID: What about 15 minutes? [Chuckles] That’s about where my level of obsession comes in, is I will sometimes spend 10, 15 minutes tweaking. And my partner will say, “Dude, we need to move on,” and I’m like, “No, no, no, no. We are leaving this for posterity, so it needs to be intuitive.” And the part where this becomes a confession is sometimes it’s not for posterity. Sometimes it’s a debugging message. And I will just habitually start cleaning it up and making it pretty. And that’s a habit I have to try and break myself of sometimes.
CHUCK: Yeah. If you’re spending 15 minutes out of the hour, is that one quarter of the [value]?
DAVID: No, no. 15 minutes once a day. I spend 15 minutes out of the day, 15 minutes out of every other day. Yeah, I’m not spending 25% of my time futzing with print statements.
CHUCK: Yeah, that’s the thing. For me, it comes back to the value. Is the amount of time you’re spending worth the value that you’re adding?
DAVID: Yeah, yeah. And I will say one other thing. If you write stuff to standard error or to standard out in the middle of my RSpec run and you mess up my pretty green dots with your stupid trash…
DAVID: I will find you.
AVDI: Yeah. Those dots are our fix, those nice clean dots. Those are our fix. [Chuckles] Even though I have sometimes distressingly low test coverage, so it’s not really as good a fix as it should be.
DAVID: Well, three dots is not as nice a fix as 2,000 dots. It really isn’t.
DAVID: A whole screen of green dots is just [sighs] oh, yeah.
AVDI: See the thing is it’s really to add dots. I could write you a website that will just output dots if you want.
CHUCK: Okay, I could totally see Dave just staring at this website. And then his eyes roll up into the back of his head and he’s just like…
DAVID: [Laughs] I’m going to go buy myunittestspass.com and all it is, is just green dots.
CHUCK: For when you just can’t take it anymore.
AVDI: You need that little endorphin rush.
DAVID: That’s right, redgreenawwyeah.com. [Chuckles]
CHUCK: I have another confession. And that is that sometimes I have long, long time running tests, like the tests run longer than Dave’s seven seconds.
CHUCK: Sometimes they run a few minutes. Usually it’s integration tests and usually then I’ll write some spec, like rake spec not integrations or whatever so that I can get that fixed. But on occasion, they’re just slow. And I just let them run anyway.
DAVID: I’ve taken to, I’m working on a Resque project right now where it’s Rails and it’s slow. And the test suite was, oh three to five minutes. And I’ve gotten it down to two minutes. But it’s still way too slow. And one of the things that I’ve done is I’ve taken to writing in my spec file, I will write context with data setup, tag slow = true. And then all of the stuff that uses fixtures and factories and touches the database, these are model tests, they’re unit tests, and to my thinking they should run very fast. And if you touch a factory, loading a factory is slower than just creating an object and saving it. And that is much slower than just creating an object in memory and not saving it to the database. And you end up losing 40, 50 milliseconds every time you try and write one of these objects. And if you’re testing an object that because of the way Rails, the classes can find each other very easily, you have an employee class that can talk to its timecard class that can talk to its task unit class that can talk to the job class that can talk to the supervisor class, which can talk to the employee class again. So, all of a sudden when you say context with data setup and then you have a string of let statements that create these factory objects, that factory takes 500 milliseconds to run. And that’s enough that you will get wrapped in a context that says, slow. And I like it when you print out the documentation, that you get this thing that says testing this class and here’s all the stuff. And then you get this with data setup. And then you have stuff like it loads the correct file and it does da, da, da, da, da, and that sort of thing. That took me way too long to make my point didn’t it? Sorry. Let’s just tag the last two minutes with ‘slow’.
DAVID: Yeah. That would be awesome if you could play back this podcast with tags not slow and it would just cut out all of my stories.
DAVID: Actually, that would be awful. Nobody please invent that.
AVDI: My classes are getting long too. I got to confess that. I’m not sure what to do about it except extract them out. But here’s a confession for you: I don’t always know where to draw the lines.
CHUCK: It’s hard. Sometimes you have a pain and it’s very apparent. I got to extract something out of here. It’s doing two jobs. These are two jobs, so I’m going to extract one. But sometimes it just feels long and it’s a little bit hard to keep it all in your head, but there’s not really a clear way to do it or even if you should.
DAVID: Yeah. We had Sandi Metz on to talk about her book. And I remember saying that I was having trouble with some aspect of my design. And Sandi, she didn’t blink. She was just like, “You’re not breaking things down enough.” And I’m like, “But I break them down, I break them down, I break them down, and now I’ve got all these pieces everywhere,” and da, da, da. And I was explaining this problem and Sandi just, we do this as an audio call, we don’t have video, but I could in my mind I could see her smiling. And the answer was, “You’re not breaking things down enough.” Break them down one or two or seven more layers deep. And all of a sudden it will just start work, or start working. And she’s kind of right. But Avdi, I think you’ve hit it on the head. Sometimes, I don’t know where to draw that line. You wrote a, what did you call it, an Active Record Fable?
AVDI: Yeah. I just wrote a blog post talking about, well long story, basically how there are actually a whole lot of patterns that go into what we now refer to as the Active Record library. And it encompasses, altogether it encompasses many, many more things than Active Record.
DAVID: Right. I loved the fact that you pointed out that you had peeled off, you were writing in your own code, and you were peeling off all these patterns. And you had these two patterns conflated. And how did you put it? You were almost literally writing the code with the keyboard in one hand and…
AVDI: Yeah, yeah. Yeah, yeah.
DAVID: And a pattern for the other, right?
AVDI: On an app I’m working on, just for my own edification, my own curiosity, I’ve been doing it without any kind of preexisting ORM framework. I’ve just been writing what I need as I need it, as little as I need to do the kind of object relational mapping that I actually need in my application. And I’ve been trying to be very inspired by principles about enterprise application architecture. And so, I’ve been, like Dave said, I’ve been writing with keyboard in one hand and the book in the other. Every time I need to add a little bit extra, I’m like, “Okay, let me look up the appropriate pattern from this book and see if I can implement it in the correct way and see what that buys me,” because often the decisions in those patterns are subtle but important. And even so, the other day, I was looking at my code. I was trying to reason about my code. It was hurting my head. I was like, “How did I let this very small-scale lightweight process result in code that I cannot reason through?” And it suddenly occurred to me, holy crap. I managed to jam a data mapper and a repository together without even thinking about it. I have a class here which totally mixes together the concerns of mapping record columns to domain attributes and the concern of selecting subsets of rows and iterating over them. And those are very different. They both have a lot of code that needs to be dedicated to them. And when you put it all together, you get 200 plus lines of code that is, well it’s conflated.
CHUCK: Sounds like your confession is, I use the Active Record library.
DAVID: No. So, there’s actually a big difference between Active Record the library and the Active Record pattern.
CHUCK: Yeah, I understand that. What I’m saying is that the things that he’s saying that have all these extra things going on, Active Record does a lot of that stuff.
AVDI: I guess, yeah. It’s surprisingly easy to arrive at mixed concerns. I guess that much I’m saying.
AVDI: And it’s surprisingly difficult to tease them apart sometimes and difficult to know sometimes whether it’s worth the time to tease them apart or work around the problems in my design and work on getting another feature out. And that’s a real toughy.
AVDI: And it’s not even, I’m not even sure I can call that refactoring now. Granted, I’m still only talking about a 200-line class. But I’m not sure I can legitimately call it, fixing that, refactoring, because I think it might wind up being closer to a rewrite. A small rewrite, but a bit of a rewrite. Object design is hard.
DAVID: Objects are hard.
DAVID: Okay, so I have a confession to throw out here. I talked about this with James in private last week. And I didn’t come to a good resolution on this. I have a confession. And this is here’s something that I don’t know really how to test. I tweeted a joke about how I believe that the bane of all computer science is letting objects talk to each other. And that of course was a joke. But it was inspired by a genuine thought, which is I seriously wonder if the bane of all the programming that we do, by ‘we’ I mean all of you our wonderful listeners, most of us are web programmers. Some of us are doing backend programming. Most of us are doing object-oriented programming. Here is what I think is an implicit problem. We’ve been soaking in it for years. And I’m just now starting to realize that it’s been hurting this entire time. And that is objects that know how to find each other. Objects that know each other by name is a real problem. And here’s what happened. I’m just going to confess this, that I still don’t have a good solution for this. I had an object that was a has_many, belongs_to relationship with another object. And I removed the belongs_to and my tests broke and that was great. I removed the has_many and nothing broke in my code. And James had the right answer, which was, well obviously there’s a missing test. Something in your test suite isn’t executing that has_many relationship. And the realization, if you’re doing TDD and you’ve got a person, you have a people table and you have an address table. And a person has many addresses, say. When you’re doing TDD, or we’ll step back. You want to relate. You’ve got person which is basically an empty class and you’ve got addresses which is basically an empty class. And you want to say address belongs to person, person has many addresses. How do you test that, just that, without duplicating the has_many? Every way I can think to test this is to sit down and write a test that basically says it should have many addresses, person should have many addresses. And that to me feels like I’m duplicating, I’m going to end up duplicating my entity relationship diagram, my ERD, in my spec suite.
DAVID: It’s pure duplication. I can’t see any reason for that to be there. And James had the argument that…
AVDI: That’s all it does.
DAVID: Yeah, basically. [Chuckles]
DAVID: It’s like when I first got into private methods. I found out, I got into some furious arguments with Bryan Lyles or with Pat Maddox and with a couple of other people. And then at the end of our argument, we all peeked at each other’s poker hand and we all confessed, I don’t write private methods. Those of us who were in favor or against, we just don’t write private methods, right? I have started writing private methods since talking with Sandi because now I actually see a reason to have them and why classes should be able to protect some of their behavior from the outside world. And then this is relevant to my current point, which is that if the only way to test that person has many addresses is through some code that exercises a person and talks to that person, in a Rails app the only code that’s going to test that is a view. And we never test our views right?
CHUCK: I’ll confess to that.
DAVID: I’ll confess to that. I test every other layer. I test my models really hard. And then I will sometimes integration test suites of models. Controllers, basically the only thing I will test there is if I have a really complicated bit of logic. But at the view level, I don’t test. And that’s what bit me, was the website broke because we had a view that was rendering all of these people basically. And it was trying to render their addresses underneath them. And nothing was exercising that view. So, that’s my confession, is I’m just a bad programmer.
DAVID: Yeah. So, my point to all this is that there’s this problem in computer science that the relationships between objects, this ERD that we draw out, this map of the way the objects hang together, is really hard to test.
DAVID: We can draw all of our little boxes on a napkin and we can write tests to test those boxes. But when you draw a line from one box to another, where do you write the test to test that line?
AVDI: Right. It’s difficult to test structure.
AVDI: And I like to think that most of the time it doesn’t matter. Maybe most of the time that’s a good thing because if there’s a point to object-oriented design at all it’s that it’s connecting black boxes together and composing things out of the black boxes. But yeah, the way you connect them together does have the net result of your program working or not.
DAVID: Yeah. This is business logic now, right?
DAVID: We’re no longer testing the mechanics. We’re now testing, in order to, and maybe that’s what it is, is you have to all the way up to true business logic and say in order to bill a customer I have to be able to find her address so that I can send her a bill. And given a person, I should be able to find their address. And yeah, that’s starting to sound like an integration or acceptance test, doesn’t it?
ADVI: Yeah. Yeah, maybe an integration test. I guess that is where some of my integration tests come from, is testing those kinds of relationships.
DAVID: So, there’s a confession that I don’t really have time to get into because I don’t, I’m so eyeball-deep into it that I don’t know that I understand it well enough. But somebody wrote a famous blog post a while back called ‘Integration Tests Are a Scam’. And I was offended by the title. I just thought it was so obnoxious that I’m not even going to read the blog post. And then some people that I really respect came back later and said, “No, you really ought to go read this because there are some pretty good points in here.” And I went and read it. I’m like, “Oh, this a lot like the TDD debate with DHH and Kent Beck where there are some people that have got some pretty strongly polarized views here.” I’m now circling back to this with a much more open mind about how would this affect, if integration tests are a scam, how does this affect my design? And I’ve been pushing really hard to do all of my unit testing. And I freely admit that unit test not equal integration test. But I’ve been pushing really hard to follow Sandi’s rules for testing. If it’s a public message, you should be able to send it, send the object that message. If it’s a getter, you should be able to get it. And if it sends a message, you should inject that dependency and assert the message was sent. But you should not let it send that message out. You should test the class completely in isolation.. And I was really nervous going into that about being able to test, trust that my objects all hang together correctly. And that’s where I’m at right now, is trying to trust that my unit tests all together work well enough to catch this. And as I said a couple of days ago, it wasn’t enough. I removed the belongs_to and a test broke. But I removed the has_many and nothing changed.
AVDI: Yeah. And I think with the tools that we have, it’s all too easy to have tests that don’t really check that they all hang together. Things are moving forward slowly. If you’re going to be using mocks and stubs, it’s very exciting that RSpec 3 ships with verified doubles, which will complain if you try to send them a message that the thing they’re a double of doesn’t respond to. DAVID: Yeah, yeah. Like “stub versus stub!”?
AVDI: Well, they don’t use those terms.
DAVID: Oh, okay.
AVDI: Yeah, basically there are new class double and instance doubles and one other kind of double, object doubles. And you basically, you’re giving the double an example of what it’s standing in for, whether a class or an instance. And you’re saying, “This is what you’re standing in for. So, if my test sends you a message that doesn’t look like something that your real object could respond to either in terms of the message name or the arity, then please explode because I am writing tests that are lies.”
DAVID: Please explode because I am writing tests that are lies.
AVDI: Maybe lies.
DAVID: I am so happy.
DAVID: I am so happy that that is a sentence that just got said. [Chuckles]
AVDI: Punish me for my sins, is what [it is].
DAVID: Yes, yes.
AVDI: And yeah, I guess I mostly at this point rely on the fact that I also write higher level tests. If nothing else, I try to write some acceptance tests. And if not everything hangs together, then that will usually make an acceptance test blow up, but not always.
CHUCK: Well, I’ve got to take off pretty soon. So, I’m going to push us toward the picks. I do have one final confession and that is that I like Haml. But I don’t think you guys have anything to say about that. So, [inaudible].
DAVID: [Chuckles] I have a confession! I have a confession!
CHUCK: Anyway, let’s go ahead and do the picks. Avdi, do you want to start us with picks?
AVDI: Oh, sure. I have, well I know I have one tech pick which is I started playing with CircleCI the other day. I wanted to set up, finally get around to setting up some continuous integration and continuous deployment for a project of mine. And actually, thanks to a thread on Parley, I found out what some of the currently popular alternatives are. And one of the ones that came up several times was CircleCI so I checked them out. And it was really, I’ve been really, really impressed so far. The setup is really great. It does a lot of nice sniffing to determine what sort of project you have. My project’s not completely normal because it’s not just like a standard Rails application. It’s a Sinatra app and most stuff in it is hand-rolled. But for the most part, the auto-configuration worked pretty well. Everything was easy to configure. The speed is good. The speed is great, actually. The live feedback as the test progresses, the setup and the test progressives, is really slick. And it has some interesting features that I’m not sure if other providers have, like the SSH mode where basically you can turn SSH mode on and it’ll run through the tests. And then I think if there’s a failure, maybe regardless, I’m not sure, I think if there’s a failure it’ll give you an SSH command, a preconfigured SSH command that you can punch in to SSH into the testing box that test was run on and poke around. And figure out, if the tests are passing locally but failing on the CI server you can poke around and figure out exactly what’s going on because you can actually run commands and stuff on the CI server. Yeah, so far I’ve been really impressed by that. And it also has some nice integration with Heroku and stuff. So, you can push once the tests pass. Also, their support has been quite responsive. I don’t think I have anything else.
CHUCK: Alright, David what are your picks?
DAVID: I have two weird picks today that are going to involve you, the listener, really bending your brain. And I would love some feedback from you on these. The first one is all for you to think about really hard. It’s the Just-World Hypothesis or the Just-World Fallacy. You can look for these on Wikipedia. And essentially, this is a cognitive bias that we all have. And even if you know you have it, you are still subject to it. The Just-World Hypothesis, you know what, we don’t have 20 minutes for me to go into the whole long thing. I’ll give you the super short version. It is that we all, at some level, believe that the universe is ordered and is a meritocracy. And that if you do good things, then good things should happen to you. And this leads to a really dark bias towards what happens when something really bad happens to someone. We are actually biased to assume that they must have been a bad person. And it is a fascinating fallacy. It is well worth the read and it’s worth thinking about when you think unkindly about the programmer who made the awful mess that you are cleaning up in the code.
DAVID: Because even if you know about the Just-World Hypothesis, you are very likely, and this is serious stuff. It is funny, but it also goes all the way down to the very serious level stuff like women who get raped are far more likely to be judged as deserving of the abuse that they received because of the Just-World Hypothesis. Even by people who know about the Just-World Hypothesis, they are that much more likely to make that kind of judgment. This is serious, heavy, heavy-duty philosophy stuff. Please take a look at it. It’s really, really worth contemplating. My second pick is a combination pick and request for a pick. I’m going to pick something that is, I want to say it’s a timeless classic except that it has to do with computer graphics. And you can’t take anything in computer graphics and say it’s a timeless classic. So, I’m going to give you my pick. And then I’m going to ask all of the readers to write back to @RubyRogues on Twitter. Sign up for Parley and post your ideas there. I’ll start a topic there. And I’ll put a note in my file to start this topic next Wednesday when this show airs. My pick is the Foley and van Dam book. This is back from the era, I don’t know if we still do this, if the kids do this nowadays. But back in my day, when there was a really, really good textbook, you referred to it by the names of the authors. And this book was written by Foley and van Dam and Feiner and Hughes. But who could be bothered to remember more than two names? The Foley and van Dam book is a book called ‘Computer Graphics: Principles and Practice’ and it was written in the mid-90s. So, this was before the NVidia GeForce came out. This was before 3D hardware transformations. This was before shaders. This was before [inaudible], filtering. This was before any hardware existed. This was written back in the day when you had control over every single pixel on your screen and you wanted to build a raytracer. And this topic came up when I was talking about writing a Phong shading raytracer in a single tweet. And James and I, I don’t know if I picked that for the show. I’ll pick that as well. James and I spent about an hour on YouTube, 45 minutes on YouTube, talking about how to write a raytracer in a single tweet and make it work. And it was a fun discussion. And all of the tricks that I used in that tweet came out of this book. The reason why this is a reverse pick is that it is now the 21st century and we have things like 3D hardware pipelines and lighting models and shaders and all that fun vertex manipulation goo ga stuff. And I am curious to know if there is a modern equivalent, all in one textbook. It can be a college graduate level textbook. I’m okay with that. I’m not afraid of that. But I would love to hear people’s suggestions for what is the best book that will take you from, “Here’s how to set the color on a pixel,” all the way through, “Here’s how to take a convex hull in geometry and tessellate it and shove it down the OpenGL pipeline and get back a rendered scene of exactly what you want to see.” So, that’s my pick, is a request for something new and updated and modern in computer graphics. And those are my picks.
CHUCK: Awesome. I’ve got a couple of picks here. So, the first pick I have is a game I’ve playing the last little while. I tend to, what I’ll do is I’ll work for about two hours and then I’ll stop the time clock and I’ll go play a game of Hearthstone because it takes about 10 to 20 minutes for me to play it. So, it’s a computer game but you play cards like Magic or Pokémon. And all of the different characters and stuff are basically classes of creatures or heroes or whatever, from Warcraft. And anyway, it’s a pretty fun game. I’ve really been enjoying it. And so, I’ll pick that. I’ve also been reading a couple of books here. I just want to make sure I haven’t picked them before. The first one, I’m pretty sure I haven’t. It’s called ‘Winning’ by Jack Welch. And I’m really enjoying it. It’s just been a terrific book. And he was the CEO of GE. And so, he talks a lot about the strategies and tactics and things that he used, and some of the principles you need to live by or act by if you’re running a company. And I’ve just been really enjoying it and the thought processes behind it. The other one that I’ve been reading or I finished last week is ‘EntreLeadership’ by Dave Ramsey. And it’s another one. He talks more, I think it applies to smaller companies a little bit better than Jack Welch’s book. But still, there’s a lot of stuff there that is really terrific. And he talks about how he built up his team and how they run their company, and the values and things that they have there. And again, just a terrific book. So, if you’re looking at building a company or becoming a better manager or things like that, or if you are in a startup and you’re wondering about getting acquired and things like that, both of these books are excellent resources. So, I’m going to pick both of those. And that’s all I got. So, we’ll wrap up the show. I don’t remember what our new book club book is.
AVDI: ‘Refactoring: Ruby Edition’.
CHUCK: That’s right. So, we will be reading that. I don’t think we have a scheduled date yet.
DAVID: And Chuck has a confession about whether or not he’s started reading the book club book.
CHUCK: [Laughs] That would be a no. I haven’t started reading the book club book because I’m a terrible person. But anyway, so we’ll wrap up the show. Thank you all for listening. We will catch you all next week. Go leave us a review on iTunes. We would really appreciate that. And that’s it.
[This episode is sponsored by Codeship. Codeship is a hosted continuous deployment service that just works. Set up continuous integration in a few steps and automatically deploy when all your tests have passed. Codeship has great support for a lot of languages and test frameworks. It integrates with GitHub and Bitbucket and lets you deploy cloud services like Heroku, AWS, Nodejitsu, Google App Engine, or your own servers. Start with their free plan. Setup only takes three minutes. Codeship, continuous deployment made simple.]
[A special thanks to Honeybadger.io for sponsoring Ruby Rogues. They do exception monitoring, uptime, and performance metrics and are an active part of the Ruby community.]
[Hosting and bandwidth provided by the Blue Box Group. Check them out at Bluebox.net.]
[Bandwidth for this segment is provided by CacheFly, the world’s fastest CDN. Deliver your content fast with CacheFly. Visit CacheFly.com to learn more.] [Would you like to join a conversation with the Rogues and their guests? Want to support the show? We have a forum that allows you to join the conversation and support the show at the same time. You can sign up at RubyRogues.com/Parley.]