Deep Design Lessons
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Feathers, Michael
It seems that the word 'design' has been surrendered to UX over the past few years. We don't talk about the internal design of software as much as we used to. However, there are still things to learn. In this talk, Michael Feathers will outline and codify some of the gems that he feels are less understood today.
 there. We're describing integers, right? We're describing the ranges of integers in various programming languages. And it's odd about this because quite often we don't think about particular limits that we have on the integers and on the data types that we have within our languages. We know that they're there. We kind of hope that we have values that are always going to fall within those ranges. We know that overflow and underflow are possible and all those things. But it's interesting to consider just what these ranges mean in terms of larger level program organization. Okay. So integers, we definitely have questions of overflow and underflow and what kind of things are valid. Some of these things can go away. Quite often in many languages today, you have the notion of promotable and integer integral types so that you have integers that will always be unbounded, right? So you can keep on going and keep on going and they'll never overflow. They start building like bigger internal representation to go and handle, you know, very, very large sums and stuff along those lines. But the big question we always have to deal with is really just what is appropriate for our domain. So let's take a look at an example here. Here's a class called account and I'm sure you've seen this kind of thing as an example in many little presentations. It's almost like a fundamental object-oriented program, right? What do we know about this class? What's permissible with this class? Well, if we look at it, it seems like any integer can come in, right? We can basically go ahead and pass in any number as our initial balance. We can deposit values into it and they get added onto the balance. We can subtract them and that's a withdrawal and we can also go and get the balance, right? So all very simple things to do. What are the appropriate ranges for these things? Okay, now it's funny, there's no real constraint when we're looking at this now in the code. It seems like a reasonable constraint might be to go and say, look, we're only going to allow positive balances in our bank account, okay? And then that might be a particular type of bank account that we care about. No overdrafts might be the best way of going and looking at this. How do we change this code in order to go and make this sort of thing possible? Can we do it with the type system? It's kind of odd to think about. There is no, it depends on what language you're working in. Some languages don't have unsigned integer types. If they do, it makes things a lot easier. If they don't, then you're basically stuck going and putting constraints inside of this code in order to understand, in order to go and basically accommodate the conditions where things can go wrong, right? So for us, we probably want to go and have a check in the very beginning with our constructor. We'd also want to go and have a check with our deposit, verifying that what we have coming in happens to be positive and or non-negative. And then our withdrawal, we'd want to go and basically check to verify that our balance is never put into a negative state by something happens with the withdrawal. Now, of course, in doing these things, we're going to end up going and putting in all sorts of things, exception checking and stuff like that, putting our code into a state where a user can put, can do things to it, okay, they're going to put it into a state where it's not really valid any longer. And this really has to do with the limitations of static type checking in languages and the limits of types in general. Now, this view of types that we have can also be extended out a bit. Since I like to go and put these kind of annotations when I'm thinking about things, you know, we can look at the particular values that are coming in to these particular functions and annotate them and say, look, you know, we've got things between zero and nintmax. Those are things that we can go and place in. GetDalance can return a value between zero and nintmax. That can serve as an understanding for us about what really is legal, what's permissible with this particular type of class. Now, it's funny with this. When we have this data representation view of an object, what's it like when we start using inheritance? Some ever used design by contract. You ever hear of this at all? Okay, it's an idea that was first articulated by Bertrand Meyer with Eiffel and then basically it was adopted by just about every contracts library you'll ever see. C-sharp has contracts libraries and such and such. And there are basically ways of going in sort of like asserting particular conditions, tying them to the code in such a way we can say, look, I have a precondition for a particular function. And if I know, if I pass in values that meet that precondition, I'm guaranteed that the post conditions will hold for that particular class. And as a result, it makes it rather easy for us to reason about things. Now, what happens when you start to use inheritance? Well, that's when things start to get a little bit strange. If we look at this, would it be permissible for us to go and have a, let's take a look at this class right here. It would be permissible for us to go and subclass this class and then do the checks inside of it to go and verify that the values coming in are only positive. Would that work out okay? A subclass of this class. Who wants to answer? Oh, God. Well, of course, you could do it, right? I mean, you could definitely go ahead and write a subclass of this class and then override deposit so that you check the incoming values, overwrite withdraw so you verify that you don't withdraw into negative. But the thing about that, though, is it would start to mess up with your polymorphic substitutability, right? You've heard of LISC off substitution principle? Yeah? Okay. Definite LSP violation when you do that sort of thing. And what's funny about this is that essentially you can look at the same thing from a data perspective also. From a data perspective, we can essentially say that subclasses can weaken preconditions and strengthen postconditions, okay? In other words, we are kind of allowed to basically accept more, okay, in a subclass, but we can guarantee, we have to guarantee more. We can't guarantee any less than what we have previously. And so there's a way of looking at this is like a data constraint that happens on the input values that you happen to have for a class and the output values that come back from it. And so it's funny because if you have this mindset, you're thinking about the constraints of things coming in, the constraints of things going out, the constraints are left with the class, it helps your reasoning about how things fit together within a program. Now again, that's all data. It's all constraints. Let's try looking at another example here. Suppose I've got my account class and it uses a tax table, okay? And here I have a method called apply and I pass a tax table into the account. And what does that actually do? If we look at it, it basically goes ahead and adds onto the balance, okay, the rate for particular category. And you can see the category happens to be a variable which is part of this class, right? Now when you do this, what kind of constraints do we have around this particular operation? It's kind of funny because a lot of them are kind of silent in a way, right? If we have a constraint on balance, basically saying that it has to be positive or non-negative at least, we'd have to go and basically make sure that we can't have values coming back from rate for category which happened to be negative, right? Or else we could possibly put ourselves into a situation where we've got a negative balance that could be kind of awkward. The other constraint that we have here is kind of subtle. We have to pass in some kind of a category that rate for category understands. We have to pass in some data that rate for category understands. And of course, it isn't really specified in this code here, but you know, category could be an enumeration. It could be an integer, in which case you'd have to go and sort of think about, gee, am I handling all the cases for all, you know, you know, two to the 16, two to the 32 integers that there are? And I have to be able to handle errors gracefully. I need to be able to go and sort of make sure that all that works. So we've got constraints running in both directions when we do this sort of thing. Now have you ever heard of tell, don't ask at all? Have you heard of that idea? Okay, an object oriented programming. There's something that was written up by Dave Thomas and Andy Hunt back in the early 2000s. And the idea behind this is that essentially object orientation is much better when you are not asking questions of objects instead when you're telling them to do something for you, right? And it's a subtle thing because when you think about it, it's like, oh, what difference does it make, right? But it's subtle but it's also very valuable. It helps you go ahead and arrive at better encapsulation decisions. The thing about this code right here that's a little bit awkward is that we're asking the table for something, some integer. And that means we have to be very aware of what comes back. And we have to basically know, does that play well with the stuff that we want to go and add it into? Is it negative? If it's not negative, everything's okay. If it's negative, we're in trouble. We need to be aware of that constraint. And as a result, things are a little bit, you know, awkward that way. We have coupling between these two objects. Let me show you an example of something which doesn't have as much coupling. And it's because we're dealing with like a pure tell system. If you look at this, okay, take a look at what's going on here. Okay, here's our deposit method. I've added something new to this. I have a transaction record object and I tell it to go and record the balance and the value. Okay? Now, what can go wrong when I call this? Do I have to worry about the, I have to worry about the types of balance and value as much in a way? The important thing is that transaction record, the record method will take the full range of those values, right? But we don't have to care too much about what's being supplied back to us. We don't have to care about things being out of range, out of the range that we can handle. In essence, any problem that occurs is going to be a problem that's going to be on the other end. We give it to them and we're never even going to hear about the problem unless it throws an exception back in our face, right? We pass information in and it can choose, it can take that information and ignore it. It can choose to take that information and record it. It can do all those things. But we've basically passed the problem on, okay? It can't impact us any longer. And this is a kind of neat thing about object orientation is that essentially in many cases we can kind of organize our objects in such a way that we are not getting things from people and we're not getting things from people then we're actually not concerned about the things that they could pass us that are wrong. If we flip it around and say, look, we're just going to pass things to other objects in order to go and get our work done, we actually have a higher chance of not having as many errors. And it's kind of fascinating when you do that. Essentially what happens is if you look at things from a design by contract point of view, the contract that we would have for a method like this is just really skinny. It's all about going and saying, look, I pass you an integer. I pass you any valid integer you accept it, right? There's no particular constraint on this. And because there's no particular constraint on this, you know, it's kind of like we have a higher chance of producing things with fewer errors. So let me, people have seen this style of programming much at all. It's like it's all about notifications and things along those lines. It's cool, okay? And if you look around at some, like the end-unit testing framework internally has a very tell-don't-ask type style. So this is the J-unit testing framework in Java. And you'll see this type of pattern you're being used to over and over again. Some of the worst models you'll ever get into when you're working an object-oriented code are where you have some collaboration between yourself and several other objects. And it's like, I grab from this, I grab from this, I grab from this, I do things, pass it on to somebody else or change my internal state. And when you discover that you want to test stuff like that, you find, oh, terrible problem because now I've got to go and mock out all this craziness. I have to go basically know what state this will be when I call it, what state this will be in when I call it, and, you know, deal with time-dependent dependencies and stuff along those lines. When you organize your design in a way where you're pushing things out, often you're testing this way easier and your design's not being a lot more decoupled. Now, funny thing about this though, when you're doing this sort of thing in a design, one thing that tends to happen is you start to go and get like a bit of a dichotomy in your design, right? You end up having objects where you pass things to other objects, right? But then what do you pass? Well, here I'm just passing data and passing balances and values and stuff like that. As you move further and further into this tell-don't-ask style of programming, one of the things you start to do is you start to consolidate some of these primitives into bigger objects. They're just really like data objects, right? And so you're passing around these objects so you have like a layer of little messages that you pass around and then basically you have these hubs which are things that accept and receive the messages. Does that sound like anything else? It's kind of like networking in a way, isn't it? It's kind of like you have these autonomous things and you pass messages back and forth. People decode these messages, do things with them and stuff along those lines. So it tends to kind of dichotomize. And again, it comes down to the contract between these things being all about data, just like what we were seeing with our constraints looking a little bit earlier in things. So let's go a little bit further with this, looking at data again. Is there anything wrong with this code at all? I'm just going to camp on this until somebody tells me. I know you can't be happy with this. Or if you are happy and you want to tell me about it, tell me. Am I happy with this code? No? Come on, speak up. You'll tell me. Okay. Go ahead. Yeah. Yeah, that would be. You know, it's funny because what you're doing is you're presenting a solution, but what's the problem? What he said was, why doesn't the client just choose which function to call? That's fair. But what's the problem? Is there a problem here, though? Beyond the thing? Okay. So if you look at it from a single responsibility point of view, function's doing two different things, right? This is actually a very, there's an old piece of advice from way back in the structured programming days. And that piece of advice was never pass control flags into functions, right? And it's funny about this because you can look at it from all different directions. One of them is single responsibility. Essentially, inside, you've got to decode these things. Another reason why this is particularly bad is if you look at this and you say, I'm passing a control flag into this run method, how does anybody know what true or false mean, right? It's very error prone. And I don't know, have you seen code like this before? Raise your hands if you've seen code like this before. Okay. Yeah. Lots of people do this sort of thing. They just don't understand that there's a problem there. So essentially, true and false don't really mean all that much to us. It would be better if we were going and actually going and passing, you know, or calling the specific functions that we care about. Would this be better if we had like an enumeration and we pass in stepwise or continuous? Could do that. And then we'd have to go and say, well, we could do that, but why don't we just have separate functions for that, right? That's another argument. One reason why you might just pass an enumeration in could be this is part of an API and you want to go and have a very narrow API. You don't want to go and have a whole series of functions to do and do various things. So you basically make enumeration as part of your API. Possible. That could work. But anyway, there's something kind of smelly about this thing. I want to compare this to something else, which is a little bit smelly also, and see if we can find the underlying principle behind this. Okay. This is some Ruby code. What's wrong with this code? If anybody screams that it's Ruby, I'll have to get out of the fit, right? Anything wrong with this at all? I may not have the best example for this, but the thing that I think is funny with this, and this happens all the time in code, is that we have a layer separation here where we have one part of the program constructing a data string. And then we have another part of the program decoding that data string in order to go and basically do something. Okay. Now, think about that. We do that sort of thing all the time with like XML and JSON and stuff along those lines, right? And particularly, we do that sort of thing when we are passing from one system to another through a network or something along those lines. What gets to be really crazy is when you do that kind of stuff inside of your program. Okay? What makes that crazy inside of a program? Okay? What happens is you've got one program, one part of your program that's going in constructing something, right? Only for another part of your program to go and, you know, destruct it. Basically, go ahead and parse it out, right? So you hope that you basically get both ends of that right, okay? On one end, you're constructing something, and on the other end, you're basically parsing it. And essentially, whenever you go and modify the data format over here, you can modify the data format over there, right? And that's like a natural coupling between two different parts of the system. What's underlying between this example and the previous one is that both of those examples are cases where we can be lossy. They're cases where we can lose information in the program, right? Essentially, when you're calling this function over here, it's kind of like, well, true or false, what does that mean, right? You have to go inside the function to understand what true or false mean. And over here, it's kind of like, oh, construct this string and hope we did it right. And it's like, well, then we have to go and do the parsing, but we've lost information in the type system. We're in the explicit representation of the objects to go and sort of, you know, be able to keep track of it. So we are introducing the possibility of error. We're also making it harder to understand what's going on with things. I look at this as being like a smell I call private language, okay? And these are just two examples of this kind of thing. Private language is spring up quite often when people don't really see direct ways of doing things. I don't know if you've ever seen this sort of thing before where you have like some part of a very big function and what people do is they say, okay, I'm going to set this flag here because I discovered this problem. I'm going to go down further and further and further. Now I've discovered another part of the problem. I'll set this flag here and then down here, set another flag here. And then the whole bottom of the method is like, oh, if this is true and this is true and that's false, then do this. If that's true and that's true and that's false and that's true, then do this. You ever seen code like that at all? Yeah, okay. And again, this is a private language. You're building up one representation in order to decode it later in order to go and solve a problem. So anyway, it's a code smell. It's something I don't really, I don't think I've ever seen anybody write up or talk about. But the thing that's interesting to me is that this sort of thing shows up when you are thinking about things from a data perspective, okay? As much as we love behavior in objects, it's kind of like we can still see, as I was saying earlier, data at the boundaries when we pass a message from one object to another. We still see data in the parameter list. We see data in the structuring of like it's passed back and forth. We definitely see data within, you know, these little private means of communication that we kind of build up inside of objects. So that's a little bit awkward and it's a nice thing to know about. Okay, so essentially, sort of like summarizing that, translating the strings loses constraints. A thing that I find interesting also is that this is really something which happens all the time when you're dealing with error handling and, error handling in applications also, okay? What's everybody's favorite method of handling errors in an application? Logging. What? Logging? Yeah, logging is good. Yeah. What about the E word, exceptions? Well, I guess it's overstated to go and call that a favorite, right? Anybody here love exceptions? No, okay. Yeah, I think exceptions are one of those kind of like beneficial evils in a way sometimes. The thing that's very easy to do when you're working with exceptions is to go and sort of, you know, as soon as you discover a problem, you throw an exception and you hope that somebody up higher in the stack is going to catch it, right? And you hope that the behavior when you throw that exception is well-defined. You know, is it really understandable to the person who receives it that this is something that I want to terminate the application for? I want to log it a higher level. Do I want to retry things? It's a very easy way to go and defer problems. But the thing that's funny about this is it still has that same issue that what we do is we have local information about a context. We produce an exception and we take that exception and we throw it back to another context where we don't have the inner context at all, right? We try to make a decision based upon that. So it's kind of like, again, building a private language between this piece of the program and the other piece. Can you get past that? Of course you can. I mean, you can add a stack trace into your exception. You can add in a context to go and get a hold of all the variables that occurred, you know, back in that lower context and stuff like that. But again, you're now in that position of writing a program to decode your program. At the upper level, you've got to go and parse that stuff and understand what to do with it, that kind of thing. So it feels like this thing of inner and outer context of private languages is rather pervasive around applications. And again, I think that we see that when we are looking at data constraints in particular areas of code. This kind of leads me to something else that people aren't really aware of that much these days. Anybody ever hear of this one at all? Postos law? It's really been dawning over me over the past couple of years that design, the people that really understood design in the context of software development that people don't listen to that often are really the architects of the Internet, right? There are so many little tacit bits of knowledge that are just kind of baked into the stuff that the IETF has done, the protocols that we use, you know, day to day TCPIP, things like fail fast, all these other things. This one is just neat because it tends to be true of networking systems. It also tends to work out well with like Unix command line utilities. And you know what? Once you understand this one, you start seeing applications of this all over the place in all the code that you work in, okay? So what's this about? Be conservative in what you send and liberal in what you accept. What role does that mean, okay? Well, let's say that you're writing some utility, okay? And your utility is supposed to accept tab-delimited, you know, values, okay? Just rows and rows of them. And then what you do is you basically tabulate those things and produce some number, that's like a summation of each one of the lines or something along those lines. Should you throw away your input if somebody goes and puts a space rather than a tab between their fields? Depends on whether spaces are significant or not. I mean, if they're just numbers, you might say, well, I'll be a little bit lenient. I'll accept spaces and tabs, right? And maybe you don't even document that as you come up, okay, this is kind of just, I'll accept this sort of thing. It makes my program a little more useful to do that sort of thing. Now, when you're putting out your values, should you just put out tabs and spaces willy-nilly? No, pick one, okay? Always produce tabs or always produce spaces, right? And so what you're doing with this is what you're doing is you're creating like a funnel. You're basically allowing lots of people to produce input for you and being very forgiving in what you accept, but you're also going and saying, look, for me, you can count on me producing exactly this format and it's kind of like, that's how you deal with errors, right? It's kind of like you're being, you're being accepting of what is being produced for you. You're trying to go and leave the world in a better place by producing something which is a very tight format. So may you see examples of this in other places in programming at all? It's funny. You can look at LISC off substitution as being almost a variation of this at a very high level. It's kind of like when you have classes, it's kind of like they, subclasses should be permissive in what they accept but restrictive in what they produce. This, that whole precondition, post-condition thing I was talking about earlier, right? And when you think about this even deeper, you start to realize that this is fundamental to all systems, not just networking systems, not just computing systems. It's like when you, when you build pipes like in a plumbing system, isn't one end a little bit wider to connect to the other one in a way and it's a little bit narrower? It's like there's this thing where basically you want to be permissive on the input and then sort of like more restrictive on the output. And by going and doing that sort of thing, you end up building robust systems over time. Again, this is something that you start to see as a result of looking at, you know, data formats and stuff along those lines. One of the weird experiences I had as a programmer early on was working at a biomedical company and I was lucky enough to work trying to develop translators for, or parsers for a data format that's, that was designed by some biologists. And generally that's okay because biologists are very good at producing data formats, isn't that true? Now it's, it's typically that thing. Quite often people, they know their domain, they feel they can do this sort of thing, they do it and they make some mistake and they don't really quite understand what the mistake was. The data format I was working with is something called flow cytometry standard, FCS. And there was a very cool thing about FCS format. You could save data in comma separated value and binary. You could save it in arbitrary precision numbers. You could do this. You'd have all these data formats and headers and all these other things. You could save data in a million different ways in FCS format, which meant writing a writer was very, very easy. Writing a reader was a real pain, right? Because you had no idea which of the end formats people chose to go and write things out in, right? And I think, you know, with that sort of thing, if anybody that was on the team developing this standard was really aware of that, they would have said, oh no, we have to be restrictive just because this is going to be a pain to go and write, you know, readers for this. And I think, yeah, essentially you don't see developers making that same mistake all that often, you know, because they are aware of the pain involved in receiving something and having to go and deal with it. So it's, it's funny about this. Even though you may not have heard of Postal's Law, it's probably something you've encountered where you probably almost have baked into you now. You almost have this sense that that's just the way that you organize things. And so, yeah, it's kind of an interesting thing to be aware of. So let's look at data one last time. What's wrong with this code? You know, if nobody finds anything wrong with this code, I'm just going to walk through it and see what happens. Sorry? Too many dots. Okay. But you know, when you think about it, that shouldn't be a problem because dots take up very little ink, right? So they're, they're green. You know, dots are green relative to the other punctuation. We have in languages, right? Okay. That was a joke, right? This is a classic example of a violation of the law of demeter. Okay. You've heard of the law of demeter, right? And the notion is that essentially when you have an object, you shouldn't be going in sort of letting your internals out for other use, right? And there's a bunch of reasons why this can be awkward, okay? One of them is that basically goes and makes, it puts, it puts the burden on the person who's using this. They not only have to know how to deal with an account, they have to know that an account has a calculator. They have to know that a calculator has a superannuary table and they have to know the superannuary table has to have cells and you address them this way and it says, you've increased the burden on people trying to understand your stuff automatically, right? Beyond that, from a dependency point of view, it's possible to break code that is written this way very easily by going in saying, well, maybe I don't have my calculator on the account. Maybe I have it on another object that's held by the account. Anything you do to go and alter internal structure can go and alter these chains of objects and force recompilation or force test errors and stuff along those lines. And, you know, the other thing too as well is just from a compilation point of view in a statically typed language, anytime I touch any of these intermediate classes, it's probably going to force this to recompile. In a dynamically typed language, you probably want to go and force the rerun of all the tests for all these intermediate pieces also, right? So, you're convinced it's bad? It's kind of bad, right? What about this? Is this code okay? It's a Ruby code. Makes it inherently bad, doesn't it? Anyway, I'm not going to go and paint this as being an ideal vision of what code should be or anything on those lines, but I feel more comfortable with this code than with the previous code. This code, even though it's a little bit terse, you know, it's a little bit weird, it relies upon something which is more functional in style than object oriented in style, okay? What we have here is we have a series of transformations. We basically say we have a list of events, we map each of the events to a date. We go and we pull out the date of each of those events, we sort all those dates, we do a unique on those which basically goes and gives us all those dates uniquely, it removes all the duplicates. Then we call each cons too, which is kind of something which goes and gives me each consecutive one of those things as pairs. Then we map those pairs into the difference of the dates and then we do a frequency histogram and we select certain things within certain ranges and stuff. Now I'm sure you guys looking at this, you're just like, oh my God, that's horrible, it's just terrible. It's code written by mathematicians that have no design sense or anything like that at all. There are much tamer examples of this sort of thing that I feel much happier about, but the style itself, I don't feel bad about regardless, okay? The thing about the style which makes this useful and interesting is that it's fully transformational, okay? Essentially, there is no state over here in events which is being modified. When I do a sort, I get something completely new out, okay? I get basically a completely new array of things and then when I do a unique, I get a new array of things also and I'm not, there's nothing I can do in a later part of the sequence that's going to change anything earlier in the sequence, okay? The other thing too is that generally each one of these things I'm working with is accepting an array and returning an array back, right? So we don't have that big type issue that we were talking about a little bit earlier of like, okay, here it's like I've got five different types of things all the way down to my chain. Here, it's just basically a bunch of array transformations giving me the things that I want with these things. Can you still get into trouble with this? Yeah, I mean somebody could take the map operation and move it off of arrays, but they'd be shot, right? I mean that would just be a terribly nasty thing to do, would break every base code in the world. But it's interesting to notice that there is this thing where in a particular context, this demetrious stuff is actually okay and another one it isn't quite okay. And a lot of it comes down to change characteristics. How easy is it to change the code when you're doing these things? So it doesn't feel like there's any strict rule with that. The thing that I think is neat about this is, and I was looking for a word for this, in category theory which gets, you know, bandied about in like, you know, the Haskell and functional programming language communities, there's this concept of something called an endomorphism. And an endomorphism is essentially a function which accepts a type and returns the same type back. It can do things to the values, right, but it accepts that type and returns another one back. And you'll see that roughly here, most everything we have is endomorphic. We accept an array, we return back an array. We accept an array, we return back an array. And we're able to go and chain things together in a very nice style that does things well. Now, have I ever seen a framework called Jmock at all? Okay, some of you have. There's also variations of this in the.NET space, but it's like those very strongly DSL-ish testing frameworks where you're able to go and say, you know, this.should equals this, da-da-da. And just, you have long message chains that make the entire thing read like English. And you might look at that and say, well, gee, it's that, right? But it's basically going ahead and sort of like dealing with this trade-off. The trade-off between having things which can cause trouble and break later when people move things and having something which is a bit more expressive and quite often relying upon this notion of having the same type used prolifically across the chain of operations. Again, this is something you basically see when you look at the data that goes between things. Now, it's funny about all this. The topics I've discussed so far have been kind of, you know, mix and match. I think the thing that's really common is basically looking at the data representation between things. And I think that's something that we can try to go and basically pay an awful lot more attention to as we develop code. And I don't see anybody talking at that very much in design, so I wanted to share it with you guys. I did want to go and basically mention a couple of things at the end though about the law of demeter. We just basically saw cases where what we consider to be the law of demeter is not quite the same as what we expect it to be. There's other cases where basically undemetered code is okay. So when you are working in a situation where you have this kind of thing going on, can anybody explain or anybody describe what systems this sort of thing tends to happen in more than others? I mean, do you see this in, you know, data acquisition systems or compilers? I don't know. I tend to see this sort of thing a lot in database-centric applications. I know about you guys, right? You ever see that sort of thing? It's like I have a bill of sale and I go and I get this thing, dot this thing, dot this thing, dot this thing, because essentially you have records and sub records and sub records based upon what your data scheme happens to be, right? So how do you get rid of that when you're working in an application? How do you adhere to the law of demeter when you're doing that sort of thing? It's kind of hard, isn't it? I mean, one thing you can do when you have a data model is you can say, look, I'm accepting, I'm going to basically say that this one thing is primary like the bill of sale. And even though it has sub-objects, what I'm going to do is I'm going to place all of the methods that were on those things, try to push them up to the top and basically have high-level operations that allow me to manipulate all the substructure without having to care about it all that much. Does it work? It can at times, right? But we always have to have a lot of things that are not in the application. And OO applications are database centric between having like an overarching thing which is a container and then all these things underneath it need to go and change also. So it's very typical that when you're working with a database centric application that the law of demeter stuff doesn't quite work out as well. If you're doing things that are kind of like workflow-y, right? It's kind of like, I have this one processing step, I pass this thing through and pass it through another processing step and another processing step. And quite often, you know, you can have as many demeter violations. You're not pulling into things and stuff like that as much. I had an experience years ago which kind of opened my eyes to something rather fascinating. I was visiting a team that had the worst law of demeter violations I've ever seen in my life and it took me a long time to figure out why they were doing it and why it was happening. It was a giant Java project and it turned out that what they were doing is they were, every time a visitor visited their site, they loaded a user and when they loaded the user, they loaded up every bit of data that could possibly be accessed from the user and used in that session, right? So you can imagine what that was like, right? So you basically each time through, you have the session, you navigate to the user, you're able to go and get the entire history of everything, da-da-da-da-da, all this stuff. And I was like looking at this and saying, this is just madness. And so we'd look at particular functions and it's like, okay, you've got this one sub-piece and it goes and talks to this, which talks to this, which talks to this, which talks to this. And it took me a long time to understand why that was happening. And the reason was essentially what I was saying. They loaded everything all at once, right? And in most database-centric applications, what you end up doing is saying, look, I need to go and have, you know, I need to have a bill of sale, I need to have a transaction record, stuff like that. And you're basically dealing with one piece plus it's subsidiaries, it's immediate subsidiaries and you get through joins and stuff like that, only the piece that you care about. And so as a result, you're going to have a constrained sphere of what you're working with, right? And it was kind of fascinating to notice that because they didn't do that, they ended up with far worse demeter issues than you would have otherwise. And it kind of led me to this notion that essentially it's like databases are better, are good not just for storing data but also the constraints that they place on your development. They kind of force you to go and basically sort of get down to a smaller crux, a smaller change set and the things that you need to pull in and modify and then push back into your database. So it seemed kind of interesting that way. So yeah, the law of demeter is a rather powerful and interesting thing. I think the thing that makes it overlap in this talk a bit is that it is about representation. When you have a structure that looks like this, essentially you are making the relationship between pieces very obvious and that's good at times except if that structure changes. So cases where you want to make things very explicit are cases where the structure of the data holds information rather than just the data itself. Some cases where that sort of thing happens, things like parse trees and compilers and stuff like that, it's not that you have an equal sign and you have a variable and you have a literal, it's the entire tree structure that has a meaning. So as a result, you want to have structure data like this. It ends up looking very undemetered in a way, right? But I think as programmers we just need to really be aware of when we need that kind of thing, when we need a complicated data representation and when we don't. And you can essentially go all the way back and start looking at all the different ways that you can see data each step in your program and quite often it gives you insights that you would not necessarily have had otherwise. So anyway, that was my weird talk. It's all about data representations across things and it ties in different aspects of design. Any questions or comments? Yes? Yeah, sure. There's one and there's the other. Yeah, okay. So the comment is basically the problem is that this is basically taking you deeper into the object and these are all off of the same object, right? And that's fair. Basically all these things are things which are unenumerable or array and so they are just aspects of the same protocol. So yeah, it's just essentially that's the thing. And quite often, I mean, it's funny because now that this has become more popular, many people are going and adopting this point of view. They're basically saying that a key aspect of demeter is the amount of type exposure that you have. It's like how many types are you really exposing in a context? And in this case it's generally just one and that makes things a lot easier to deal with. That's fair. Yes? Okay. Okay, the question was if, would I say that if we're using ORM we would end up with a structure like this and that's bad and that we should avoid it? I think it's really data has to be structured in certain ways, you know, in order to go and make it easy to add, you know, access. The representation that you use inside of your program doesn't have to be exactly the same. It's probably going to map quite easily. My bias is always with that to pull up only the amount of information we need and in the form that we care about into objects that may not use this much. But there's a real tension there because then you can end up going and producing a lot more classes to represent the same data in a way. So it's, I think it's just really a fundamental tension. And, you know, the indication that really is a fundamental tension is the fact that ORM is not a solved problem. It's always causing people trouble. That's just inherent to the mismatch between database schema and stuff. So, okay. Any questions or comments at all? Okay. I think I'm a little bit early, but thank you very much. Thank you very much.
 there. We're describing integers, right? We're describing the ranges of integers in various  programming languages. And it's odd about this because quite often we don't think about  particular limits that we have on the integers and on the data types that we have within  our languages. We know that they're there. We kind of hope that we have values that are  always going to fall within those ranges. We know that overflow and underflow are possible  and all those things. But it's interesting to consider just what these ranges mean in  terms of larger level program organization. Okay. So integers, we definitely have questions  of overflow and underflow and what kind of things are valid. Some of these things can  go away. Quite often in many languages today, you have the notion of promotable and integer  integral types so that you have integers that will always be unbounded, right? So you can  keep on going and keep on going and they'll never overflow. They start building like bigger  internal representation to go and handle, you know, very, very large sums and stuff  along those lines. But the big question we always have to deal with is really just what  is appropriate for our domain. So let's take a look at an example here. Here's a class  called account and I'm sure you've seen this kind of thing as an example in many little  presentations. It's almost like a fundamental object-oriented program, right? What do we  know about this class? What's permissible with this class? Well, if we look at it, it  seems like any integer can come in, right? We can basically go ahead and pass in any  number as our initial balance. We can deposit values into it and they get added onto the  balance. We can subtract them and that's a withdrawal and we can also go and get the  balance, right? So all very simple things to do. What are the appropriate ranges for  these things? Okay, now it's funny, there's no real constraint when we're looking at this  now in the code. It seems like a reasonable constraint might be to go and say, look, we're  only going to allow positive balances in our bank account, okay? And then that might be  a particular type of bank account that we care about. No overdrafts might be the best  way of going and looking at this. How do we change this code in order to go and make this  sort of thing possible? Can we do it with the type system? It's kind of odd to think  about. There is no, it depends on what language you're working in. Some languages don't have  unsigned integer types. If they do, it makes things a lot easier. If they don't, then you're  basically stuck going and putting constraints inside of this code in order to understand,  in order to go and basically accommodate the conditions where things can go wrong, right?  So for us, we probably want to go and have a check in the very beginning with our constructor.  We'd also want to go and have a check with our deposit, verifying that what we have coming  in happens to be positive and or non-negative. And then our withdrawal, we'd want to go and  basically check to verify that our balance is never put into a negative state by something  happens with the withdrawal. Now, of course, in doing these things, we're going to end  up going and putting in all sorts of things, exception checking and stuff like that, putting  our code into a state where a user can put, can do things to it, okay, they're going to  put it into a state where it's not really valid any longer. And this really has to do  with the limitations of static type checking in languages and the limits of types in general.  Now, this view of types that we have can also be extended out a bit. Since I like to go  and put these kind of annotations when I'm thinking about things, you know, we can look  at the particular values that are coming in to these particular functions and annotate  them and say, look, you know, we've got things between zero and nintmax. Those are things  that we can go and place in. GetDalance can return a value between zero and nintmax. That  can serve as an understanding for us about what really is legal, what's permissible with  this particular type of class. Now, it's funny with this. When we have this data representation  view of an object, what's it like when we start using inheritance? Some ever used design  by contract. You ever hear of this at all? Okay, it's an idea that was first articulated  by Bertrand Meyer with Eiffel and then basically it was adopted by just about every contracts  library you'll ever see. C-sharp has contracts libraries and such and such. And there are  basically ways of going in sort of like asserting particular conditions, tying them to the code  in such a way we can say, look, I have a precondition for a particular function. And if I know,  if I pass in values that meet that precondition, I'm guaranteed that the post conditions will  hold for that particular class. And as a result, it makes it rather easy for us to reason about  things. Now, what happens when you start to use inheritance? Well, that's when things  start to get a little bit strange. If we look at this, would it be permissible for us to  go and have a, let's take a look at this class right here. It would be permissible for us  to go and subclass this class and then do the checks inside of it to go and verify that  the values coming in are only positive. Would that work out okay? A subclass of this class.  Who wants to answer? Oh, God. Well, of course, you could do it, right? I mean, you could  definitely go ahead and write a subclass of this class and then override deposit so that  you check the incoming values, overwrite withdraw so you verify that you don't withdraw into  negative. But the thing about that, though, is it would start to mess up with your polymorphic  substitutability, right? You've heard of LISC off substitution principle? Yeah? Okay. Definite  LSP violation when you do that sort of thing. And what's funny about this is that essentially  you can look at the same thing from a data perspective also. From a data perspective,  we can essentially say that subclasses can weaken preconditions and strengthen postconditions,  okay? In other words, we are kind of allowed to basically accept more, okay, in a subclass,  but we can guarantee, we have to guarantee more. We can't guarantee any less than what  we have previously. And so there's a way of looking at this is like a data constraint  that happens on the input values that you happen to have for a class and the output values  that come back from it. And so it's funny because if you have this mindset, you're thinking  about the constraints of things coming in, the constraints of things going out, the constraints  are left with the class, it helps your reasoning about how things fit together within a program.  Now again, that's all data. It's all constraints. Let's try looking at another example here.  Suppose I've got my account class and it uses a tax table, okay? And here I have a method  called apply and I pass a tax table into the account. And what does that actually do? If  we look at it, it basically goes ahead and adds onto the balance, okay, the rate for  particular category. And you can see the category happens to be a variable which is part of  this class, right? Now when you do this, what kind of constraints do we have around this  particular operation? It's kind of funny because a lot of them are kind of silent in a way,  right? If we have a constraint on balance, basically saying that it has to be positive  or non-negative at least, we'd have to go and basically make sure that we can't have  values coming back from rate for category which happened to be negative, right? Or else  we could possibly put ourselves into a situation where we've got a negative balance that could  be kind of awkward. The other constraint that we have here is kind of subtle. We have to  pass in some kind of a category that rate for category understands. We have to pass  in some data that rate for category understands. And of course, it isn't really specified in  this code here, but you know, category could be an enumeration. It could be an integer,  in which case you'd have to go and sort of think about, gee, am I handling all the cases  for all, you know, you know, two to the 16, two to the 32 integers that there are? And  I have to be able to handle errors gracefully. I need to be able to go and sort of make sure  that all that works. So we've got constraints running in both directions when we do this  sort of thing. Now have you ever heard of tell, don't ask at all? Have you heard of that  idea? Okay, an object oriented programming. There's something that was written up by Dave  Thomas and Andy Hunt back in the early 2000s. And the idea behind this is that essentially  object orientation is much better when you are not asking questions of objects instead  when you're telling them to do something for you, right? And it's a subtle thing because  when you think about it, it's like, oh, what difference does it make, right? But it's subtle  but it's also very valuable. It helps you go ahead and arrive at better encapsulation  decisions. The thing about this code right here that's a little bit awkward is that we're  asking the table for something, some integer. And that means we have to be very aware of  what comes back. And we have to basically know, does that play well with the stuff that  we want to go and add it into? Is it negative? If it's not negative, everything's okay. If  it's negative, we're in trouble. We need to be aware of that constraint. And as a result,  things are a little bit, you know, awkward that way. We have coupling between these two  objects. Let me show you an example of something which doesn't have as much coupling. And it's  because we're dealing with like a pure tell system. If you look at this, okay, take a  look at what's going on here. Okay, here's our deposit method. I've added something new  to this. I have a transaction record object and I tell it to go and record the balance  and the value. Okay? Now, what can go wrong when I call this? Do I have to worry about  the, I have to worry about the types of balance and value as much in a way? The important thing  is that transaction record, the record method will take the full range of those values,  right? But we don't have to care too much about what's being supplied back to us. We  don't have to care about things being out of range, out of the range that we can handle.  In essence, any problem that occurs is going to be a problem that's going to be on the  other end. We give it to them and we're never even going to hear about the problem unless  it throws an exception back in our face, right? We pass information in and it can choose,  it can take that information and ignore it. It can choose to take that information and  record it. It can do all those things. But we've basically passed the problem on, okay?  It can't impact us any longer. And this is a kind of neat thing about object orientation  is that essentially in many cases we can kind of organize our objects in such a way that  we are not getting things from people and we're not getting things from people then  we're actually not concerned about the things that they could pass us that are wrong. If  we flip it around and say, look, we're just going to pass things to other objects in order  to go and get our work done, we actually have a higher chance of not having as many errors.  And it's kind of fascinating when you do that. Essentially what happens is if you look at  things from a design by contract point of view, the contract that we would have for  a method like this is just really skinny. It's all about going and saying, look, I pass  you an integer. I pass you any valid integer you accept it, right? There's no particular  constraint on this. And because there's no particular constraint on this, you know, it's  kind of like we have a higher chance of producing things with fewer errors. So let me, people  have seen this style of programming much at all. It's like it's all about notifications  and things along those lines. It's cool, okay? And if you look around at some, like the  end-unit testing framework internally has a very tell-don't-ask type style. So this  is the J-unit testing framework in Java. And you'll see this type of pattern you're being  used to over and over again. Some of the worst models you'll ever get into when you're working  an object-oriented code are where you have some collaboration between yourself and several  other objects. And it's like, I grab from this, I grab from this, I grab from this,  I do things, pass it on to somebody else or change my internal state. And when you discover  that you want to test stuff like that, you find, oh, terrible problem because now I've  got to go and mock out all this craziness. I have to go basically know what state this  will be when I call it, what state this will be in when I call it, and, you know, deal  with time-dependent dependencies and stuff along those lines. When you organize your  design in a way where you're pushing things out, often you're testing this way easier  and your design's not being a lot more decoupled. Now, funny thing about this though, when  you're doing this sort of thing in a design, one thing that tends to happen is you start  to go and get like a bit of a dichotomy in your design, right? You end up having objects  where you pass things to other objects, right? But then what do you pass? Well, here I'm  just passing data and passing balances and values and stuff like that. As you move further  and further into this tell-don't-ask style of programming, one of the things you start  to do is you start to consolidate some of these primitives into bigger objects. They're just  really like data objects, right? And so you're passing around these objects so you have like  a layer of little messages that you pass around and then basically you have these hubs which  are things that accept and receive the messages. Does that sound like anything else? It's kind  of like networking in a way, isn't it? It's kind of like you have these autonomous things  and you pass messages back and forth. People decode these messages, do things with them  and stuff along those lines. So it tends to kind of dichotomize. And again, it comes down  to the contract between these things being all about data, just like what we were seeing  with our constraints looking a little bit earlier in things. So let's go a little bit  further with this, looking at data again. Is there anything wrong with this code at  all? I'm just going to camp on this until somebody tells me. I know you can't be happy  with this. Or if you are happy and you want to tell me about it, tell me. Am I happy with  this code? No? Come on, speak up. You'll tell me. Okay. Go ahead. Yeah. Yeah, that would  be. You know, it's funny because what you're doing is you're presenting a solution, but  what's the problem? What he said was, why doesn't the client just choose which function  to call? That's fair. But what's the problem? Is there a problem here, though? Beyond the  thing? Okay. So if you look at it from a single responsibility point of view, function's doing  two different things, right? This is actually a very, there's an old piece of advice from  way back in the structured programming days. And that piece of advice was never pass control  flags into functions, right? And it's funny about this because you can look at it from  all different directions. One of them is single responsibility. Essentially, inside,  you've got to decode these things. Another reason why this is particularly bad is if  you look at this and you say, I'm passing a control flag into this run method, how does  anybody know what true or false mean, right? It's very error prone. And I don't know, have  you seen code like this before? Raise your hands if you've seen code like this before.  Okay. Yeah. Lots of people do this sort of thing. They just don't understand that there's  a problem there. So essentially, true and false don't really mean all that much to us. It  would be better if we were going and actually going and passing, you know, or calling the  specific functions that we care about. Would this be better if we had like an enumeration  and we pass in stepwise or continuous? Could do that. And then we'd have to go and say,  well, we could do that, but why don't we just have separate functions for that, right? That's  another argument. One reason why you might just pass an enumeration in could be this  is part of an API and you want to go and have a very narrow API. You don't want to go and  have a whole series of functions to do and do various things. So you basically make enumeration  as part of your API. Possible. That could work. But anyway, there's something kind of  smelly about this thing. I want to compare this to something else, which is a little  bit smelly also, and see if we can find the underlying principle behind this. Okay. This  is some Ruby code. What's wrong with this code? If anybody screams that it's Ruby, I'll  have to get out of the fit, right? Anything wrong with this at all? I may not have the  best example for this, but the thing that I think is funny with this, and this happens  all the time in code, is that we have a layer separation here where we have one part of  the program constructing a data string. And then we have another part of the program decoding  that data string in order to go and basically do something. Okay. Now, think about that.  We do that sort of thing all the time with like XML and JSON and stuff along those lines,  right? And particularly, we do that sort of thing when we are passing from one system  to another through a network or something along those lines. What gets to be really  crazy is when you do that kind of stuff inside of your program. Okay? What makes that crazy  inside of a program? Okay? What happens is you've got one program, one part of your program  that's going in constructing something, right? Only for another part of your program to go  and, you know, destruct it. Basically, go ahead and parse it out, right? So you hope  that you basically get both ends of that right, okay? On one end, you're constructing something,  and on the other end, you're basically parsing it. And essentially, whenever you go and modify  the data format over here, you can modify the data format over there, right? And that's  like a natural coupling between two different parts of the system. What's underlying between  this example and the previous one is that both of those examples are cases where we  can be lossy. They're cases where we can lose information in the program, right? Essentially,  when you're calling this function over here, it's kind of like, well, true or false, what  does that mean, right? You have to go inside the function to understand what true or false  mean. And over here, it's kind of like, oh, construct this string and hope we did it right.  And it's like, well, then we have to go and do the parsing, but we've lost information  in the type system. We're in the explicit representation of the objects to go and sort  of, you know, be able to keep track of it. So we are introducing the possibility of error.  We're also making it harder to understand what's going on with things. I look at this  as being like a smell I call private language, okay? And these are just two examples of this  kind of thing. Private language is spring up quite often when people don't really see  direct ways of doing things. I don't know if you've ever seen this sort of thing before  where you have like some part of a very big function and what people do is they say, okay,  I'm going to set this flag here because I discovered this problem. I'm going to go down  further and further and further. Now I've discovered another part of the problem. I'll  set this flag here and then down here, set another flag here. And then the whole bottom  of the method is like, oh, if this is true and this is true and that's false, then do  this. If that's true and that's true and that's false and that's true, then do this. You ever  seen code like that at all? Yeah, okay. And again, this is a private language. You're  building up one representation in order to decode it later in order to go and solve a  problem. So anyway, it's a code smell. It's something I don't really, I don't think I've  ever seen anybody write up or talk about. But the thing that's interesting to me is  that this sort of thing shows up when you are thinking about things from a data perspective,  okay? As much as we love behavior in objects, it's kind of like we can still see, as I was  saying earlier, data at the boundaries when we pass a message from one object to another.  We still see data in the parameter list. We see data in the structuring of like it's  passed back and forth. We definitely see data within, you know, these little private means  of communication that we kind of build up inside of objects. So that's a little bit  awkward and it's a nice thing to know about. Okay, so essentially, sort of like summarizing  that, translating the strings loses constraints. A thing that I find interesting also is that  this is really something which happens all the time when you're dealing with error handling  and, error handling in applications also, okay? What's everybody's favorite method of  handling errors in an application? Logging. What? Logging? Yeah, logging is good. Yeah.  What about the E word, exceptions? Well, I guess it's overstated to go and call that  a favorite, right? Anybody here love exceptions? No, okay. Yeah, I think exceptions are one  of those kind of like beneficial evils in a way sometimes. The thing that's very easy  to do when you're working with exceptions is to go and sort of, you know, as soon as  you discover a problem, you throw an exception and you hope that somebody up higher in the  stack is going to catch it, right? And you hope that the behavior when you throw that  exception is well-defined. You know, is it really understandable to the person who receives  it that this is something that I want to terminate the application for? I want to log it a higher  level. Do I want to retry things? It's a very easy way to go and defer problems. But the  thing that's funny about this is it still has that same issue that what we do is we  have local information about a context. We produce an exception and we take that exception  and we throw it back to another context where we don't have the inner context at all, right?  We try to make a decision based upon that. So it's kind of like, again, building a private  language between this piece of the program and the other piece. Can you get past that?  Of course you can. I mean, you can add a stack trace into your exception. You can add in a  context to go and get a hold of all the variables that occurred, you know, back in that lower  context and stuff like that. But again, you're now in that position of writing a program  to decode your program. At the upper level, you've got to go and parse that stuff and  understand what to do with it, that kind of thing. So it feels like this thing of inner  and outer context of private languages is rather pervasive around applications. And  again, I think that we see that when we are looking at data constraints in particular  areas of code. This kind of leads me to something else that people aren't really aware of that  much these days. Anybody ever hear of this one at all? Postos law? It's really been  dawning over me over the past couple of years that design, the people that really understood  design in the context of software development that people don't listen to that often are  really the architects of the Internet, right? There are so many little tacit bits of knowledge  that are just kind of baked into the stuff that the IETF has done, the protocols that  we use, you know, day to day TCPIP, things like fail fast, all these other things. This  one is just neat because it tends to be true of networking systems. It also tends to work  out well with like Unix command line utilities. And you know what? Once you understand this  one, you start seeing applications of this all over the place in all the code that you  work in, okay? So what's this about? Be conservative in what you send and liberal in what you accept.  What role does that mean, okay? Well, let's say that you're writing some utility, okay?  And your utility is supposed to accept tab-delimited, you know, values, okay? Just rows and rows  of them. And then what you do is you basically tabulate those things and produce some number,  that's like a summation of each one of the lines or something along those lines. Should  you throw away your input if somebody goes and puts a space rather than a tab between  their fields? Depends on whether spaces are significant or not. I mean, if they're just  numbers, you might say, well, I'll be a little bit lenient. I'll accept spaces and tabs,  right? And maybe you don't even document that as you come up, okay, this is kind of just,  I'll accept this sort of thing. It makes my program a little more useful to do that sort  of thing. Now, when you're putting out your values, should you just put out tabs and spaces  willy-nilly? No, pick one, okay? Always produce tabs or always produce spaces, right? And so  what you're doing with this is what you're doing is you're creating like a funnel. You're  basically allowing lots of people to produce input for you and being very forgiving in what  you accept, but you're also going and saying, look, for me, you can count on me producing  exactly this format and it's kind of like, that's how you deal with errors, right? It's  kind of like you're being, you're being accepting of what is being produced for you. You're  trying to go and leave the world in a better place by producing something which is a very  tight format. So may you see examples of this in other places in programming at all? It's  funny. You can look at LISC off substitution as being almost a variation of this at a very  high level. It's kind of like when you have classes, it's kind of like they, subclasses  should be permissive in what they accept but restrictive in what they produce. This, that  whole precondition, post-condition thing I was talking about earlier, right? And when  you think about this even deeper, you start to realize that this is fundamental to all  systems, not just networking systems, not just computing systems. It's like when you,  when you build pipes like in a plumbing system, isn't one end a little bit wider to connect  to the other one in a way and it's a little bit narrower? It's like there's this thing  where basically you want to be permissive on the input and then sort of like more restrictive  on the output. And by going and doing that sort of thing, you end up building robust  systems over time. Again, this is something that you start to see as a result of looking  at, you know, data formats and stuff along those lines. One of the weird experiences  I had as a programmer early on was working at a biomedical company and I was lucky enough  to work trying to develop translators for, or parsers for a data format that's, that  was designed by some biologists. And generally that's okay because biologists are very good  at producing data formats, isn't that true? Now it's, it's typically that thing. Quite  often people, they know their domain, they feel they can do this sort of thing, they  do it and they make some mistake and they don't really quite understand what the mistake  was. The data format I was working with is something called flow cytometry standard,  FCS. And there was a very cool thing about FCS format. You could save data in comma separated  value and binary. You could save it in arbitrary precision numbers. You could do this. You'd  have all these data formats and headers and all these other things. You could save data  in a million different ways in FCS format, which meant writing a writer was very, very  easy. Writing a reader was a real pain, right? Because you had no idea which of the end formats  people chose to go and write things out in, right? And I think, you know, with that sort  of thing, if anybody that was on the team developing this standard was really aware  of that, they would have said, oh no, we have to be restrictive just because this is going  to be a pain to go and write, you know, readers for this. And I think, yeah, essentially you  don't see developers making that same mistake all that often, you know, because they are  aware of the pain involved in receiving something and having to go and deal with it. So it's,  it's funny about this. Even though you may not have heard of Postal's Law, it's probably  something you've encountered where you probably almost have baked into you now. You almost  have this sense that that's just the way that you organize things. And so, yeah, it's kind  of an interesting thing to be aware of. So let's look at data one last time. What's wrong  with this code? You know, if nobody finds anything wrong with this code, I'm just going to walk  through it and see what happens. Sorry? Too many dots. Okay. But you know, when you think  about it, that shouldn't be a problem because dots take up very little ink, right? So they're,  they're green. You know, dots are green relative to the other punctuation. We have in languages,  right? Okay. That was a joke, right? This is a classic example of a violation of the law  of demeter. Okay. You've heard of the law of demeter, right? And the notion is that essentially  when you have an object, you shouldn't be going in sort of letting your internals out for other  use, right? And there's a bunch of reasons why this can be awkward, okay? One of them  is that basically goes and makes, it puts, it puts the burden on the person who's using  this. They not only have to know how to deal with an account, they have to know that an  account has a calculator. They have to know that a calculator has a superannuary table  and they have to know the superannuary table has to have cells and you address them this  way and it says, you've increased the burden on people trying to understand your stuff  automatically, right? Beyond that, from a dependency point of view, it's possible to break code  that is written this way very easily by going in saying, well, maybe I don't have my calculator  on the account. Maybe I have it on another object that's held by the account. Anything  you do to go and alter internal structure can go and alter these chains of objects and  force recompilation or force test errors and stuff along those lines. And, you know, the  other thing too as well is just from a compilation point of view in a statically typed  language, anytime I touch any of these intermediate classes, it's probably going to force this  to recompile. In a dynamically typed language, you probably want to go and force the  rerun of all the tests for all these intermediate pieces also, right? So, you're  convinced it's bad? It's kind of bad, right? What about this? Is this code okay? It's a  Ruby code. Makes it inherently bad, doesn't it? Anyway, I'm not going to go and paint  this as being an ideal vision of what code should be or anything on those lines, but  I feel more comfortable with this code than with the previous code. This code, even though  it's a little bit terse, you know, it's a little bit weird, it relies upon something  which is more functional in style than object oriented in style, okay? What we have here  is we have a series of transformations. We basically say we have a list of events, we  map each of the events to a date. We go and we pull out the date of each of those events,  we sort all those dates, we do a unique on those which basically goes and gives us all  those dates uniquely, it removes all the duplicates. Then we call each cons too, which is kind of  something which goes and gives me each consecutive one of those things as pairs. Then we map  those pairs into the difference of the dates and then we do a frequency histogram and we  select certain things within certain ranges and stuff. Now I'm sure you guys looking at  this, you're just like, oh my God, that's horrible, it's just terrible. It's code written  by mathematicians that have no design sense or anything like that at all. There are much  tamer examples of this sort of thing that I feel much happier about, but the style itself,  I don't feel bad about regardless, okay? The thing about the style which makes this useful  and interesting is that it's fully transformational, okay? Essentially, there is no state over  here in events which is being modified. When I do a sort, I get something completely new  out, okay? I get basically a completely new array of things and then when I do a unique,  I get a new array of things also and I'm not, there's nothing I can do in a later part of  the sequence that's going to change anything earlier in the sequence, okay? The other thing  too is that generally each one of these things I'm working with is accepting an array and  returning an array back, right? So we don't have that big type issue that we were talking  about a little bit earlier of like, okay, here it's like I've got five different types  of things all the way down to my chain. Here, it's just basically a bunch of array transformations  giving me the things that I want with these things. Can you still get into trouble with  this? Yeah, I mean somebody could take the map operation and move it off of arrays, but  they'd be shot, right? I mean that would just be a terribly nasty thing to do, would break  every base code in the world. But it's interesting to notice that there is this thing where in  a particular context, this demetrious stuff is actually okay and another one it isn't quite  okay. And a lot of it comes down to change characteristics. How easy is it to change  the code when you're doing these things? So it doesn't feel like there's any strict rule  with that. The thing that I think is neat about this is, and I was looking for a word for  this, in category theory which gets, you know, bandied about in like, you know, the Haskell  and functional programming language communities, there's this concept of something called an  endomorphism. And an endomorphism is essentially a function which accepts a type and returns  the same type back. It can do things to the values, right, but it accepts that type and  returns another one back. And you'll see that roughly here, most everything we have is  endomorphic. We accept an array, we return back an array. We accept an array, we return  back an array. And we're able to go and chain things together in a very nice style that  does things well. Now, have I ever seen a framework called Jmock at all? Okay, some  of you have. There's also variations of this in the.NET space, but it's like those very  strongly DSL-ish testing frameworks where you're able to go and say, you know, this.should  equals this, da-da-da. And just, you have long message chains that make the entire thing  read like English. And you might look at that and say, well, gee, it's that, right? But  it's basically going ahead and sort of like dealing with this trade-off. The trade-off  between having things which can cause trouble and break later when people move things and  having something which is a bit more expressive and quite often relying upon this notion of  having the same type used prolifically across the chain of operations. Again, this is something  you basically see when you look at the data that goes between things. Now, it's funny  about all this. The topics I've discussed so far have been kind of, you know, mix and  match. I think the thing that's really common is basically looking at the data representation  between things. And I think that's something that we can try to go and basically pay an  awful lot more attention to as we develop code. And I don't see anybody talking at that  very much in design, so I wanted to share it with you guys. I did want to go and basically  mention a couple of things at the end though about the law of demeter. We just basically  saw cases where what we consider to be the law of demeter is not quite the same as what  we expect it to be. There's other cases where basically undemetered code is okay. So when  you are working in a situation where you have this kind of thing going on, can anybody  explain or anybody describe what systems this sort of thing tends to happen in more than  others? I mean, do you see this in, you know, data acquisition systems or compilers? I  don't know. I tend to see this sort of thing a lot in database-centric applications. I  know about you guys, right? You ever see that sort of thing? It's like I have a bill of sale  and I go and I get this thing, dot this thing, dot this thing, dot this thing, because essentially  you have records and sub records and sub records based upon what your data scheme happens to  be, right? So how do you get rid of that when you're working in an application? How do you  adhere to the law of demeter when you're doing that sort of thing? It's kind of hard, isn't  it? I mean, one thing you can do when you have a data model is you can say, look, I'm  accepting, I'm going to basically say that this one thing is primary like the bill of  sale. And even though it has sub-objects, what I'm going to do is I'm going to place  all of the methods that were on those things, try to push them up to the top and basically  have high-level operations that allow me to manipulate all the substructure without having  to care about it all that much. Does it work? It can at times, right? But we always have  to have a lot of things that are not in the application. And OO applications are database  centric between having like an overarching thing which is a container and then all these  things underneath it need to go and change also. So it's very typical that when you're  working with a database centric application that the law of demeter stuff doesn't quite  work out as well. If you're doing things that are kind of like workflow-y, right? It's kind  of like, I have this one processing step, I pass this thing through and pass it through  another processing step and another processing step. And quite often, you know, you can  have as many demeter violations. You're not pulling into things and stuff like that as  much. I had an experience years ago which kind of opened my eyes to something rather  fascinating. I was visiting a team that had the worst law of demeter violations I've ever  seen in my life and it took me a long time to figure out why they were doing it and why  it was happening. It was a giant Java project and it turned out that what they were doing  is they were, every time a visitor visited their site, they loaded a user and when they  loaded the user, they loaded up every bit of data that could possibly be accessed from  the user and used in that session, right? So you can imagine what that was like, right?  So you basically each time through, you have the session, you navigate to the user, you're  able to go and get the entire history of everything, da-da-da-da-da, all this stuff. And I was  like looking at this and saying, this is just madness. And so we'd look at particular functions  and it's like, okay, you've got this one sub-piece and it goes and talks to this, which talks  to this, which talks to this, which talks to this. And it took me a long time to understand  why that was happening. And the reason was essentially what I was saying. They loaded  everything all at once, right? And in most database-centric applications, what you end  up doing is saying, look, I need to go and have, you know, I need to have a bill of sale,  I need to have a transaction record, stuff like that. And you're basically dealing with  one piece plus it's subsidiaries, it's immediate subsidiaries and you get through joins and  stuff like that, only the piece that you care about. And so as a result, you're going to  have a constrained sphere of what you're working with, right? And it was kind of fascinating  to notice that because they didn't do that, they ended up with far worse demeter issues  than you would have otherwise. And it kind of led me to this notion that essentially  it's like databases are better, are good not just for storing data but also the constraints  that they place on your development. They kind of force you to go and basically sort of get  down to a smaller crux, a smaller change set and the things that you need to pull in and  modify and then push back into your database. So it seemed kind of interesting that way.  So yeah, the law of demeter is a rather powerful and interesting thing. I think the thing that  makes it overlap in this talk a bit is that it is about representation. When you have  a structure that looks like this, essentially you are making the relationship between pieces  very obvious and that's good at times except if that structure changes. So cases where  you want to make things very explicit are cases where the structure of the data holds  information rather than just the data itself. Some cases where that sort of thing happens,  things like parse trees and compilers and stuff like that, it's not that you have an  equal sign and you have a variable and you have a literal, it's the entire tree structure  that has a meaning. So as a result, you want to have structure data like this. It ends up  looking very undemetered in a way, right? But I think as programmers we just need to  really be aware of when we need that kind of thing, when we need a complicated data  representation and when we don't. And you can essentially go all the way back and start  looking at all the different ways that you can see data each step in your program and  quite often it gives you insights that you would not necessarily have had otherwise.  So anyway, that was my weird talk. It's all about data representations across things and  it ties in different aspects of design. Any questions or comments? Yes? Yeah, sure. There's  one and there's the other. Yeah, okay. So the comment is basically the problem is that  this is basically taking you deeper into the object and these are all off of the same object,  right? And that's fair. Basically all these things are things which are unenumerable or  array and so they are just aspects of the same protocol. So yeah, it's just essentially  that's the thing. And quite often, I mean, it's funny because now that this has become  more popular, many people are going and adopting this point of view. They're basically saying  that a key aspect of demeter is the amount of type exposure that you have. It's like how  many types are you really exposing in a context? And in this case it's generally just one and  that makes things a lot easier to deal with. That's fair. Yes? Okay. Okay, the question  was if, would I say that if we're using ORM we would end up with a structure like this  and that's bad and that we should avoid it? I think it's really data has to be structured  in certain ways, you know, in order to go and make it easy to add, you know, access. The  representation that you use inside of your program doesn't have to be exactly the same.  It's probably going to map quite easily. My bias is always with that to pull up only the  amount of information we need and in the form that we care about into objects that may not  use this much. But there's a real tension there because then you can end up going and  producing a lot more classes to represent the same data in a way. So it's, I think it's  just really a fundamental tension. And, you know, the indication that really is a fundamental  tension is the fact that ORM is not a solved problem. It's always causing people trouble.  That's just inherent to the mismatch between database schema and stuff. So, okay. Any questions  or comments at all? Okay. I think I'm a little bit early, but thank you very much.  Thank you very much.
Michael_Feathers_-_Deep_Design_Lessons_0_95.png Michael_Feathers_-_Deep_Design_Lessons_96_419.png Michael_Feathers_-_Deep_Design_Lessons_420_791.png Michael_Feathers_-_Deep_Design_Lessons_792_1559.png Michael_Feathers_-_Deep_Design_Lessons_1560_4523.png Michael_Feathers_-_Deep_Design_Lessons_4524_5135.png Michael_Feathers_-_Deep_Design_Lessons_5136_5195.png Michael_Feathers_-_Deep_Design_Lessons_5196_6275.png Michael_Feathers_-_Deep_Design_Lessons_6276_6371.png Michael_Feathers_-_Deep_Design_Lessons_6372_6563.png Michael_Feathers_-_Deep_Design_Lessons_6564_7715.png Michael_Feathers_-_Deep_Design_Lessons_7716_8303.png Michael_Feathers_-_Deep_Design_Lessons_8304_8735.png Michael_Feathers_-_Deep_Design_Lessons_8736_8843.png Michael_Feathers_-_Deep_Design_Lessons_8844_8975.png Michael_Feathers_-_Deep_Design_Lessons_8976_9167.png Michael_Feathers_-_Deep_Design_Lessons_9168_12515.png Michael_Feathers_-_Deep_Design_Lessons_12516_12563.png Michael_Feathers_-_Deep_Design_Lessons_12564_18263.png Michael_Feathers_-_Deep_Design_Lessons_18264_19775.png Michael_Feathers_-_Deep_Design_Lessons_19776_21863.png Michael_Feathers_-_Deep_Design_Lessons_21864_22079.png Michael_Feathers_-_Deep_Design_Lessons_22080_24311.png Michael_Feathers_-_Deep_Design_Lessons_24312_24515.png Michael_Feathers_-_Deep_Design_Lessons_24516_25007.png Michael_Feathers_-_Deep_Design_Lessons_25008_27035.png Michael_Feathers_-_Deep_Design_Lessons_27036_27155.png Michael_Feathers_-_Deep_Design_Lessons_27156_27419.png Michael_Feathers_-_Deep_Design_Lessons_27420_30023.png Michael_Feathers_-_Deep_Design_Lessons_30024_31403.png Michael_Feathers_-_Deep_Design_Lessons_31404_37427.png Michael_Feathers_-_Deep_Design_Lessons_37428_37499.png Michael_Feathers_-_Deep_Design_Lessons_37500_40163.png Michael_Feathers_-_Deep_Design_Lessons_40164_43199.png Michael_Feathers_-_Deep_Design_Lessons_43200_43331.png Michael_Feathers_-_Deep_Design_Lessons_43332_43931.png Michael_Feathers_-_Deep_Design_Lessons_43932_44099.png Michael_Feathers_-_Deep_Design_Lessons_44100_44507.png Michael_Feathers_-_Deep_Design_Lessons_44508_44603.png Michael_Feathers_-_Deep_Design_Lessons_44604_45659.png Michael_Feathers_-_Deep_Design_Lessons_45660_46043.png Michael_Feathers_-_Deep_Design_Lessons_46044_46331.png Michael_Feathers_-_Deep_Design_Lessons_46332_46415.png Michael_Feathers_-_Deep_Design_Lessons_46416_46931.png Michael_Feathers_-_Deep_Design_Lessons_46932_47447.png Michael_Feathers_-_Deep_Design_Lessons_47448_54947.png Michael_Feathers_-_Deep_Design_Lessons_54948_55163.png Michael_Feathers_-_Deep_Design_Lessons_55164_55235.png Michael_Feathers_-_Deep_Design_Lessons_55236_56219.png Michael_Feathers_-_Deep_Design_Lessons_56220_58502.png