AVDI:
Yeah, it’s RailsConf season. So, everybody’s off doing whatever they do at RailsConf.
[This episode is sponsored by Hired.com. Every week on Hired, they run an auction where over a thousand tech companies in San Francisco, New York, and L.A. bid on Ruby developers, providing them with salary and equity upfront. The average Ruby developer gets an average of 5 to 15 introductory offers and an average salary offer of $130,000 a year. Users can either accept an offer and go right into interviewing with the company or deny them without any continuing obligations. It’s totally free for users. And when you’re hired, they also give you a $2,000 signing bonus as a thank you for using them. But if you use the Ruby Rogues link, you’ll get a $4,000 bonus instead. Finally, if you’re not looking for a job and know someone who is, you can refer them to Hired and get a $1,337 bonus if they accept a job. Go sign up at Hired.com/RubyRogues.]
[This episode is sponsored by Codeship.com. Don’t you wish you could simply deploy your code every time your tests pass? Wouldn’t it be nice if it were tied into a nice continuous integration system? That’s Codeship. They run your code. If all your tests pass, they deploy your code automatically. For fuss-free continuous delivery, check them out at Codeship.com, continuous delivery made simple.]
[Snap is a hosted CI and continuous delivery that is simple and intuitive. Snap’s deployment pipelines deliver fast feedback and can push healthy builds to multiple environments automatically or on demand. Snap integrates deeply with GitHub and has great support for different languages, data stores, and testing frameworks. Snap deploys your application to cloud services like Heroku, Digital Ocean, AWS, and many more. Try Snap for free. Sign up at SnapCI.com/RubyRogues.]
[This episode is sponsored by DigitalOcean. DigitalOcean is the provider I use to host all of my creations. All the shows are hosted there along with any other projects I come up with. Their user interface is simple and easy to use. Their support is excellent, and their VPS’s are backed on Solid State Drives and are fast and responsive. Check them out at DigitalOcean.com. If you use the code RubyRogues you’ll get a $10 credit.]
DAVID:
Hello and welcome to episode 205 of the Ruby Rogues. Today on the Rogues panel we have Avdi Grimm.
AVDI:
Hello from Tennessee.
DAVID:
And we have myself and I’m hosting. So, do your own intro joke. Our guest today is… we have a very small panel. This is going to be a very intimate conversation. Our special guest today is Piotr Solnica. Do you want to introduce yourself and give the listeners a little bit of a background about yourself?
PIOTR:
Yeah, sure. So, I’m a software consultant from Poland. I’ve been programming Ruby on Rails for the last eight years. Sometimes a little bit of JavaScript as well. I’ve been doing some open source work too. These days I’m mostly busy with a project called Ruby Object Mapper, which as I call it is a data mapping and persistence toolkit for Ruby. Yeah, I also created a project called Virtus which was an [extraction] of property API from the data mapper project.
DAVID:
Awesome, okay. So, we’ve dragged you before the Rogues tribunal today to defend your blogpost, which is actually kind of awesome. I’ve just skimmed over it. But you wrote a post last month I guess called ‘8 Things I Learned During 8 Years of Ruby and Rails.' And rather than just go through your post point by point, well I guess we can do that or maybe we could do both, but if you could sum up, what is the most important thing you’ve learned in the last eight years of Ruby and Rails? Is there any one… I realize you don’t want to eliminate, say this thing and not these other things. But what’s the happiest or strongest or most important thing that you’ve come away with so far?
PIOTR:
Yeah, I think the first one, which is test-driven development. That would be the most important thing I’ve learned that has the biggest impact on the way I work. It’s not strictly related to Ruby and Rails of course. But I learned it while writing Ruby and Rails. So yeah, I think that’s the most important thing. And I guess the second point is also pretty important for me.
DAVID:
Now your second point is heretical. There’s a world outside of Rails.
PIOTR:
Yeah.
DAVID:
What?
PIOTR:
[Chuckles] Yeah, that’s true. Yeah, I spent the first I guess couple of years mostly doing Rails exclusively without writing any Ruby code outside of Rails. And I was also surrounded by Rails related gems, so everything I used was related to Rails in one way or the other. And at some point I realized that whoa, there are so many nice things out there. But for some reason, throughout all those years Rails has been the most popular thing in our community, which is kind of a shame because there are so many things outside of Rails. Like so many people building really, really interesting libraries and frameworks. And I just think it’s worth it to explore this world because there’s massive potential there as well. So for me, this is something that motivates me a lot, to work on things that are not strictly related to Rails or are just completely not related to Rails.
DAVID:
Yeah. No, that totally makes sense. I came to Ruby, oh gosh, almost ten years ago before Rails had really caught on. And so, I sometimes, people say there’s a world outside of Rails. There’s Padrino. There’s Sinatra. If you go back far enough, there’s Camping. There’s all these other… there’s Rack. And I like to smile and point out to people that in Ruby there’s a world outside of web.
You can do other programming besides web programming in Ruby.
PIOTR:
Yeah, that’s so true. For me, the way I see it at least, is that Ruby has been mostly used as a language to build web apps, because of Rails. But it doesn’t change the fact that you can use Ruby for lots of different things. So, if we keep focusing on Rails and web related solutions, then it may even damage Ruby as a language, because it will not evolve as fast as it could.
DAVID:
Yeah. Or it will evolve purely in the web direction, right?
PIOTR:
Yeah, yeah, yeah.
DAVID:
It might start making tradeoffs, giving up generalized programming abilities for web specific specialization.
AVDI:
What does your current library stack look like when you have a choice?
PIOTR:
When I have a choice. That’s completely weird [laughs] because these days I’m really experimenting. So, if I had a choice I would at least try to build my own stack almost from the ground up. I will probably just use Rack for a good start.
DAVID:
Wow.
PIOTR:
And build something on top of it. Although literally last week, I started building a small website. And I started using Roda framework, which started as a fork of Cuba which is like Sinatra but I think faster and smaller, even smaller. And what I like about Roda is that it’s similar, when it comes to its philosophy, it’s similar to Ruby Object Mapper, because for example it’s immutable. Once you create an application instance, you can freeze it and it will never change. It’s also… it was built by a creator of Sequel library which also is used by Ruby Object Mapper. So, I’m a big fan of this library.
So basically, when I looked at Roda I was like, “Okay, this looks really nice.” So, I decided to give it a go. And so far, so good. It’s really small, really fast, and basically every additional functionality is a plugin. So, Roda essentially is a router. Everything else is a plugin. So, I could easily add tiny little features like I don’t know, view rendering, or some additional stuff like assets for example. So, I really like that it was so small but I could create my own little stack from it. And I had Ruby Object Mapper to handle the database. So, for web stuff I would probably choose Roda these days, although I just started experimenting with it. It just, it looks good. It just looks good.
DAVID:
Jeremy Evans talked about Roda at MountainWest RubyConf this year. And one of the things that blew my mind is that it uses a, I’m going to get this wrong, it uses, I want to say a binary tree. That might be wrong. What I do know is that it uses a tree to build out all of the routes, which means that as you add more and more layers to your routes and your routes get bigger and bigger and bigger, we’re talking applications with 10,000 routes to them. So, this is a routes file that’s completely out of control. Roda will slow down logarithmically over time. So basically, 10,000 routes is only twice as slow as a thousand routes, or some fraction. It’s far less than linear, where Sinatra will go linear for a while and then it’ll start to go exponential. And Rails would do the same thing.
And there was a great question from the audience which was like, “What kind of apps are you building that have 10,000 or 100,000 routes in them?” And Jeremy just shrugged and says, “It happens.”
PIOTR:
[Chuckles]
DAVID:
And when I look at Roda, I like the DSL to it. I like, you can just r.on “hello” do
And that’s a route. I don’t know. It just, the nested tree format of it looks really pretty to me.
PIOTR:
Yeah, I like it too. I like it mostly because you just see what happens. You don’t have to jump between some routing definition in one file and then go to the controller or whatever it is that handles the request to see what will happen. You just see the routing tree. It’s right in front of you.
And you see exactly what gets invoked. I think that’s nice. It’s explicit and it’s in just one place.
DAVID:
Yeah. You know, we skipped over TDD a little bit. We glossed over it. There are a lot of people that love TDD, obviously. And I’m definitely one of them. And there’s a fairly large camp of people that, they do ‘TDD, but’. And I’m kind of in this camp, that we do ‘TDD, but’. And you actually say in your blogpost, you say that you do it even when you’re spiking. You say, “I do it even when I’m prototyping.” And those are the cases when I often will not TDD, because TDD the way I do it slows me down. If I don’t know the interface of the thing that I’m building, TDD will slow me down.
If I know the interface, if know the architecture and design of what I want to build, then TDD is like shoring up a boardwalk as I move out over the ocean. It makes you move very fast, very stable, very secure. But if I don’t know where I’m going, TDD gets in my way. And I’m always open to the possibility that this is because I’m doing it wrong, not because TDD is a poor candidate for spiking and prototyping. So, my question for you then is, talk me through how TDD helps you with spiking and prototyping.
PIOTR:
Yeah, it’s actually pretty simple. The first thing that helped me a lot is that I don’t have to focus so much on unit testing as I thought I should. I didn’t think that’s true. I think that good test coverage through unit testing is like you’re [inaudible]. You’re getting there all the time.
DAVID:
Yeah.
PIOTR:
But it’s perfectly good to start with some higher level tests.
DAVID:
Oh, okay. So, when you talk about TDD, it’s not necessarily unit tests that you’re starting with? You might be starting with more higher level, almost behavior-driven?
PIOTR:
Yeah, exactly, exactly. Like when I’m building, I don’t know, a Rails app, typically I start with a Capybara feature test. And I don’t know. I just make it pass. That’s the most basic thing that I can do, describe how something should work on a very high level. And then I just make it pass. And it’s a start for me. And I do it with absolutely everything. When I’m spiking, I have some idea. Okay, I want to achieve something. And I can always describe it with a test. Because if I don’t describe it with a test, I would do something like, I don’t know, puts some output and just look at the output.
But that’s not so great for me. I prefer to write a test and just see it pass.
DAVID:
Right. Why puts the output when you can capture the output and compare it and make an assertion?
PIOTR:
Yeah, exactly.
DAVID:
Okay. So, for anybody that does TDD the way I do, there’s actually an epiphany here in what you just said, which is that you go up to a higher level. If you can’t TDD at a very low level because you don’t know what the low level interface is, you can always go to a higher level and TDD at that level and that totally makes sense to me. I’m spiking out an app right now and I have no freaking clue how the low level objects are going to work. I’ve got a really nasty knot. There’s three different ways I can build the interface and they all have problems. And I’m like, I can’t TDD this. And I just realized that the one level higher of this, the behavior, is very clear. I know exactly what the higher level code should do. And you’re absolutely right. I should build a test at that point. And then, however I choose to build out the units underneath it, then that won’t matter. That will fix it.
PIOTR:
Yeah, exactly. That’s the process. It’s about the design, right?
DAVID:
Yeah.
PIOTR:
You need to go through multiple steps to get to a point where you’re happy with the design. But it’s just impossible to imagine the entire design upfront.
DAVID:
Yeah.
PIOTR:
I mean, you can do that, but it’s upfront design and that’s bad. Typically, you’re wrong in the beginning. So, that’s where TDD helps, because you describe something that’s very basic in the beginning. And you just see it work.
DAVID:
Oh, that’s beautiful.
PIOTR:
And then you can take it from there and just see how things evolve. And one thing that really helped me, and thank you Avdi because it was one of your episodes in Ruby Tapas, is one of your episodes you showed how you… I think you were building some command line script or something. I forget what it was. But you were showing how to do TDD. And you had this rule that once you don’t get a meaningful feedback from a test, once you see a failure and it’s hard for you to figure out why it actually failed, it’s a good moment for you to break things down into smaller pieces and switch to a more lower level test. And for me, that was a great explanation. And I just loved it. So, that’s how I work these days. I just write a test, make it pass. And once something is failing and I’m finding myself struggling with a long backtrace or anything, for me it’s a sign that, okay, now it’s just too complicated. And I just need to break things down into smaller pieces.
DAVID:
Yeah.
PIOTR:
And at that point I already know enough to write those lower level tests. And for me, that’s just the most efficient way of working.
DAVID:
That is so great, because what you’ve just described is, there’s a time when you get stuck on how it’s too big so you need to break it down. And there are times when you don’t know how the broken down pieces are going to work, so you need to scale up and test at a behavior level.
AVDI:
Yeah, you really have to listen to what the tests are telling you and adjust your altitude.
DAVID:
Yeah. Okay, my mind is blown. You two talk amongst yourselves. [Laughter]
AVDI:
Alright. So, we’ve talked a lot about TDD. So, I don’t want to hammer that to death in this episode. One thing that you brought up in your blogpost which I think is a really interesting topic to talk about in Ruby, is you’ve been finding a lot of value in immutability, haven’t you?
PIOTR:
Yeah.
AVDI:
Why do you like immutability? And what was the shift for you, starting to think about immutability in
Ruby?
PIOTR:
Well, it’s a whole story. Because it started when Dan Kubb started using something called Command-Query Separation, which is an idea that you divide your interfaces into two categories (that some methods are commands and some methods are queries). And commands can change objects and queries cannot. So, that was the first step for us, because we’ve started doing that in our open source projects. And I really enjoyed it.
So, an example of a violation of this rule, this idea, is for example valid method in Active Record, because it’s a command and a query. So, it changes the object because it has the error messages on an Active Record instance. And at the same time, it’s a query because it gives you an answer to the question whether an object is valid or not. So, the separation was a great first step because it allowed us to make things more explicit and more clear, that some methods can change objects and some methods cannot.
One interesting thing that Dan started doing was that command methods always returned self so that you cannot rely on the return value immediately. You just call the command method and then you ask for some information from an object using a query method. So, in case of Active Record it would be something like, validate for example would be a command which would validate the object. And then you would ask a valid predicate method to ask if that object is valid or not. And that’s just a cleaner way of organizing things. And I really liked it.
But that was the first step, because the second step was to actually drop completely all the methods that changed the object. And Dan started doing that. And I liked the idea because I like how the code looked like, although it was difficult for me to understand how to do it. [Chuckles] Because if you design your objects in a way that they will never change, then there’s much more work that you need to do before instantiating them. So, it was a little bit difficult for me, because it didn’t feel idiomatic in Ruby. But I decided to struggle just because I was curious. And I did that and after some time, I don’t know how long it took me, but after some time I just learned it. And it felt great.
And these days, it’s just how I do things. It just feels natural for me. So, that was the story [chuckles] behind it. That’s how it all started. So these days, every time I write a class, I design an object, it has that [interface]. When I need some newer presentation of the given object, I would return a new instance rather than to change the object in place. So yeah, that was the story.
AVDI:
Okay, so actually I kind of want to get concrete on that. So, a user object, you want to change the person’s name. What does that look like?
PIOTR:
That’s tough, because the way I do it these days is that, at least in Ruby Object Mapper, we have separation between reading and writing. So, if you read data you get objects that don’t change or that shouldn’t change, at least. And if you change data, you have a separate, completely separate interface for that. You have a command interface. So, when I’m using ROM I don’t have a user object that changes. It’s like…
AVDI:
Well, okay. I want to circle back on that. But I’m curious. Just the… leaving out persistence, leaving out how you would get that out of the database and put it back in, just to have an object and change its… like change a user’s name. Do you have a way of saying clone yourself but with one field changed? Or how are you managing that?
PIOTR:
Yes, yes, yeah. That’s how it works. You just return a new instance with a different name.
AVDI:
Do you have a setter method for each attribute that does that? Or do you have a global update method? How do you structure that?
PIOTR:
My convention is to have a private method that can return a new instance based on the current state and some input that was sent. There are different libraries out there that have this kind of functionality. For example there is mbj’s Anima. It has I think update method. Anima is similar to Virtus for example. So, it instantiates an object with an attribute hash, but it’s immutable. So, you can’t change an object. And if you want to get a new instance with an updated name, you just call user.update but it will return a new instance rather than changing the object in place.
AVDI:
Alright, so that’s really interesting. So, that’s basically how you’re doing it?
PIOTR:
Yes. Like naming wise, I’m still considering what the name of this method should be, considering with for example or .new again. I mean, not .new, instance new method. But yeah, in general the convention is to return a new instance. And it has to know how to do it. So, it needs to merge the existing state of an object with the new thing, the new attribute or a new attribute hash or whatever it is.
DAVID:
Right. So, you might say User.find(1) which is going to return Bob. And then in that same chain, you could say
name:
Alice) and that’s going to clone the object and give you back a new user object. It’s not going to modify the one that we loaded from the database. It’s going to give you back a new one?
PIOTR:
Yes.
DAVID:
I like that. I like that. Immutability is actually the third point that you mentioned in your blogpost. And I love immutability. I love the concept of it and I love when you follow it and adhere to it really well, it gives you back a lot. But it’s one of those tradeoffs that you have to work hard to get it to work, especially in Ruby. And you actually mentioned that, that immutability’s definitely not a firstclass feature in Ruby. You’ve already talked about how the code doesn’t quite feel idiomatic. But are there some actual technical problems or performance issues with immutability in Ruby? Like if you’re cloning objects, obviously you’re burning memory somewhere. What other issues do you run into with immutability?
PIOTR:
The biggest problem is freezing objects. So, we started by using a gem called Adamantium which deeply freezes your object. And that’s just slow. So, for me for my point of view, using this gem was a great way to teach myself to [inaudible] immutability. But now I’m no longer using it because it’s just too slow. So, that was the concern. That’s why I mentioned that it’s not a first-class feature of Ruby. Even though it’s a public interface and so on and so forth, but still it’s just slow. But when I say immutable object I’m not very strict here. I’m not saying make sure that you cannot change an object.
DAVID:
Right.
PIOTR:
It’s just, avoid unnecessary mutation.
DAVID:
Yeah.
PIOTR:
Because you really don’t need that so often as you think. So, when I’m introducing a new object with some methods, those methods just don’t change this object even though it’s still possible to change it because it’s Ruby and you can do whatever you want. But I’m just avoiding it. It’s not my interface. My interface cannot be used in a way that would change something. And for me that’s valuable.
DAVID:
That’s actually a really good general point. There’s a difference between avoiding doing something in your code and trying to build a gatekeeper to prevent anybody from doing something in the code.
PIOTR:
Yeah, it’s really hard. When we started using Adamantium in ROM I was constantly hitting a wall with some gem that was used where it actually did rely on mutability of some object. And it would be frozen [chuckles] because I used Adamantium and it deeply freezes everything. So, we were freezing objects that come from some third-party dependencies. [Chuckles] So, that obviously breaks things.
DAVID:
Right.
PIOTR:
So, you just cannot be so strict about it in Ruby because there are so many things that are mutable. It’s just not reasonable to do it like that.
DAVID:
Yeah, yeah.
AVDI:
Where I see immutability really being difficult in Ruby, it’s one of these things you don’t run into immediately. You start to realize it as your app becomes more complex. And I’m curious if you’ve had the same experience. Where I see it being difficult is deeply nested data structures, or even moderately nested data structures. Because changing Bob’s name is one thing. But let’s say Bob has a cat and the cat has a food bowl and the food bowl is red. And then you want to change the food bowl’s color to blue. With mutable data you would just say, okay Bob, user.pets.first. (okay, this is sounding terrible already)… obviously…
DAVID:
I love that your brain is refusing to let you violate the Law of Demeter here.
PIOTR:
[Laughs]
AVDI:
Yeah, yeah. My brain hates me right now. But you know, you might… the thing about Demeter though is that it’s perfectly legal to do this in separate methods. You don’t want to jam it all together in one method. But it’s perfectly legal to have a series of methods that are digging down into this data structure that are saying, okay, one is getting Bob’s pet. And the next one is getting Bob’s pet’s food bowl. And finally there’s one that sets color to blue. But what happens with immutability is you can’t just chain it and put an assignment on the end, because you have to go down there and you have to clone the food bowl with a new color. And then you have to clone the cat.
DAVID:
And you have to clone Bob.
AVDI:
You have to clone the cat with a new reference to that new food bowl. And then you have to clone the pet collection that now contains the new cat, not the old cat that has the reference to the red food bowl. And then you have to clone Bob to reference the new pets collection. And that’s where immutability seems to get really difficult in Ruby. And in functional languages, you have fancy concepts like zippers and things that, I think that’s the right term, that enable you to sort of tackle these updates in a way that looks vaguely like it would if you were doing it mutably. But yeah, do you run into issues with that?
PIOTR:
Not really.
AVDI: [Laughs]
PIOTR:
Yeah. [Chuckles] It’s just that I decided to split things. So, I just do it separately. Like if I loaded something into memory then I did it to read the data. And if I want to change something… but yeah, we’re talking about ROM right now [chuckles]. I haven’t experienced that problem, the problem that you described. I’ve never had code like that.
AVDI:
I hope you don’t. [Laughter]
AVDI:
I hope you never do.
DAVID:
This sounds like a Turing equivalence of just the deeply nested object problem, right? And you’ll run into this code smell. Well, it’s not a code smell. You’ll run into a testing pain where you want to test changing the color of the food bowl when all you have is a Bob interface. And so, you end up with these fixture files that have to set up Bobs and pets and cat and food bowl. And you have all these fixtures and let statements in your RSpec and all that stuff. You have all this setup and this teardown. And it’s a testing pain. And the testing pain is actually leading you to a code smell which is you have this deeply nested object structure.
And I’m wondering if, because I agree with you Avdi. Changing the color of the food bowl, forcing you to re-clone your entire employee’s database, because you modify Bob which means you now have to clone the entire employee database. And it seems like this is a pain that gets surfaced very quickly with immutable code.
AVDI:
Yeah. I don’t know. There’s a reason that things like zippers exist in immutable programming languages.
DAVID:
Yeah.
AVDI:
Because you really do often need to update. You might have… it’s not uncommon to have trees that are three layers deep or something of data.
DAVID:
Sure, sure.
AVDI:
And you often need to say, okay I need to update five different things in this aggregate object. And I need to make sure that I get the new copy of the whole structure back.
DAVID:
Yeah.
AVDI:
And it’s not something that Ruby, in my experience it’s not something that Ruby really lends itself to. But if it works for you. [Chuckles]
AVDI:
Now, I’m curious how far you’ve gone with this immutability stuff. Have you dug at all into persistent data structures like the Hamster library or anything like that?
PIOTR:
No, not yet. I haven’t used Hamster anywhere. These days I’m using, when I’m loading data from a database I’m trying to build very simple Struct-like objects that are just used for reading. But yeah, I haven’t actually tried anything that would be truly immutable like Hamster. It’s just the way I use it, is that I don’t rely on mutability of those objects. I use separate objects for reading and separate objects for writing.
AVDI:
Cool.
DAVID:
I like that. This is actually a really good segue into the next point in your post which is you talk about, no rules, just guidelines. And we talked about how immutability with deeply nested object structures can give you a source of pain. And so, you tend to start… I anyway have been shocked by the cattle prod enough times that I’ve stopped building deeply nested… yes, you do get forced at times into building trees that are two or three or four levels deep. But when you’ve got a tree that’s 20 levels deep, you riot. Instead of saying, “Yes, we’ll go ahead and build that,” no, you revolt. Or at least I do anyway. Or at least people say I’m revolting.
Anyway, the interesting thing is that as we talked about immutability we talked about things you run into problems with Ruby. And point four is kind of interesting because you run into problems with Rubyists if you start to give them rules. Every argument I’ve ever heard against the Law of
Demeter centers around the fact that people rankle over whether or not it’s a law. They’re like, “Well, who made it a law? Who’s the judiciary body here?” I’m like, “No, just listen to what I’m saying. Stop chaining together so many things and you’ll stop having so much coupling in your code.” “Yeah, but it’s not a law.” How have you found rules turning into guidelines?
PIOTR:
I think if I remember correctly, I started thinking about it like that after spending a lot of time using various code metric tools. So, I learned a lot about various code smells. I learned a lot about all kinds of rules and laws as you mentioned, like Law of Demeter. And I tried to maybe not follow those rules 100% all the time. But I tried to follow them as much as I could. And I just realized that quite often you don’t really need to follow something until you really see that it is a problem. And it’s much more important to keep in mind what kind of common problems you may face or what kind of code smells may give you trouble. And being aware of that is much more important than refactoring your code immediately just because you realize that something violates some law or rule or whatever.
DAVID:
Yeah.
PIOTR:
So, for me what became much more important is to be able to change the code easily without fear. And that’s strictly related to test-driven development and just good testing skills. So, I started focusing on learning how to become better at testing, how to write better tests, so that I could change my code easily.
DAVID:
Yeah.
PIOTR:
So, this is basically something that allows me to, once I know that there are some rules and I see that some parts of my code violate some rules then having that knowledge helps me to refactor it. And with good tests I can do it easily, because I just change implementation. And if my tests were good then it will continue to pass. So, that was my realization, that I don’t need to fear that my code is violating some rules, because it happens all the time. And most of the common code smells are by definition, they are potential problems. So, when you see a Law of Demeter violation, it’s not like it’s going to ruin your life. It will probably give you trouble, 90% sure it will. But it doesn’t change the fact that it’s not like you need to immediately refactor it.
DAVID:
Yeah. Sometimes the right thing to do is to weld two things together and couple them strongly and just live with the coupling for as long as you can, right? That’s the fastest way to do it.
PIOTR:
That’s true. This is really related to testing. It’s just much more important to be able to write good tests and be able to easily change your code. And just don’t freak out just because your code violates some rules. So, that’s why guidelines, be aware of them. Have that knowledge.
DAVID:
Yeah.
PIOTR:
Learn about it. But don’t be so strict about it, because it’s just not reasonable.
DAVID:
Yeah. So, can I put you on the spot? Is there a specific “good programming rule” that has lead you to have difficult tests and hard to maintain code?
PIOTR:
You mean by violating it I have problems with tests? Or [inaudible]
DAVID:
No, no, the other way around, the other way around. Like there’s some rule that everyone agrees is a good rule and… because you say that, what did you say? You say, “I found that applying OO rules rigorously to my Ruby code has had some bad consequences.” Are there some objectoriented rules that… I guess I don’t want you to come out and just decry object-oriented programming entirely. You don’t have to debunk the entire school of thought. But is there a rule that you can take way too far?
PIOTR:
Single responsibility principle probably. That’s the first thing that comes to my mind.
DAVID:
Okay, so you might have a class that has more than one responsibility or you would spread the responsibility through multiple classes?
PIOTR:
If you try to really rigorously follow it right from the start, you’ll end up in misery.
DAVID:
Okay. That’s a good heretical statement. I like it. I like it.
PIOTR:
[Laughs]
AVDI:
[Laughs] I like to have classes that are three lines long and they just handle setting one attribute [on another].
PIOTR:
[Laughs] Right.
DAVID:
Well, cloning that object, you know.
AVDI:
My ‘hello world’ programs have a class for both ‘hello’ and for ‘world’.
DAVID:
[Laughs]
AVDI:
Because those are clearly different concepts.
DAVID:
Have you guys seen the Fizz Buzz solution in C#? I’ll post it in the show notes. It’s like 500 lines of code. There’s a Fizz Buzz concrete implementation factory. Yeah.
AVDI: [Laughs] DAVID:
There’s a manifest file. There are 30 different classes in it. And yeah, it’s pretty awesome.
PIOTR:
[Laughs]
AVDI:
Somebody should write a version that first instantiates a client for a Fizz Buzz web service.
DAVID:
Right on. I actually was in a job interview and somebody said, “Let’s do Fizz Buzz.” And I’m like, “This has got to be a well-studied problem. So, require fizzbuzz.” And he’s like, “Okay, but what if the gem doesn’t exist?” And I’m like, “Okay, then gem install fizzbuzz.” And I got the job because I went home that night and I built the gem and published it and sent him an email that night that says, “By the way, that code will now work because the gem does actually exist as of 6pm tonight.”
AVDI: [Laughs]
DAVID:
And he’s like, “Okay, I like this guy’s attitude.”
AVDI:
See, that’s an argument in favor or mutability, the fact that the internet is mutable.
DAVID:
[Laughs]
AVDI:
Is what got you that job.
DAVID:
That’s right, that’s right.
AVDI:
[Laughs] By the way, I feel like I should play a novice’s advocate here and say, “What is Fizz Buzz?”
DAVID:
So, Fizz Buzz is a beginner-level programming quiz test where you ask somebody to print out the numbers from 1 to 100, one per line. So 1 then 2. The catch is that if the number is divisible by three, you print out ‘fizz’ instead of the number. So, you would print out 1, 2, fizz, 4. If the number is divisible by five, you print out the word ‘buzz’. So, it’s 1, 2, fizz, 4, buzz, and then fizz again because it’s divisible by 6, and so on, all the way up until you have the number that’s divisible by three and by five, in which case you put out ‘fizzbuzz’. And then it all starts over again.
And if you’ve been programming for 10 or 15 years this is not a programming test, this is a typing test. But a lot of recruiters and a lot of interviewers found some beginner-level programmers that actually couldn’t solve Fizz Buzz in under an hour. And so, this was really popular back in 2008 or 2009 as an entry-level test to weed out just the utter novices from the novices that have at least been around the block once. So, I guess yeah, if you’re in a bootcamp or coming out of a bootcamp, or if you’re trying to throw your hat in the ring for a programming job for your first time, google Fizz Buzz and see if you can solve it.
AVDI:
Yeah, thank you for that definition.
DAVID:
Yeah. The other reason I got the job was because I submitted the next morning, I submitted a solution that seeded the random number generator with this big hairy 18-digit number and then looped a hundred times. And it had an array of the current number, the word fizz, the word buzz, and the word fizzbuzz, and it selected one at random and printed it. And it was just print one at random, print one at random, print one at random. And the output was correct. And it spooked the interviewer. He’s like, how in the heck? And I said, well you see that srand up there where I’m seeding the random number generator with this number that’s two and a half billion or something like that? It took my computer 17 hours to find the random number seed that would generate the correct random output in the right order.
AVDI: [Laughs]
DAVID:
Yeah, for a brief time I collected crazy solutions to Fizz Buzz. Giles Bowkett submitted my absolute favorite one which is he monkey patched Fixnum so that it would represent itself as fizz, buzz, or fizzbuzz. So, his code, once he had monkey-patched Fixnum, his code was puts 1..100.
AVDI: [Laughs]
DAVID:
And just print the numbers 1 to 100. And they will render themselves correctly. And the best part was if you loaded it up in IRB, IRB prints out line numbers at the beginning, to the left of your code. And it uses the Fixnum class to do it. So, you would write line 1 and then on line 2 and then on line fizz and then on line 4 and then on line buzz.
AVDI:
[Laughs] [inaudible]
DAVID:
[Laughs] And so yeah, it made your eyes itch. You’re like, “Wait, what happened?”
AVDI:
So, alright. So, those are some good thoughts on rules versus guidelines. You’ve got another point if we were to go through this post in order. You’ve got a point where you say class interfaces are a smell. Can we just all agree on that and move forward? [Chuckles]
DAVID:
Unless we want to elaborate on that. I’ll play newbie’s advocate at this point, because I have come into this in the past year and a half. It never really dawned on me that classes are constants defined globally up in the kernel. And public, or static class methods rather, (static, my C++ background is showing) but class methods are public accessor methods on those global objects.
Which means they are global methods, global variables.
AVDI:
Yeah. So, Piotr what’s your take on class interfaces?
PIOTR:
Yeah, the first moment when I started thinking about it as a problem was Rails. And it’s especially horrible in Rails due to the autoloading. So, one of the things that made me start to think about it as a problem was when I hit my first huge Rails app that was just so big that many, many places had to be refactored. And people were just discovering so many places where there are so much coupling, internal coupling within the code. And one of the reasons for that was that in Rails you can refer to any class anywhere. And it will be automatically loaded. So, you can accidentally without thinking much about it create a class that over time starts to depend on 12 other things, just because you can use it right there without the need to require it, without the need to instantiate it. You just call a method on a class. It’s so simple you don’t think about it. So, it feels great in the beginning. But the end result is that you create so much coupling and you introduce so many dependencies inside your classes, completely without thinking about it.
But in general when it comes to class interfaces I just like to be explicit about dependencies. So, when I have an object that has just too many things to do that I need to introduce some collaborators, I would always inject them in the constructor as objects. I would never instantiate them myself in the constructor. So, the object will not know how to instantiate its dependencies. And this technique was very helpful for me. And I realized that I always want to do it. I always want to use objects and introduce dependencies as objects, not classes, just because this makes me aware of dependencies and make dependencies very explicit.
And also, once you start using objects rather than classes everywhere, there are a lot of nice benefits as a side effect. Like it’s much easier to test things because you can easily use mocks in your tests. So yeah, these days I’m using classes, in mean class interfaces only to instantiate objects. So, I’m mostly using them as object factories or whatever you want to call it.
DAVID:
How are you handling collections? Because there’s a thing that we inherited from Rails a little bit where if you have a user object you want to create a user instance. But if you want to manipulate a collection of users, in Java or C++ I would have a users collection that extends the list or extends iterator or whatever. Or it extends iterable rather. But in Ruby, especially inherited from Rails, it seems like you say, user.update_all in order to update a collection of users and that sort of thing, how do you handle that?
PIOTR:
The way I did it in ROM is that you have relation objects. And they represent relations. So, those are your collections.
DAVID:
Okay. So, you would have basically like a users class?
PIOTR:
Yeah. You could name it like that. I actually do name it like that in the examples, yeah.
DAVID:
Okay. So yeah, if you have 10 users, they would go in an instance of a users object and then you could manipulate them collectively?
PIOTR:
Yeah, exactly.
DAVID:
Awesome.
AVDI:
This is something I’ve seen bite people in their codebases so many times. Because the implicit assumption when you have, when a class acts as the collection of all instances, the assumption there is that you’re only very going to have one collection of all instances. And that is one of those kind of similarly to the assumption of has_one, it’s one of those assumptions that will inevitably be violated. You have, for instance…
DAVID:
Yeah.
AVDI:
Typical Rails example where you have the collection of the employee class also doubles as the collection of all employees. Well, what happens when you have a net split with your sharded databases or your cloned databases? And you find yourself doing some DevOps work where you’re trying to copy a bunch of employee records from one database to another. You think, “Oh, I’ll just write a Ruby script that copies from one database to the other.” And then you realize that embedded in that idea of the employee class being the collection of all employees is that it’s the collection of all employees in one database. Well, how do you connect to both databases at once? And you can, but it’s way more painful than it needs to be, because of that assumption that there is one global collection of all employees. And this also bites you when you’re trying to test.
DAVID:
Yeah.
AVDI:
And in many, many different ways, when you have that central collection that there is only one of.
DAVID:
There needs to be a proper name for this. You guys have known me long enough to know that I have this weird aphasia where I name things the wrong thing all the time. And we ran into this, the most explosive time that this really bit me on a team was when I was working at Acclaim writing Jeremy McGrath Supercross. And up to, oh gosh, 11 months into the project, everything we had ever done in the code, we referenced the_track. Basically, whichever, when you were racing around a supercross track we represented that by the_track. And there was only ever the_track.
And the epiphany when we had to basically support 16 different tracks to put in the cartridge or the PlayStation DVD, or CD rather back then, we had to rewrite a lot of code, because we had to go from… our team collectively had this painful epiphany. And we call it moving from ‘the track’ model to ‘a track’ model. It’s the ‘the’ to ‘a’. And I’m wondering if there’s actually a proper name for this where you stop thinking about the instance, the singleton instance of a thing, and start thinking about a collection of them.
AVDI:
And more and more lately, I feel like the entire notion of a single anything is just misleading in programming.
DAVID:
[Laughs]
PIOTR:
Oh, that is so true. That is so true. One thing I want to mention about classes as well in the context of Rails, and this is something that people don’t usually think about. So, when you have some Active Record class, Active Record model, say User [inaudible]. So in Rails every class is autoloaded, right? So, you can refer to user anywhere. And User at the same time, the User model, at the same time gives you access to entire users’ table, right? So basically, from every place in your codebase you can refer to all the user data that you have in the database.
DAVID:
Yeah.
PIOTR:
Which leads to really, really nice things. Like for example, last year I noticed in one project some preprocessed asset, CoffeeScript asset that used Active Record model to do something and generate some data.
DAVID:
[Laughs]
PIOTR:
And I wanted to extract it to some other project because we were splitting things up. And I realized that, “Oh shit, I have an asset that’s coupled to my database. What the hell? Really?" But it’s possible. And it’s so simple to do it like that in Rails because it will just work.
DAVID:
Yeah.
PIOTR:
So, that’s probably one of my biggest problems with using classes in Rails. It’s just that you can couple so many things with your database without thinking too much about it.
DAVID:
Yeah, yeah. That’s actually a really good point. We’ve talked a lot on the show in the past about the surface area of your class. If you include a module in your class, basically that’s multiple inheritance. You have added all of the methods in that module to the public interface of your class. And you have just greatly increased the size of the interface. And that’s one of the SOLID principles. I think it’s the I or the D. It basically says have small interfaces, if at all possible. It’s better to have a bunch of small interfaces than to have one big interface.
And you’re talking about the other point of view into this, which is… not only is a very large surface area a bad thing, or well, increase your maintenance nightmare, but I get this paranoia when I’m working especially with Rails, because visibility into an object. Every object in the system can see this object, which means that I have to be very paranoid as I’m writing this object, because anybody can get me. Anybody can come grab me. I’m not protected in any way. And that’s doubly paranoia-making.
In C++ or Java I can make a bunch of stuff private and you can’t get at it. And so, I can be very defensive. And now lots of visibility doesn’t hurt me as much. But with Ruby when there really is no privacy, privacy is really just a protocol. Or it’s politeness. You can totally invade another class’s privacy if you want. Throwing in a huge surface area with universal visibility, it really is just a recipe for woven mat code where every object is interlaced with every other object in the system.
AVDI:
Yeah, absolutely.
PIOTR:
Yeah, that’s so true. This is the reason, one of the reasons why in Ruby Object Mapper there are no classes. The classes are only used to configure ROM. But once you finalize the configuration you get registries with objects. So, if you want to access users, you do it through a registry of relations. And I noticed that some people see it as a problem because they compare it to Active Record and they do things like, “Oh, in Active Record it’s like User.create. And here I need to get the registry and then I need to get this object and call this method. It’s more work.” And I say, “Yes, and that’s a feature. That’s the feature, to make it explicit.” You need to treat this as a dependency and be really conscious about where you really want to use it and where you don’t want to use it.
And the fact that you cannot just refer to it from anywhere is a feature.
AVDI:
And that leads really well to your next point in this blogpost where you say that convenience can be dangerous.
PIOTR:
Yeah. Right. Yeah, yeah. I briefly talked about this as well. So, it mentioned autoloading in Rails, things like that. So yeah, these days I’m trying to use tools that are simple. I don’t mind writing a little bit more code if it’s more explicit and if it’s just easier for me to understand what’s happening. So, I think that Rails in general, the philosophy behind Rails is to make people write as little code as possible, which is sometimes okay. But my experience of that in the long-term, it’s not really sustainable. And the code quickly grows to something that is just so difficult to maintain. And when there’s a bug it’s just almost impossible to debug it, because there are so many objects with implicit behaviors. So many things just happen automagically. And that’s the price of convenience, the way I see it at least.
DAVID:
We had Dave Thomas on last week and we touched a lot on the principles that he mentioned in ‘Pragmatic Programmer’. And one of the things that he talks about in that book, it’s a 15-year-old idea, but it’s based on a pain that we started feeling back in 1989, which is never trust an evil wizard. And an evil wizard is anything that does for you something in the system that you do not understand. And I like Rails from 2006 and 2007 when their idea was convention over configuration. We’re going to make the easy path simple.
And Rails 4, it’s now the behemoth that it used to make fun of. In 2006 Rails made fun of Java because it was so big and bulky. Well, Rails is now Java. And it still has convention over configuration but there’s so much configuration now that in 2008 when people said, “Rails does a lot of magic,” you just patted them on the head and say, “Well, just learn the magic. It’s fine. There’s not that much of it.” And now people go, “Rails has so much magic,” and yeah, holy crap, it really does. It has tons of magic.
AVDI:
How do you know, though? How do you know when you’re at the sweet spot between convenience and explicitness?
PIOTR:
For me, when it’s easy to do something like easy enough and at the same time when something goes wrong it’s easy for me to understand what’s happening behind the scenes. That’s a good balance. I wouldn’t sacrifice the simplicity just to get something more convenient. I don’t mind writing two lines of code more, as I mentioned, if the benefit is that I’m using something that’s simpler. So, I think the balance is important here. Have something that’s easy enough to use and at the same time it’s just simple to debug and simple to understand what’s happening.
AVDI:
It seems like that awareness requires being really mindful of the pain that you encounter when there’s a problem. Because I think something that we do very often is we run into a problem and it’s horrible and we spend the day just wrangling with it. But then the next day when we finally wrangled it to ground and beaten it, we’re just like, “Whew. Okay.” And we move on. We go on exactly as we were, because we’re done with that problem. And it’s not a problem anymore. And then when we see somebody new hit that same issue, we’re like, “Oh yeah, that’s a huge thing. That’s hugely painful and that happens. And here are all the steps that you have to do to fix it.” It’s so easy I guess to dismiss the pain of working through an issue once you’re done.
PIOTR:
Yeah, that’s true. That’s true.
AVDI:
I’m curious. Do you make an effort to notice when things are too complicated to debug?
PIOTR:
An effort like in terms of inspecting my stack or digging through the sources?
AVDI:
I don’t know. Some people keep logs of issues they’ve had. I’m just curious if there’s a way that you’ve made yourself more sensitive to, “Okay, this was too difficult to debug. I need to scale back on the convenience.”
PIOTR:
Yeah, I’m spending a lot of time making sure that what I write and what I use is just simple. We’ve had a couple of cases in ROM during the last few months where something was just too difficult. And even though it was convenient we decided to remove it and simplify it, just because it was too implicit. For example, we were doing too much magic. And even though it felt just convenient in some simple cases, the downside was that it was just difficult to understand. Yeah, like scaling down on metaprogramming mostly. So yeah, I do. Yeah, a lot of effort goes into making sure that things are simple, that things are as explicit as possible. And there’s just as little magic as possible.
AVDI:
That seems like a really cool skill as a project maintainer, a really mature skill as a project maintainer. Because I think my temptation as either a project maintainer or as an advocate of tools is often, when somebody runs into an issue like that, it’s often to jump straight to, “Oh yeah. It’s magical. Let me show you the magic. Let me explain the magic,” instead of jumping straight to, “Hmm. Maybe that’s too magical.”
DAVID:
Yeah.
PIOTR:
Yeah, that’s true. And sometimes, you can get the same result by just changing the implementation. So sometimes, you don’t really need that much magic. Sometimes you just need to change your strategy and just achieve the same goal but with a simpler implementation.
AVDI:
Yeah. Alright, so segueing very badly, [chuckles] let’s talk a little bit about mutation testing. Because you are one of the few advocates I’ve seen in the Ruby community that’s really talking a lot about mutation testing. What is it and why does it matter?
PIOTR:
Yeah, so mutation testing is a technique where you’re using a tool that loads your code, changes it in memory like at runtime, and then runs your tests. And the expectation is that some of those tests must fail. And if everything still passes, then you’re missing some coverage. So, in Ruby we have a really good tool for that. It’s called Mutant. I’ve been using it since it was created I think two years ago. And yeah, it’s I’d say an advanced technique. It requires a lot of time to learn how to do it properly. Probably the biggest problem with it is the tool that you’re using, because it must run your tests and it must match which tests have to be run after changing some specific piece of your code. Because if you run all your tests, it might get very, very slow. So, that’s a challenge on the tool side.
But it’s also a challenge because you need to know what kinds of mutations are problematic for you. So, at least that’s my experience with Mutant. It’s the only tool that I’ve used so far in Ruby. Before that I used [Haskell]. But the general problem is to know which mutations are actually problematic, because if you want to achieve 100% mutation coverage it’s just a lot of work. And it will slow you down drastically. So, I still find it extremely valuable because it can easily find bugs in your code. The only problem is that, at least that’s my opinion, that it’s not something that you would like to do on a daily basis, especially if the code that you’re writing is still changing a lot. Because it puts a huge emphasis on really good test coverage.
So, the way I use it these days is that in my open source projects I would use Mutant before the release, again in specific parts of my code. And from one release to another the test coverage is getting better and better. And one thing, probably one of my favorite parts of mutation testing is that it actually helps a lot in removing your code. So, you can easily simplify your code and remove things that you don’t actually need, because it shows you exactly all the places that are not covered by tests. And quite often, you may realize that, “Oh, I actually don’t need that,” and just remove something. So, it’s a fantastic technique but it requires a lot of time to learn how to do it properly.
DAVID:
Yeah.
AVDI:
So, if there’s a line that can’t cause a test failure even if it’s changed or removed, that suggests that you might not even be using that line of code at all?
PIOTR:
Yeah, exactly.
AVDI:
Very cool.
PIOTR:
Yeah, but it’s tricky. It’s tricky, yeah. There are some cases where some mutation is not really necessary. But yeah, it’s on the tool side. So, Mutant is getting better and better with each release. It’s just, it takes time to learn what kind of mutations you can safely skip if something isn’t really a problem. That’s why aiming for 100% coverage is just not reasonable.
DAVID:
I find my biggest challenge with testing, even today, is eliminating duplication between my tests and my code. And what I’m trying to get towards is almost like a crosscheck. I’m old enough to have taken a bookkeeping class in high school for how to do tabulation and stenography and that sort of stuff. And one of the things that they have you do is that you crosscheck your work. So, if you’re adding up columns of numbers, then you check your work by adding up the rows of numbers and then you sum up both sums. The sums of the columns should equal the sums of the rows. And if those two numbers don’t equal, then you have a bug somewhere. And if you just sum up your columns a second time, like in your unit tests, basically that’s duplication.
So, I try to build unit tests that will crosscheck my functionality rather than basically duplicating what my code is doing. It will actually basically say, “This is what I expect you do,” rather than, “Do you do this step? Do you do this next step? Do you do this other step?” And I have to admit, I fear mutation testing because I can’t, in my mind I can’t see how I would do good mutation testing without duplicating a lot of my code in my tests. Is that a legitimate fear or am I just being spooked by something in my own imagination?
PIOTR:
No, that sounds legit. It is a problem because as I said, it’s related to the tool that you’re using for mutation testing. As I mentioned, running the tests is the problem because it can be too slow. That’s why it’s important for the tool to find which tests run. And if you want to have very isolated tests that would cover everything, then you will end up with a lot of duplication in your code, in your tests. And I’ve experienced that myself. But it is possible to cover most of the mutations without the duplication. It’s just that you need to run the entire suite.
DAVID:
Yeah.
PIOTR:
Which can be just too slow. But yeah, it’s still something that you can do against very specific parts of your code.
DAVID:
Yeah.
PIOTR:
So, I used it in a couple of projects just for specific parts. And it was extremely helpful there.
DAVID:
Yeah. Yeah, I can definitely see how it would improve the thoroughness of your tests.
PIOTR:
Oh yeah, definitely.
DAVID:
So, we’re getting close to needing to switch over to picks. But I don’t want to do that until we talk about this last point in your blogpost, because I really think you saved the best for last, which I love.
The last point in your blogpost is ‘ideas behind ORM are a fallacy’.
AVDI:
Yeah.
DAVID:
And I just want to give you the podium. Or Avdi, did you have something to add on the fire? [Go for it].
AVDI:
Well yeah. I just want to say, I’m absolutely looking forward to this because the last time we talked to you, you were neck-deep in implementing a for-real serious object relational mapper for Ruby. But that has changed course significantly since the last time we talked. So, I’m very curious about your thoughts there.
PIOTR:
[Chuckles] Yeah. Oh, that’s true. Yeah, in general the main idea behind ORM is actually something that we need, especially in Ruby. In Ruby everything is an object, right? So, typically we represent data as hashes or array of hashes for example. So, we definitely need something that we know how to pull up the data from a database and just map it to the Ruby object. So, that part is obviously something that we need. But everything else is a horror for me these days.
So, something that made me change my mind was that I spent a lot of time working on two ORMs basically. The first one was DataMapper. And then Ruby Object Mapper. Initially the project was supposed to be the actual DataMapper, the implementation of Data Mapper pattern. And the more I worked on it, the more I realized that it’s just so much complexity. And it requires so much logic that it just made me start to question it. Do I really want to do it? Is it really worth the effort?
And it’s also related to the ideas behind immutability. So, the typical thing in Active Record for example is that the objects are mutable. So, you basically load a bunch of objects into memory. And then you change them. And then something needs to synchronize the state of those objects with the data that you have in your database. And that part alone is just extremely complicated. And to make things worse, we have a ton of additional functionality in Active Record and lots of other ORMs in Ruby. So, the way we do it is that we add even more complexity to something that is already extremely complicated. And [chuckles] what I learned about all those tiny little problems that must be solved, I just realized that it’s just too much. And I don’t want to build software based on something that’s so complicated.
So, I just decided to do something that’s way simpler than that. And I realized that the most important thing for me is the actual data. And I want to really focus on that part. I just want something that makes it easy and efficient to get the data from the database and just load it into memory. And when I want to change something, I want something that’s separate from the reading, because that’s just a separate concern. And after time, after two months of thinking about it, I just decided to change ROM direction and rebuild it from scratch. And I decided to follow the command-query responsibility segregation which is about separating reading from writing. And so far, so good. I’m extremely happy with how it evolved. So yeah, that’s in a nutshell I guess.
AVDI:
I’m trying to get my head around this. And I know we don’t have a lot of time and it’s going to be difficult to talk about, just hand-waving anyway. But I really want to understand this better. I know with traditional ORMs in order to read something from the database and then update it and then write it back, we usually have things like dirty tracking to know which fields in which objects were changed. How do you manage that kind of thing? Are you just explicitly writing back new copies of the records that you know you’ve changed or what?
PIOTR:
The way it works in ROM right now is that you have a command interface. Commands work with some input. So, essentially you have three steps. You receive some input. You need to perform some coercions, sanitize it, validate it. And then you just persist it. And if you’re updating an existing record you can provide the original data tuple so that you can compare the input with the original one and decide whether or not some database operation is to be executed. But in ROM you don’t load an object to change it somehow in memory and then get its state and then send it back to the database. It’s just very, very simplified. So, it’s mostly focusing on dealing with the data itself.
AVDI:
Very interesting. How far along would you say you are with this? Do you feel like you’ve realized most of your vision so far? Or are you still getting started on it?
PIOTR:
I’ve built a couple of projects already with ROM. So, I feel like it’s a good direction. But there are some things that should be improved in terms of building queries and mapping more complex structures, especially when it comes to associations. But in general, I’m loving it, to be honest. I’m obviously biased. But so far, so good as I said. It feels really good. And I like the separation. I can clearly see that it simplifies a lot. It makes things more explicit. And also, in ROM itself it’s just easier to work on it and evolve it when things are separated.
I like the data-centric approach. I like that it’s focused so much on the data. The way I’m using it these days is that I just load data into memory and represent them as simple Structs. And then I have some other objects that depend on the data structures and they do some work. So, it feels really good. I don’t have… there are no callbacks, no lifecycle kind of stuff as we have in Active Record. Validation is separated. Data coercion is separated. Mapping is separated. Those are just small little pieces that work together. And the core concept in ROM is that you have a pipeline of data and you could change things, I mean change [inaudible] and map things in individual steps. And it’s just easy to do it in ROM.
So, when you have some relation it can send data through multiple mappers and decorate the data the way you want. I think that it’s also easier to do DCI with ROM, because it’s just so easy to decorate data with objects that are supposed to be used in individual contexts. So for example, if you’re rendering something you can get the data from a relation and decorate it with some behavior that is needed for the rendering part. You can easily create presenters. You can easily create some objects that know what templates to render. Like one thing that I tried very simply in a Rails app was that I completely stopped using helpers to render views, to render HTML pieces. And I started using templates for everything. So, I basically load data and I decorate it with objects that know how to render individual elements of the user interface. And I absolutely loved it. I removed an entire layer from my app which is helpers, and I hate helpers. So, that felt really great.
I think there are lots of things that you can do just differently once you focus on fetching data efficiently. And depending on very specific data structures as opposed to trying to design some abstract object that would, I don’t know, be similar to the real world. Because it just doesn’t work like that. So, I’m trying to focus much more on data and data processing, and simple decoration that is used in individual context. So for example, in ROM you wouldn’t have a single user object. You would have, I don’t know, a user renderer for the user interface or some JSON serializer. So yeah, it’s just a different approach. I love it. Seriously. I think it’s really refreshing and it opens up possibilities like lots of things can be done differently and more efficiently.
AVDI:
Are there any projects, open source projects, where people can see examples of this in action?
PIOTR:
I’m not sure, to be honest. I have a simple ROM demo in my account. To be honest, I don’t know. [Chuckles] I would have to ask people. But the community is growing. More and more people are using it. So, I guess if we ask around, we would probably find something. But I know that people are starting using ROM in their projects at work. So, I think it works for more than just me. [Chuckles]
AVDI:
Obviously we’ll include a link to the ROM homepage in the show notes. Are there any other resources that people should know about if they want to get started with this?
PIOTR:
I think the website, the main ROM website is a good place to start. We have a couple of short articles as an introduction to the project, just because it’s just so different from anything else that we have. So, we put together some articles explaining the philosophy behind the project. And yeah, the most difficult part is to explain that the Ruby Object Mapper is not an ORM. [Chuckles] Which I’m still trying to explain clearly. But it’s just difficult.
DAVID:
That’s awesome. Okay, well I guess it’s the point where we segue into picks. I don’t have anything else. Avdi or Piotr, do you have anything cool you’re working on that you want to pitch?
PIOTR:
I think Avdi mentioned that we should mention also Transproc which is the data mapping backend for ROM.
DAVID:
Yeah.
PIOTR:
Right, Avdi?
AVDI:
Yeah, yeah. Let’s… we didn’t really have time to talk about it. But let’s definitely include that in the show notes at least.
PIOTR:
Oh, okay.
DAVID:
Can you give a quick 30 second summary of Transproc?
PIOTR:
Oh yeah, it’s a library that is the actual data mapping backend for ROM, which is written in a less standard way, I should say. It’s more functional. So, you basically compose a transformation function outside of the actual data context. So, it’s separated from the actual data that will be used. And you can compose a pretty complicated data transformation and then call it with some input.
And it will just do its job. And it’s used in ROM as one of the backends. Right now it’s the only one. But you could implement your own if you want.
DAVID:
Right on. Very cool. Well, Piotr, thank you very much for being here. This was a lot of fun. Let’s jump into picks. Avdi, do you want to do picks?
AVDI:
Sure. Well, so I have completed my move a month or so ago to the beautiful state of Tennessee. And I’m beginning to dig into the local brewing scene. And local, semi-local, generally checking out the southern, south-eastern beers. One of them that particularly stuck out to me recently is the Road Trip beer from SweetWater Brewing Company. It is, I think they call it an India Pale Pilsner, which is one of those weird combos. But it actually really, really works. Great summer beer if you like [hoppier] beers. I’ve been enjoying it a lot.
What else? Oh, here’s a silly little one. For years and years, I think along with everyone else I used the standard little jaws staple remover whenever I wanted to remove staples from papers.
Everybody’s got one of those little jaws that you grab the staple with and you yank it out.
DAVID:
Yeah.
AVDI:
And occasionally terrorize young children with. But as I’ve been getting more serious about having a paperless or at least limited paper office, I realized it was time to see if staple remover technology had progressed at all in the last 20 years. And it turns out it has. You can get these little push style staple removers. I’ve got a BOSTITCH push style staple remover. And it works way better than the little grabbers. Hard to describe over the air, but basically you slide it under the staple and you push forward and it just comes right out. And this one is magnetized as well so the staple usually sticks to it instead of going flying off into the wilderness. And it works really well, very efficient. Also doesn’t seem to tear up the paper as much. So, I don’t know about you. Office supplies really excite me. They make me happy.
DAVID:
[Chuckles]
AVDI:
And this one [chuckles] this one along with my stapler collection is making my office life a little easier. So, I think that about covers it for me.
DAVID:
I’m going to invent a service that lets you submit a pdf or an eBook and it will remove the staples from it and send it back to you.
AVDI: [Laughs]
DAVID:
It’s like electronic staple removal. I think that would be pretty good. I guess it’s just the two of us today. And so, I’ll do some picks real quick. And I’ve had a chance to practice these picks because last week we messed up the recording about the time we got to picks. And we lost all of the picks.
And so, if you listened to last week’s show you’ll notice that we did actually get picks from Dave. But that’s because he graciously agreed to jump back on a new call and re-record his picks. And the rest of the Rogues didn’t re-record theirs. So, just real quick my picks from last week, these are stale reused picks but you didn’t get them. So, they’re new to you. So, why not? Actually, I’ll just do the two most important ones that I think are in here.
The first one is Avdi picked the Evoluent ergonomic mouse a couple of weeks ago or a couple of months ago rather. And I like that mouse. In fact, I have two of them. I have a right-handed one and a left-handed one so that I can switch off when my elbow starts to hurt. And I found that I had some problems with it. Notably, I kept clicking on the back and forward buttons. So, I literally dismantled the mouse and removed those buttons from the mouse so that I would stop doing that. But the other problem that I had with it is that it’s a three-button mouse. And the third button goes all the way to the bottom.
By the way, if you’re not familiar with these and you’re listening along, these are vertical mice. You hold your hand out like you’re shaking hands with somebody. And that’s the position, because the mouse is turned up on its side. And so, you just move your hand around vertically the whole time that you’re using it. And for me, holding onto the Evoluent mouse is a problem. Because when I go to squeeze it I end up right-clicking. I end up clicking the third button on the mouse. And that was a problem for me.
And so, I went looking for a mouse that had dead space down on the bottom edge so that I could actually hang onto it. And Avdi and I had a great comedic back and forth because I mentioned that I had the Evoluent that he had picked. And I picked this mouse which is the Anker ergonomic mouse. And Avdi then pointed out that he has the Anker mouse as well and he favors the Evoluent over the Anker. They’re both good mice.
AVDI:
Mouse fight!
DAVID:
Yeah, mouse fight!
AVDI:
Mouse fight!
DAVID:
So…
AVDI:
David, didn’t you learn anything about squeezing mice from the book ‘Of Mice and Men’?
DAVID:
I love them and I pet them and I call them George.
AVDI: [Chuckles]
DAVID:
And yeah, you can be taught. I cannot. I need a mouse that can be [inaudible]. So, this is why all of my toys are big and brightly colored. So anyway, [laughs] the Anker mouse is, it’s just a two-button mouse. It does actually have, for third button you have to click the mouse wheel. It’s like that old style. But the place where the third button would be is just dead mouse area. So, I can actually hang onto the Anker mouse. It’s a lot more light-weight. It’s cheaper than the Evoluent. And by cheaper, I mean less expensive. It does feel like a really good solid high-quality mouse. And I really like it.
It comes in a Bluetooth and a wired form. And my wired mouse showed up the day after we recorded last week. And so, I’ve had six days to play with it. And I love it way better than the Bluetooth. The Bluetooth mouse sometimes skips. The transponder doesn’t quite keep up all the time. And the wired mouse is just rock-solid. So, the Anker ergonomic mouse. We’ll put a link to Amazon in the show notes. And I absolutely love it. I think it’s fantastic.
The other pick that I have is the Kickstarter page which will be going on for about another week and a half to two weeks at the time this show goes to air, for the Schlock Mercenary Planet Mercenary role-playing game. So, years ago I picked, I think on the very first show that I was on which actually was episode one, I picked SchlockMercenary.com which is a web comic space opera sci-fi pseudo-military just space romp. If you like spaceships and running gun battles and a punchline every single day, Howard Taylor’s been drawing this comic since 2000. And the art back then was awful and now it’s absolutely gorgeous because he learned to draw in the past decade and a half. And his writing has just gotten… was amazing to begin with and now it’s even better.
And people have begged him for years to do a role-playing game. And he finally got with a group of people that do role-playing games professionally. And they said, well it will take this much money to really do a proper role-playing game. And he said, fine, let’s do a Kickstarter. And they did a Kickstarter. And the good news is it funded in less than 24 hours. And they are now setting stretch goals that are 5 and 10 times the amount of funding level that they originally needed. And it looks like they’re going to hit all of these goals. But if you want to get in on the goodies, you need to go to the page and sign up.
So, if you’re not reading Schlock Mercenary, you need to be reading Schlock Mercenary. If you do read Schlock Mercenary, you’ll be happy to know that there is a role-playing game coming out for it. And I’m really excited for it. And it’s coming from a third-party company called Planet Mercenary. And you can get to that at bit.ly/PM-RPG in all caps, so capital P, capital M, et cetera, dash RPG. And we’ll put a link to that in the show notes. There’s going to be a Kickstarter going on for another month and a half or so.
And the goodies that are being included as the stretch goals end at the higher level stuff are the kind of stuff that you would really expect from creators who really like their fans, like getting to hang out and talk with the creators. And Howard has basically offered to write an entire book if it reaches, he’s going to write the actual Maxims book that people quote in the comic strip all the time. So, this is all inside noise if you’re not a follower of the comics, so I apologize. But if you do follow the comic you know what I’m talking about and it’s very, very exciting. So, the Planet Mercenary RPG Kickstarter, that’s my other pick.
I was going to do four picks, but we don’t have another 45 minutes. So, I will just cut myself off here, which I should have done 5 minutes ago. So, Piotr, you said at the top of the show that you didn’t have any picks. Have you changed you mind? Do you have any picks for us?
PIOTR:
Yeah, actually I do have one thing that I really want to mention. It’s a service that you can use to record you terminal sessions. It’s called, I think you pronounce it as ascii-cinema, asciinema. I don’t really know to be honest, which is funny. But you can maybe help me with pronunciation. Anyway, it’s a really nice service. I’ve used it a couple of times to record my terminal sessions when I was programming. I think RSpec is using it on their website. It’s really nice, really easy to use. I’m definitely recommending it. So, that’s my pick for today.
DAVID:
Awesome. Very cool. Well, thank you guys. This was a fun show. It was nice and intimate, just the three of us just hanging out and shooting the breeze.
PIOTR:
[Chuckles]
DAVID:
But that blogpost you wrote gave us a lot of meat to talk about. I really appreciate you coming on the show.
AVDI:
Yeah. Thanks a lot.
[This episode is sponsored by MadGlory. You’ve been building software for a long time and sometimes it’s get a little overwhelming. Work piles up, hiring sucks, and it’s hard to get projects out the door. Check out MadGlory. They’re a small shop with experience shipping big products. They’re smart, dedicated, will augment your team and work as hard as you do. Find them online at MadGlory.com or on Twitter at MadGlory.]
[Hosting and bandwidth provided by the Blue Box Group. Check them out at Blubox.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.]