Show Notes
02:48 - Jim Gay Introduction
03:43 - Object Design
04:39 - DCI (Data, Context, Interaction)
07:20 - What Painpoint DCI Aims to Solve
09:31 - Designing From DCI From the Start (Process)
- Levels of Use Cases
11:42 - Object Composition
13:56 - Definitions: Forwarding, Delegation, Consultation, and Inheritance
- Class-Based Inheritance vs Prototype-Based Inheritance
- JavaScript Influence
18:37 - DCI and Service Objects
- Context
24:36 - Roles and Object Factoring
- Authentication
28:49 - One Context in a Single File
30:17 - Coupling and Cohesion
31:37 - Typeclasses
33:09 - DCI Criticism
36:51 - The Current State of DCI (Skepticism & Criticism?)
38:56 - Preventing Reuse
41:18 - When should you not use DCI?
43:45 - Transition: Using/Undoing DCI (Experimentation)
45:04 - Resources
More DCI Blog Posts by Jim
- Delegation Is Everything And Inheritance Does Not Exist
- Chubby Models Are Still Fat With Concerns. DCI Focuses On How Things Work Together
- The Gang Of Four Is Wrong And You Don't Understand Delegation
- Triggering The DCI Context
- OOP, DCI And Ruby - What Your System Is Vs. What Your System Does
- 4 Simple Steps - Extending Ruby Objects - The Tip Of The Iceberg With DCI
Picks
Richard Hamming: You and Your Research (Jessica)
Martin Fowler: Yagni (Coraline)
Ruby Monday (Saron)
JunkFill (Saron)
Wappalyzer (Saron)
WhatFont (Saron)
Julian Feliciano: What Is Source Control? (Saron)
Bodum Santos Stovetop Glass Vacuum 34-Ounce Coffee Maker (Avdi)
The Master and His Emissary: The Divided Brain and the Making of the Western World by Iain McGilchrist (Jim)
request_store_rails (Jim)
littleBits (Jim)
Martin Fowler: Yagni (Coraline)
Ruby Monday (Saron)
JunkFill (Saron)
Wappalyzer (Saron)
WhatFont (Saron)
Julian Feliciano: What Is Source Control? (Saron)
Bodum Santos Stovetop Glass Vacuum 34-Ounce Coffee Maker (Avdi)
The Master and His Emissary: The Divided Brain and the Making of the Western World by Iain McGilchrist (Jim)
request_store_rails (Jim)
littleBits (Jim)
Special Guest: Jim Gay.
Transcript
AVDI:
Jim, as you can tell, we are always a little discombobulated when Chuck is not around.
JIM:
[Chuckles] Well, I'm in for the ride regardless.
[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.]
[This episode is brought to you by Braintree. If you’re a developer or manager of a mobile app and searching for the right payments API, check out Braintree. Braintree’s new v.zero SDK makes it easy to support multiple mobile payment types with one simple integration. To learn more and to try out their sandbox, go to BrainTreePayments.com/RubyRogues.]
AVDI:
This is the Ruby Rogues Podcast, episode number [mumbles inaudibly]. [Laughter]
AVDI:
On our panel today, we have Coraline Ada Ehmke.
CORALINE:
Good morning.
AVDI:
Jessica Kerr.
JESSICA:
Good morning.
AVDI:
Saron Yitbarek.
SARON:
Hey, everybody.
AVDI:
And special guest, Jim Gay. Alright, so Jim, why don't you tell us briefly a little bit about yourself?
JIM:
I live in Arlington, Virginia right outside of DC. I like to tell people that DC is probably Arlington's biggest and most well-known suburb. I've got a wife and four kids. And I haven't really done much graphic design as of late but I have a graphic design background. And I got into doing Ruby programming via JavaScript, ActionScript, and Flash stuff, and then dabbled with PHP and building stuff on Postgres databases. And after getting frustrated with that, when Rails was nearing or was around 1.0 I started looking into Ruby on Rails and started getting into doing more backend stuff and less of the frontend work. And really haven't touched a lot of frontend in a good long time.
AVDI:
I get the impression that since that time you've done a lot of thinking about object design.
JIM:
I have. So, I wrote a book called 'Clean Ruby' which comes out of experience that I had on one particular project that was frustrating and had a lot of changes and was big and complicated. It was a great team that I worked with, but it was a client that constantly changed its mind about what the system should do. And at the time, I wasn't really well-versed in SOLID principles and what all the acronyms were and all that stuff. But I started... I honestly don't actually remember where I started hearing about DCI. But I heard about it somewhere, read what the goals of it were basically in order to make your business use cases directly reflected in your code. And that's exactly what I needed. So, I just started looking into it and reading it. And it took me quite a long time to really understand it and get my head around the idea.
CORALINE:
You should talk a little bit more about what DCI is and what it stands for.
JIM:
Sure. It stands for Data, Context, and Interaction. In fact there was an earlier version of it. It was like DCA. And I can't remember. It might have been algorithm or something like that, was that A letter. But Trygve Reenskaug who created the MVC pattern also came up with this DCI idea, him and Jim Coplien have been then ones who have been exploring it and talking about it and trying to figure out what it is and isn't. And there's a website called FullOO.info which tries to gather a lot of the resources that are out there about people exploring this idea.
But to explain what it is, you have objects in your system that represent some view of what the world is. So, something in the world inside your computer has information about it. And DCI is designed to take those data object and put them into a context reflecting some particular use case, the way they are used. And the interaction is how multiple objects interact together. So, to break that down, if you look at for example, I don't know, a user class that you have in your Rails application, Devise needs to hook into it and you add a bunch of stuff for authenticating to user, that authentication only happens at one point in the system when the user is logging in. Or your user is becoming friends with another user to make a friend request or something like that. You might add methods to that user class.
And over time it gets bigger and bigger. And you decide, “Well maybe I’ll move these things into modules to clean it out, or concerns.” But the user class still has all the stuff in it. And putting your system together like that means you’re adding behavior to objects because at some point in time they might need that behavior. What the DCI structure says is, “Well, let’s make a context object. And its entire goal is to encapsulate the behaviors for the different roles that objects will play.” So, when I have two or more objects that have to come together and do things, they enter into this context. And then that’s where the behavior is defined. And that’s where they gain the behavior. And after, whenever they’re outside of that context, they don’t have it.
So, you don’t just throw things into your user class and it gets bigger and bigger as you need more features in your system. You actually implement the features as cohesive units. And when the objects come in, they gain the ability to do whatever they need to do.
AVDI:
Jim, can you boil down the basic problem or pain point that DCI is seeking to address?
JIM:
So, I don’t remember the quote. But I know in the ‘Gang of Four’ book it mentioned something about how we’ll never be able to represent the runtime structure of the interactions of objects in our system. And I can’t remember if it’s Trygve or Coplien who in discussion, particularly in the mailing list… sorry, there’s an object composition mailing list where people are talking about, it’s a Google group, where people are talking about DCI and what does it mean? How do you implement it? What are the semantics of it? Anyway, so in there, they’re discussing this idea that why can we not represent that object in a relationship graph in our code?
And the problem that I had when I was working on this system that I had several years ago is a business analyst would come in and say, “Hey, that feature that we implemented two sprints ago, I know I said we need it to work this way. But the client decided that’s actually not correct. We need it to happen this way.” And this conversation happened with me. I said, “Okay, well show me what it is you need.” And so, we talked about it. I went and I looked through our Rails application and I found this class of thing which had some bits and pieces of what needed to happen. And then it was related to another class. And then there might have been a method call that went out to another one And I had to pull up all these files, read half a dozen methods in each different one and try to piece it together in my head.
And my best and fastest response at the time was, “Go and talk to this person because he implemented it. I can’t quite figure that out right now.” we were under the gun for churning out features quickly. And it was a terrible thing to say, “Just go and talk to that other developer,” but it was the easiest thing. And he would be able to recall more of the context around ‘How does this work?’ rather than me piecing it together in my head. So, the idea is let’s build ourselves something so that we can just look and say, “Well, here are all the pieces you need. This is your recipe for baking this cake. Just put all this stuff together and that’s what you need. You don’t have to go grabbing it from all over the system.”
CORALINE:
How easy is it to design from DCI from the start and what does that process look like?
JIM:
That’s a good question. I don’t know. I find it a little bit more difficult to get going because when I get started with it, I feel like I tend to do a lot more thinking about the implications of when this behavior or when this feature is actually really necessary. So, Alistair Cockburn wrote a book called ‘Writing Effective Use Cases’ and one of the things he talks about in that book is different levels of use cases. You might have a very high-level use case where you care generally about what happens in the system and you might talk to business owners about it. But then you might have a really low-level thing where you care about persistence layer and how things interact.
And so, as I start looking at the use case that I have, I start finding out, “Well actually, I’ve got a
couple of different features here and there are different layers of the system.” And trying to figure out what piece needs to go where and what should be used by the other one is to me really different than a traditional way that I’ve done it in the past where you just make a class of a thing and it has all its behavior with it. And then if you need another feature, you make another class.
So, I’m not sure if that really answers the question. But I tend to find that I have to do a lot more thinking upfront. And sometimes what I’ll do is I’ll just bang out, well, here’s the typical way I would do it with just a couple of classes and see what happens. And then I look back and I see, well actually, I don’t need this method everywhere. I only need it in this case. And so, I can then build a context object for myself that just accepts a couple of arguments and assigns behaviors to each of the objects that are playing the roles that I define.
The thing that I like about that and that I feel gives me a lot of freedom is it is like my own little sandbox. I have this feature and I just want these things to happen here. And I don’t want to introduce new behavior to a class of an object that someone else might be using, because they might use it in a way that I didn’t intend. And that could be serendipitous for them, but it also could be bad especially if I decide to make changes and I’m unaware of the dependency on this other object.
AVDI:
Yeah, I could see that. Before we get too much further into the discussion, I want to talk about some terms. I was doing a little reading up on some of the things you’ve written about object design and object composition. And you get into some interesting discussions of what various terms that we toss around mean what they were originally introduced to mean. And I feel like if we’re going to be talking about some of this stuff, we should really have our definitions in order. So, I wonder if I could just, I want to go through a few different terms with you and just get a really quick definition from you, if that’s okay.
JIM:
Sure.
AVDI:
Alright, so let’s start with composition, object composition. What is that?
JIM:
[Chuckles] That’s a really good question. I don’t even know if I know the answer to that. But I don’t think I have…
AVDI:
Well, you’re on a mailing list about it. [Laughter]
JIM:
It’s just the name of the list.
[Laughter]
JIM:
So, on the mailing list they talk about the structure that makes up an object. So, I’m not sure, I’m not really sure what a good answer for what object composition is other than the [chuckles] bits and pieces if there are any that we consider that what makes up an object. It makes me think of whenever I talk about this idea that you add behavior to an object when it’s necessary, people often think of the Single Responsibility Principle. And if I just make a class that has all that stuff, then I’m following the Single Responsibility Principle. But I don’t think there’s necessarily anything that says that an object must only every do the one thing that it can do. It might still behave in a certain way in a certain scenario. But it doesn’t have to be tied to its type.
AVDI:
When I think about composition, when I think of that term, I usually just think in terms of an object owns another object that helps it to accomplish its job. Is that…?
JIM:
I suppose so. Like if you think about class inheritance versus prototype based inheritance…
AVDI:
We’ll get to that in a minute. [Laughter]
JIM:
Well, I think of it in terms of maybe the class of an object is sort of an attribute that it maintains information there. That’s how it has certain bits of behavior. And that’s one way of composing an object. Another way is it holding onto something else that it treats as a prototype and just uses that other object as how it handles its behavior.
AVDI:
Let me get another definition from you. How about forwarding? Real quick.
JIM:
Forwarding is a method call.
AVDI:
Okay, so one object…
JIM:
Or passing…
AVDI:
Calls a method on another object. Okay. Now, how about delegation?
JIM:
Delegation is using behavior from or defined in another location I guess in another method while maintaining the reference of ‘self’ as the object receiving the original message.
AVDI:
And contrast that with consultation.
JIM:
So, this idea of consultation actually seems to be erased from the web. I’ve only seen it in… I think there was a Darwin project. Gosh, I can’t even remember his name now, or somebody wrote about a thesis on this project. And he introduced this idea of consultation which is essentially just forwarding. What we often call the delegation pattern is when you send a message to one object and it just passes it along to the next one.
And so, in ‘Clean Ruby’ I tried to identify that and say, “Well, I think that maybe consultation is just forwarding a message on without making any changes to it.” So, if I say ‘to_s’ on one method and it passes it down to some other object that it holds onto, it could just take the result of ‘to_s’ and return it back to me. I would say that maybe that’s consultation. Whereas if I have ‘to_s’ and I pass it onto another object, call ‘to_s’ on that and then I process that string that I get back and return it, then that’s something different.
AVDI:
Okay, alright. Now, just to wrap this list of definitions, would you say that inheritance is just one kind of delegation?
JIM:
Yeah.
AVDI:
There’s only one ‘self’. It’s not like two different objects that each has their own ‘self’.
JIM:
Yeah, and I think often people tend to mistake inheritance for class-based inheritance rather than inheritance being this overarching concept for both class-based and prototype-based inheritance. But basically, when you define a method in a class in Ruby you just say ‘def’ whatever the method name is and then ‘self’ inside that method. It’s not the class. It’s whatever object receives the message. It will delegate to its class and say, “Hey, give me the behavior that you have defined for this message and for your ‘self’ reference, use me.”
CORALINE:
So, JavaScript is a prototype based inheritance system. How do you think that influenced the way you deal with classes and inheritance in Ruby?
JIM:
You mean in thinking about how JavaScript works?
CORALINE:
And how you applied that to the Ruby code that you write now.
JIM:
Yeah, my understanding of delegation in general is very different. In fact, the whole reason I understand it in the way that I do is because I started writing about delegation and realized I didn’t have a good definition. And so, I started looking for it. And I looked in the ‘Gang of Four’ book. And the referenced work by Henry Lieberman who I believe was the person who originally coined the term. And he was talking about I think it was the Self language. And his definition was that ‘self’ reference is always maintained. So, if you have two objects, one object passing a message to another, forwarding a message to another, it’s delegation when ‘self’ remains that original receiver.
And so, it wasn’t until Ruby 2.0 came out that you could really do this easily but you can take a method off of a module and apply it to whatever arbitrary object that you want. So, I could treat, like you can in JavaScript, you can take a bunch of functions and just say, “Apply this function to this object.” And using for example the call function I can say, “Well, this is the object reference that you should use for the keyword ‘this’ inside of the function.” And in Ruby, you can just unbind an instance method from some module and bind it to another object and hit call. And ‘self’ that is inside that method definition just like when you use ‘self’ inside the methods defined on the class, it will remain the receiver of the message to the object that you bound that [inaudible] call to.
SARON:
It sounds like delegation is like a specific type of forwarding. Is that right?
JIM:
Exactly, yeah. So, any message that you send is forwarding. But delegation always preserves ‘self’ to be the object that received the message.
JESSICA:
All of this really makes me appreciate functional programming where there’s no confusion about ‘self’ or ‘this’. If you want such a thing, you just pass it in like everything else.
JIM:
Right.
CORALINE:
So, I have a question about DCI, if we can return to DCI for a second. Is it a philosophically different approach than service objects? Is it trying to solve the same kind of problem?
JIM:
I think so. Generally, probably because I’ve been spending a lot of time thinking about DCI, when I see people talking about service objects I’m really not crazy about them because, not to use the same exact term, I feel like you lose a lot of context. When I see someone’s bare service object, I
don’t really understand all of the different aspects of why and how it’s being used. I think even in the people…
So, some of the people who had been interested in DCI when they’ve heard me point out things about it they’ve been surprised in that you don’t necessarily want to just take some piece of code and have it implement one half or one small portion of some use case, right? I’ve seen a lot of people do things like, gosh I don’t know, create post. And you just basically have taken a single function and turned it into a class that has a constructor with some arguments. What I found to be really helpful is to take all of the information around whether or not you can create a post given this current user, or what happens if you’ve hit your limit for the number of posts you’re allowed to make, or what happens if there’s a failure. And I want all of that stuff to happen inside of the context object.
So, when I think about, going back to Alistair Cockburn’s book, he talks in there about you have the success paths through the system. But then you have alternate paths. And alternate paths may also be considered successes or they may be failures. But there are all kinds of things that can happen. And I want to be able to look in one place for what happens in the system. If I’m implementing this feature and somebody comes and says to me, “Hey, it’s going to work a little differently,” I want to be able to pull something up and say, “Well, here’s how it currently works. What should be different?” And I should be able to see all of the paths through that feature in that single place.
So, I kind of claim ignorance about service objects because I haven’t really thought much about them. But a context object, it really, DCI has more about the construction of that object and its internal composition than it does with taking a function and giving it a class name or giving it a namespace. Any object in your system could be a context. And from the outside, you don’t really need to know. You just need to know, what do I have to give it to get it going and what’s the public API on it that I can tell it what to do? And so, even when I throw objects into a context and I want to give them their abilities, all those abilities remain private to that context. It’s just, it’s like private methods are instance variables. You don’t want that information to leak out. You just want your public APIs.
CORALINE:
So, with service objects the context is pretty much defined by the caller, by passing value objects into the service object. How is the context object actually modeled in DCI? Is it a distinct object? Or is it just inferred from the state of whatever ‘self’ is?
JIM:
It is a distinct object. So for example, the traditional example of objects interacting in DCI parlance is this account transfer where you choose once bank account and you’re going to transfer money to another. And when you assign them their roles, they have the ability to do those things. But even at a lower level, an account is not a number in a database. It is a calculation of multiple transactions to and from some identified account number.
And so, you can look at the internal structure of your account object as a bunch of objects interacting. From the outside you don’t treat it that way. You just say, “Get me the account for this account number and what’s its current total?” And that’s really all you care about when you’re moving money from one to another. And so, there are different levels I can model this transfer which has a lot to do with who the current user is triggering the transfer, what accounts they’ve selected. And then at a lower level I can say, but those accounts themselves internally are a collection of objects interacting.
AVDI:
I feel like the relationship between DCI and service objects is that they’re both taking on the notion of context. They’re both saying, “Okay, we can’t put all the responsibilities in all the objects all the time.” There are these contexts of interactions and there’s a lot of logic that we don’t need outside of those contexts. The difference to me seems like the service object approach basically just punts on OO. It says, “OO is hard. Let’s revert to procedural programming.” And from what I’ve seen of the DCI approach, it’s saying more, “Let’s preserve the fundamental idea of roles.”
JIM:
Yeah.
AVDI:
“But let’s apply the roles just in the context of a particular interaction rather than trying to have all the objects have all the roles all of the time.”
JIM:
Right, I think so. And so, having really not worked with what examples I see of people calling service objects, I think, I see taking some procedure, like these three things have to happen, and sticking it in a function somewhere. But all of the things that you need for that to actually happen, like you’re changing some value in a database and so you use a setter to set some value, all that still exists outside of that service object. And so, to me it’s just like a place to sweep up your piles of stuff, which I think can be good. But you still have the ability to do the same type of thing externally. And you have to be, I would guess, very careful about making sure that everybody uses the service object in the way that you expect and not jumping around and just using the same exact features they have but rebuilding the procedure somewhere else.
AVDI:
Jumping off of the idea of DCI as being a way of preserving these roles, let’s make things a little more concrete. Let’s say we’re talking about the account transfer scenario. And so, you have account and an account is a huge thing. There are many different ways of looking at accounts, many different ways of interacting with them. But in this particular context, one account is the money source, is playing the money source role I guess. And another account is playing a money destination role. Is that fair so far?
JIM:
Sure.
AVDI:
Okay. So, I’m curious, your thoughts on… there are a few different approaches to this scenario. And one of the tried and true approaches is just rather than doing anything with delegation we will break down these roles into their own objects. But we’ll basically just say, okay, there’s a money source object, money source class.” Either it has a reference to the account or it knows just enough about the account, just what it needs for that interaction. Maybe it just knows the account balance or something.
And then you have the money destination. And it is a separate object which just has a reference to the account and then has the logic for receiving money. But not doing any kind of mixing in, not saying, okay, let’s temporarily extend the account with money source semantics or extend an account with money destination semantics. We’ll just make a new object which references the account and have those specific context, specific semantics. What would you say is the advantage of the DCI approach over that just traditional object factoring?
JIM:
I suppose it has more to do with the lifetime of the object. If you’re working with a system where that new object that you create has made some change, recorded a new transaction, the old object that might be hanging around won’t have a record of it or you have to do something to reify (a word that I hate, [chuckles] I’m just throwing a programming term) that information back into the account that it was before. And I would think like nil checking, you’d have to do that all over the place and make sure this thing is up to date with all the current information about it, whereas if it just remained the same object, then it would always be aware of its current state.
AVDI:
So, are you saying that a role might be temporary but it might leave around some sort of record of its existence for the next time around?
JIM:
I don’t know. I personally haven’t run into a scenario where I have that. I’ll take a different example. One of the things that I had been thinking about for a while that I only just started playing around with is the idea that when I authenticate with a system the user information and the authentication details are totally separate. And so, when the person is attempting to authenticate and login, get into some information, there’s a context object that I create that is this authenticated user. And all those pieces interact.
So, I have an email or a username and password and then some user object that I’ve looked up with one or the other. And the way they interact has everything to do with just authenticating. So, if I put that in a single place, then I understand the entire authentication system. And I don’t have to know that there’s a storage mechanism on user to update some encrypted token. I can split that out. So, I’m starting to experiment with that. When you come in, I’ll make an authenticated user. And if everything works great, then I’ll pass along the user as the current user for the system. But if not, then I can see where my failure passed through what happens in authentication.
So, I’m not sure. But the case of having information left around, it probably depends on if you’re working on like a Rails CRUD app where you’re actually going to be making updates to the database based upon some feature that you’re implementing. If that’s the case, then maybe it would be fine to make a secondary object and copy attributes over or record information from one to another. I haven’t played with anything like that.
AVDI:
A quick follow-up on that, just from a purely physical implementation perspective. Are you putting all of the things that have to do with one context in a single file?
JIM:
Yes. Yeah. In fact, when I first experimented with this I would take all of the role and I would make an app roles directory in Rails. And I found that that made it still just as difficult for me to understand than having it, actually put these things in classes. So, what I do, and I built a gem that I use often called Surrounded that helps me take all this information and put it in that single file. But the behaviors that I define are private constants. So, if I create a module or a class that represents some behavior that some object’s going to have, it cannot be used outside of that.
By making it private constant, no other developer can say, “Oh, I like the way that method’s done. I will clearly need to take that information or take that behavior and use it over here in this part of the system.” And by doing that, it forces you to reconsider the structure of your system and say, “Well maybe this feature needs to actually call that feature.” Or maybe the one, the use case that I’ve created can actually break down into two different levels. But you’re forced to put the behavior you need in one place and never reach in from the outside to share it.
JESSICA:
It sounds like a lot of this DCI and the upfront thinking that it leads to that’s usually a win, it sounds like the objective is to group code that belongs together.
JIM:
Yeah. So for me, when I think about coupling and cohesion, the cohesive bits of your system are the things that belong together. And if I can take anything that is cohesive to a feature and just stick it in the same place and prevent myself or anybody else from taking it out of that location and using it in another place, then it’s far easier for me to understand. I might have maybe incidental duplication in different areas where I do similar things. But I can look at entire, this single context, and say, well, this is everything that happens. And all that I need to know is here. I might have to know some information about what objects or what types of objects can be passed in. But beyond that, everything that I need to know, all the cohesive behaviors, are tied to the use case rather than tied to some class of a thing that might potentially need to do this at some point in the system.
JESSICA:
That makes a lot of sense. So, you’re grouping behaviors not according to what they act on but according to why they happen.
JIM:
Exactly, yeah.
JESSICA:
Do you, have you ever looked at Haskell or Scala? And they have a concept called type classes
JESSICA:
Which is a completely horrible name.
JIM:
[Laughs]
JESSIC:
Because type classes are like methods that you… they’re not methods, they’re functions of course. But you add them around a type later. So, there’s a little bit of overlap in the motivation there of type classes are where you add behavior about a particular class but it doesn’t have to be in scope all the time. You can use it only for your particular purpose.
JIM:
I haven’t seen that. But to me, that I think is indicative of people trying to solve the same problem. If we are having trouble understanding a system, we’ll probably be better off by managing locality of the features. And I know every time I’ve gone down the path of thinking, “I’ll probably need this later,” and sticking it somewhere where I could use it later, it actually ended up being problematic.
I’d rather… now I will, for example if I’m just hacking something out quickly, I just need a collection of information. Let me use a Struct. I’ll define it right there exactly where I need it rather than saying, “Oh, let me go put this in a new class because someone else might need this.” I would rather look at a problem elsewhere in the system and say, you know that feature I had over there, I think I need that same behavior. Can I use what already exists and just call the same use case? Or is there a lower level one that needs to be broken out?
CORALINE:
So Jim, DCI really came under fire when it first came to the attention of the Rails community a few years ago. Why do you think that was? And what were some of the criticisms that were leveled against the idea?
JIM:
To me, there are two things that people didn’t like about this idea. And one is that when you take an instance of some class, an object, and you call extend on it and give it a module, it changes a singleton class of the object, includes the module there, and you get all of the features. So, a
method that you’ve defined in a module is now available only on that individual object. And it doesn’t affect the rest of any other instances. So, it’s different from saying class user include this module where every single object that you initialize will have all those behaviors.
The problem with that was and is, in certain versions of Ruby it would clear the entire method cache. So, I don’t know if it was 1.8 or 1.9. I think 1.9 maybe fixed it in some small cases. But Ruby 2.0 or 2.1 it got better and better to the point where if you extend an object it only clears the class of that object, the method cache for the class of that object, and then any descendant classes. So, the problem is with that approach you may take an object, extend it, and it blows away the method cache. And other parts of your system may be affected in terms of the performance because they have to then go rebuild that method cache after that extend happens. At the call side of where you do extend, you know about it there. But elsewhere in the system, it may happen.
And you might have a performance impact because somewhere else in some other algorithm, the cache was cleared. So, that’s problematic. I have personally never had that problem. I’ve used that approach in some production applications and that has not been a bottleneck. But it is something to be considerate of. As Ruby development has gotten better not only has the language gotten faster but then they’ve done great work minimizing the impact of doing something like that.
So, that I think, people saw some criticisms of it and said, “Oh well, I’m not going to touch that if that’s the only way to do it.” Ultimately, the goal was to say, well this object needs these behaviors and I want ‘self’ to remain ‘self’. And extend is a really simple way of doing that. I wrote a gem calls Casting which basically takes, allows you to do it, but will take a method, uses method_missing, and binds it to the object that you want and acts the same way.
The other reason I think people don’t like it is you mentioned the concept of adding behavior to an object after it’s been initialized. And people, I’ve talked to many people and their gut reaction is, “I don’t like that.” I’ve pushed each time to try to get some reasoning behind it. And often, the answer was just, “I just don’t like that. It seems weird.” So, that I think is tough to swallow if you think when you initialize an object it should always have whatever it wants.
So for me, actually it was really a nice moment. A friend of mine was kind of against the idea for a while and gave me a hard time about it. Eventually, when I think he read my ‘Clean Ruby’ book he started experimenting with the idea. And he had this moment where he said, “Now I get it. This is a
usability thing.” And it absolutely is. If you can take the pieces that you care about and they’re always in the same place right where you need them when you need them, it’s so much easier to understand what the requirements are for implementing some feature.
SARON:
So, what is the current state of DCI? Are people more receptive to it? Are they opening up? Or is there still that level of skepticism and criticism?
JIM:
Good question. I think in general, I’m seeing some people say for example on Twitter, “Remember when DCI was popular? Does anyone even use that?”
SARON:
Aww.
JIM:
[Chuckles] I know that there…
SARON:
Sorry. [Chuckles]
JIM:
Well, it’s not… so, I don’t have any personal attachment to it. If people don’t want to use it, that’s fine. I think there are benefits there that are important to consider. It’s not a technique for programming’s sake. It’s actually there to help work with your brain. So, I use it often. But I’ll use it on client projects. And a lot of people want to see more open source things. And that’s kind of hard if you’re implementing a business process, say, “Ooh, I’m going to open source this entire piece.”
So, over time I have plans to try to reveal more things that I’ve done or try to make some more generic stuff. I wrote a library, I think I mentioned before, called Surrounded which has a lot of information about not only how to write your use cases in this way by extending objects with modules but also by using the Casting gem that I wrote. Or by creating your own classes or using simple delegators. So, it allows you to try all those different things, see, is there an impact in one over the other? And should we use one and write it in a different way? So, I’ve been able to make a generic tool for myself to just throw things together.
So, I don’t think a lot of people are talking much about it publicly. I’m not really sure. I know some people use it. Other people are starting to look at other ways of breaking things out. You know, I hear a lot more about CQRS and that type of thing, or domain-driven design. And I have not read the book on domain-driven design. But people will often ask to say, “Is that similar to this?” As far as I understand, domain-driven design, you tend to break things up more. And the goal of DCI is to say, “Well, let’s put them together so that we know these pieces are related. These have cohesive structure.”
JESSICA:
I like how you talk about preventing reuse, about keeping the behavior related to this use case in this use case until you find some conscious reason to break it out into a separate context, use case.
JIM:
Yeah.
JESSICA:
Yeah, I think there’s a big difference between writing something for reuse and writing something to be maximally clear about why it’s there and what the business reasons are.
JIM:
Yeah, I think it’s making something that is available, is our tendency. We will make… in Ruby you just make a new file, give the name .rb and it’s there for anybody to use. You just require it and that’s what you need. But for example most of the things that I do when I use Surrounded, I make a context object, pretty much everything is private. And I go through a lot of effort to make sure that objects outside of that system can’t interact with the objects that are inside of it. And they can’t call methods that are inside that context and change the way the context works. When I prevent myself or anybody else or any other object in the system from reaching in and using behaviors, I’ve created my own little world, right?
And this makes me think about Alan Kay’s vision of computers and object-oriented programming and how it should really be an extension of our brains. And if we add complexity by being able to reach from one part of the system all the way across to another and use behaviors without thinking about the impact, it will make me and my brain more confused. But if I can create this biological cell thing or network of interacting computers that Kay talked about when he talked about objectoriented programming, then I can see. Well, this context that I have, this is a computer or this is a
computer network. And the objects inside are computers. And their composition may be the same where they are actually a network of interacting objects.
And so, it’s turtles all the way down. However you want to structure your app, you can have these contexts. And the objects that play the roles inside of a context, they may themselves be a context of other objects interacting as well.
JESSICA:
Awesome. I love the sound of that, back to the original part of OO. I had one standard question that I ask about every technology and technique. When should you not use DCI?
JIM:
That’s a good question. So, when I first started reading about it and I think when people first started talking about it and exploring it, it was directly related to use cases. And you have some business use case and you want to reflect that. And over time, people have started saying, “Well actually, I have a scenario where it actually makes a lot of sense for me to use it.” Trygve Reenskaug says any time you have two or more objects interacting, then DCI is a candidate for the design. You may decide to solve your problem multiple different ways. Object-oriented programming is good for some things. Functional programming is good for some things.
So for example, I can think of if a certain order of steps in your system is important, being able to see those steps and what happens is important, then DCI is certainly a good possibility for structuring your application. I know when people hear me or anybody else talk about the importance of procedures, they think of procedural programming rather than thinking of the fact that there are things that must happen in the system in a particular order. And that business-level procedure is important. It’s not writing goto’s. So, it kind of depends. When I think about the code that I write and the tools that I’ve created for myself to help make it easier, I can write them pretty easily and make something work quickly. And I can throw it away just as quickly. And I like that where if I already have objects that have a certain bit of information I can say, “Well, let me just throw them in this context and see if this works for me.” If not, then I’ll just do something different.
So, I don’t know that I have a heuristic of when you do use it and when you don’t. I actually try things out and I’ll throw away code pretty quickly. And that’s one of the things that I like about the technique, is that I don’t have to… in some cases I have to do a lot of planning. In other, I can say, “Well, let me just take these objects, throw them together, and what if it could do this?” Then I can see how they interact and I might actually as I hack out some pseudocode, I might say oh well, this doesn’t really make a lot of sense because I know I’m going to need this behavior elsewhere where it’s not going to be implemented this way. So, I don’t know. I see it as a nice little sandbox for trying out ideas.
SARON:
How easily can you make the transition to use DCI and then to not? Like if I go down that path and decide, actually this doesn’t quite work or I don’t like it, can I undo it very easily?
JIM:
Yeah, I think so. So, particularly if you’re… it’s basically just methods. You know that a particular role, they have to do certain things or certain steps that have to be performed. You can always take that and put it somewhere else and say, “Well, I don’t actually want to try this. I’m just going to slap it into this module and include the module and it’ll always be available.” That’s pretty easy. What I have found is that it’s hard for people to accept, particularly in the Rails world, that you might want to take some use case and put it in your code. Because well, with Active Record I can always just reach through and get the associations and do whatever I want. And because Active Record is so free, allows you to do so much, actually saying, “Hey, no. Let’s actually go through this and allow only our behavior to exist there,” people have a tough time thinking that’s what they need to do.
AVDI:
I got to say, I kind of love the idea of experimenting with something by putting it altogether in one file that I can easily see and easily throw away if I decide I don’t like it, rather than adding new functionality through three different methods scattered across three different classes, scattered across three different files. I think we’re winding down a bit. Before we get into the picks, where are some good places for people to look for more information on DCI?
JIM:
So, the canonical place would be FullOO.info. That is… I’m actually not sure. [Chuckles] I personally haven’t looked at it to see if it’s up to date. But there’s a collection of talks there and papers that are being written or worked on, example code. There’s even a language called Marvin that’s built on top of C++ that is designed specifically to implement DCI. I have not personally looked into it. There’s the object-composition Google Group which is pretty active. And honestly, it’s so active for me. I find trouble keeping up with it lately. But there’s a lot of useful information there. I
think those are probably the best places.
AVDI:
And I hear you wrote a book.
JIM:
I did, I did. I wrote a book called ‘Clean Ruby’ which is mostly about DCI and some other things in it as well.
AVDI:
And where can people find that?
JIM:
They can find that at clean-ruby.com. It’s an eBook so you can get it in MOBI version, PDF, and EPUB.
AVDI:
Very cool. Alright, well let’s get into the picks. Jessica, what are your picks?
JESSICA:
I have one pick today. It’s a speech. It’s a very old speech from 1986 given by Richard Hamming. It’s called ‘You and Your Research’. It’s directed at scientists and he’s talking about what is the difference between a scientist who is very smart and works very hard and a scientist who is very smart and works very hard and wins a Nobel Prize. If you’re interested in having influence and doing something of significance. Maybe we’re not going to win a Nobel prize, but maybe something that really matters to the community, this is a great article to read because it talks about how to find the right things to work on, to spend your mental energies on. And various other differences between ordinary scientists and Nobel Prize winning scientists. I’ll put a link in the notes. Or you can also just google ‘You and Your Research’ and it’ll come up. That’s my pick.
AVDI:
Alright, cool. And how about…
CORALINE:
I have one pick today. It’s an article by Martin Fowler, a blogpost actually, called ‘Yagni’. YAGNI’s one of those rules, the knee-jerk reaction rules that I often find myself arguing against. I think it’s applied to consideration of features appropriately. But also often is misapplied in design decisions as well and can lead to a shortsightedness of only thinking in two-week increments. So, I think in his blogpost, Martin Fowler does a good job of explaining the context in which YAGNI was intended to be used and provides examples for when it’s inappropriate as well. And I think overall, it’s good to question our rules of thumb and actually spend time thinking about whether a guideline is applicable to a given situation. And Fowler’s article does a good job of preparing us and equipping us for that sort of analysis. So, that’s my pick.
AVDI:
Saron, what are your picks?
SARON:
Sure. I have a couple. So, we started doing this online study session at CodeNewbie called Ruby Monday. It should have been called Ruby Tuesday. I understand that that is a missed opportunity. But we do two hours and it’s very open ended. And we share code and share resources. And I got a bunch from our session last night that are really awesome.
So, one is called JunkFill. It’s a Chrome Extension that allows you to fill your forms with Junk. It’s amazing. It’s just, oh my goodness, I’m never filling out a form manually ever again. I’m really excited about that.
Another one that we talked about that was awesome is called Wappalyzer. And it’s a Chrome Extension as well. And it allows you to, when you go to a website, allows you to see what kind of dev tools they’re using. So, you can see their database and their framework and all these other, all the other information. Which is something that I do all the time. I’ll look at inspect element and see if I can figure out how they made this site. And now I don’t have to, because the extension does it for me.
And then the last one that we shared was called WhatFont, which allows you to hover over a font and it’ll tell you what font they’re using on any text on any website. And it’s amazing. Again, saving time so I don’t have to inspect element and see if I can hunt down the font family. So, those are some really good resources.
Another pick that I have is we wrote a blogpost called ‘What is Source Control?’ which is really, really good. It has some really great illustrations on the CodeNewbie blog. It was written by Julian Feliciano and illustrated by Sarah Frisk who is an amazing illustrator. So, I’d love for you all to check it out.
AVDI:
Very cool. I think I’m just going to pick one thing today. I’ve been kind of a coffee geek for a long time. I’m not super rabid about it. I like all kinds of coffee. Sometimes I like gas station coffee. But I also like fiddling around with coffee myself sometimes. And so, I have a lot different coffeemakers and coffee tools. And a few years back I received as a gift a vacuum pot coffeemaker. And I hadn’t really used it much because I was always a little nervous about using it on my gas stove. But in our new house we’ve got a glass top, which sucks for a lot of reasons. But it’s great for the vacuum pot coffeemaker.
And I don’t know. If you enjoy making coffee, if you enjoy fussing over your coffee and making it in interesting ways that take a little bit of attention and care, the vac pot is just a great process because it’s just so much fun to watch. If you don’t know what I’m talking about, look up some videos online. It’s just a neat way of making your coffee. And it makes good coffee, too. It’s also the way my grandmother used to make it. So, for me it’s a little bit nostalgic as well. So yeah, that’s it for me, just a Bodum vacuum pot coffeemaker.
And Jim, what about you?
JIM:
So, I’ve got a couple. I didn’t mention it before but there’s a book called ‘The Master and His Emissary’ by Iain McGilchrist who’s a, I guess psychology and brain researcher. And I haven’t actually finished reading it. I started reading it two years ago and haven’t gotten to the end. But it’s really interesting. And what he talks about is the way the hemispheres of our brain have helped shape history. And it’s relevant to DCI stuff because it talks about how our brain will break things up and see little bits and pieces but also has to understand the greater context of the world around it. So, interesting book.
Recently I started working with a new Rails project and we’re using a gem called request_store_rails. You may be familiar with the request_store that Steve Klabnik created which basically allows you to record information in a cache in the current thread for every request that comes into either your Rails or Sinatra application. Request_store_rails is a tweak on the way it works to make sure that if you have multiple threads using that information that it’s a safer storage mechanism for it. So, if you … I think the example they have in the readme is multiple things happening with different Puma threads that you might run into some issues with just the plain old request_store gem.
Secondly, or third, I’ve heard of littleBits before and I would hear people talk about them, how awesome they were. Being a developer and in the development community, I hear a lot of people talking about doing different things with their kids. And I try to not push my kids to do anything in particular. If they’re interested, great. And so, I’ve seen a lot of people talk about hardware hacking and doing things with Arduinos and whatnot. And I picked up an Arduino and I would just run into frustration because things wouldn’t compile. And how the heck am I going to get my kids to enjoy making blinky lights and things if I can’t even get things to compile?
So, for my son’s birthday we got him some littleBits. And they’re basically magnetic circuits that you can throw together. An/d you might have a dial on one and an LED in on another and a vibrating motor on another one or a sound mechanism. And they’re great. So, when I think about the stuff that you can do with hardware I think, “Well okay, yeah I guess that’s kind of fun.” But it actually has been wonderful because my oldest son had to do some report on Canada. And everybody in the class had to do a report on Canada. And just about half of them of course did something on Niagara Falls. But he did his diorama on Niagara Falls and he knew that he could take his littleBits and put the pieces together. And he made a little boat drive around the falls and go around in a circle.
And so, it was really cool. It allowed him to build a thing that was interesting to him and interesting to other people. And he was just really proud of himself that he could do something like this. And it was super simple. So, check it out.
SARON:
That is really cool.
JIM:
It was great. It was just, watching him, I asked him what he was going to do. And he, “I know. I can do this.” And he knew what pieces to put together. And it was like opening up a new world of possibilities for him.
SARON:
Have you personally played with littleBits?
JIM:
I have. In fact after that, I bought a set for myself so that I could play with them as well.
SARON:
[Chuckles]
JIM:
So, we’ve got, we’re starting to build up a big collection of them.
SARON:
Yeah, I did the exact same thing. So, I bought a set for my little cousins who at the time were seven and nine. And I was like, “Oh, these are really cool,” and I bought them for myself. And I don’t know what to do with them. I just keep sticking them together and that’s as far as my creativity will go.
[Chuckles]
JIM:
You need an assignment.
SARON:
Yeah, I do. [Chuckles] I need more direction, yeah. That’s awesome, though.
AVDI:
Yeah, very cool. Alright, Jim. Thank you so much for joining us.
JIM:
Thank you for having me.
[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.]
211 RR DCI with Jim Gay
0:00
Playback Speed: