 My name is Jim Wyrick. I'm the chief scientist for edge case. We do Rails applications and some iPhone apps There's my Twitter handle if you want to follow me on Twitter And there's an email address if you want to contact me with questions after this this presentation is on github So there's lots of references at the end of this So I've probably flipped through them fast by the time we get there So if you want to see what they are you can go to github and download it and I got lots of links and things in that Okay, I want to start off this talk with a question How do you recognize a good design? Think about that for a second. I asked this question a lot in telephone interviews that I do with people just to see if they can put into words What they think a good design is and the most common response I get over the phone is I Maintained that some designs. It's easy to tell that they are not a good design Actually, I got a lot of pictures like this, but what this will do for right now Who can tell me what this is? The George Washington Monument Baltimore wrong But good guess good guess. I'll give you some hints. It's in London. Okay It's a monument. Okay The monument in London It's on Pudding Lane Put it's the pudding money the monument the banana pudding. No, it is not It has it is a memorial for an event that happened on September 2nd 1966 does anybody know what happened on Excuse me. 1666 The great fire of London. Yes A baker supposedly a baker left some burning coals in his Stove overnight and he was sleeping upstairs and a spark from that caught fire and all of Pudding Lane went up in flame and Because Pudding Lane was kind of in the poor part of town They weren't really concerned about getting everybody there and getting the fire out and because they delayed because of political Manipulations because all kinds of factors that would end and there was a drought that year, right this fire Spread it was terrible. It burned for I Think it was five days It got so hot it melted the lead off of the roofs of buildings It it made the bricks explode from the pressure that built up inside because of the heat Here are people escaping the fire Carrying their belongings and another contributing factor to the fire was people were more Concerned about escaping the fire than to put out the fire. Finally, some people got together They started pulling down the buildings They made a fire break and they were able to get it under control But not before it burned down a good part of London in that day You can see the London wall there in black and a You know three-fifths or fifths of the city was absolutely terribly burned down because of that You can see St. Paul's Cathedral here. You can see the Royal Exchange up there. It was a terrible terrible event Six days after the fire were out people were already designing the new London Christopher Wren you might have heard of him. He had something to do with the dome at St. Paul's Cathedral He also designed that monument with the gold ball on top of that commemorated the fire So here's a great architect Six days after the fire he submitted to the King a plan to rebuild London. This was his plan. This was his design and You can see there's a certain cemetery. There's a certain flow in this and it looks like this the angles of the street You can see the St. Paul's down here You can see the Royal Exchange up here and and a very nice it looks very nice How would you judge this design? I don't know. I'm not good at judging city layouts. So I'm not sure Here's another design. This is by John Evelyn. He wanted to create the most noble city that can be he wanted to rebuild London from the ashes and a lot of angled lines a lot like Christopher Wrens But a little simpler a little more symmetric. I like symmetric. That was cool Here's a design by Robert Hook Robert went in for the square. I think this tells a lot about the Artistic flair of the individual architects kind of how they put things together So he has a very square shaped very grid oriented architecture here for the city Here's one by Valentine night He had seven or eight north south roads with lots of lots of lots of little tiny lanes Running east and west so he'd get a lot of houses building because he was very concerned about the people being able to Provide housing for all the people that were who lost their homes However, he made this suggestion that they should use fines and taxes to raise the money to rebuild London Charles II the king at the time had knight arrested for Suggesting that he would try to profit from so public a calamity of his people Where are those kinds of politicians today? finally, we have a Richard Newcourt's design and You can barely see it here, but here Right here is the original wall Now the interesting fact about this design is that not only is he rebuilding the part that burned down? He wants to tear down the remaining good parts and rebuild them from scratch as well Needless to say this design didn't go over very well but here we have five designs all submitted within weeks of the burning of London and The burning question now is the burning question the question now Is which of these designs did they choose to go with? none None of them They did not choose any of them. They decided actually they didn't decide so they rebuilt the city Incrementally from scratch as they needed as they got to a part. They just rebuilt it as it went No upfront design an incremental build that sounds like maybe this was the first agile project Okay, so now agile. I was introduced to agile by Bob Martin. Who here has heard of Robert Martin? Very good. Excellent. Who has read his book? Good. So so you know the the five Principles of solid design. Yes You know you want to teach this? Oh, he's thinking about it. He's seriously considering it Right and in the the late 90s early 2000s Bob Martin sat down and said we need some principles for design So when we look at a design we have some More or less objective ways of judging good designs from bad designs. It's not just this Artistic feel-good thing. No, it's got lots of angles or no, it's got lots of squares. It's it's more objective So he sat down and actually thought this out and gathered up Five principles. They're not all original with him But he kind of put them together in a package and these are the five here And if you take the first initials you get SOLID for solid therefore the solid design principles So what are the solid design principles? You can read about them here at this My hand when I put it in the pocket. There we go. Now we're back. I don't know what happened But the solid design principles are all about Dependencies because that was Bob's big concern at the time how to build maintainable code and you do that by managing your dependencies Now why are dependencies important? Suppose you have two classes like a car and an engine and your car object uses the engine object But the engine object doesn't know anything about a car. It just does its job The car uses the engine so there is a dependency that goes from a car object To an engine object and we denote that in UML like diagrams by an arrow, right? So arrows in UML always point in the direction of a dependency So this is a using kind of dependency here. There's also a dependency Okay This arrow means that car objects are allowed to call methods of engine objects Engine object will not call anything on a car because it doesn't know about a car There's also an inheritance type of dependency That's also an arrow but a different style of arrow But it is also a dependency that the car object depends since it inherits from vehicle. It depends upon vehicle existing So this is another kind of dependency again denoted by an arrow Okay, so why are they important if I were to change? Module X on this diagram Change the interface change the semantics of one of the methods in module X who was affected Absolutely everybody so dependency tells you how changes propagate through your system if I change X Then ABCD and E are all affected by that change. So I Have a great deal of Pressure upon me to not change X or to choose X. So it is a type of module that is does not tend to change How about this diagram? If I change Z in this diagram who is affected? Z alone because nobody else cares about what happens to Z Z uses everybody else. What if I change module C? Who is affected? Z is affected so in this diagram We have isolated the propagation of changes So if C changes Z might be affected and we have two modules we might have to look at but everybody else is probably okay Because there's no direct dependency on those Well this one what if I change module L who's affected Well, definitely K is affected because K has a direct reference to L Possibly maybe J is affected because if K changes we might force some changes back here So there was a kind of a transit of property to dependency So this is the basis for the solid principles Bob looked at these kinds of ideas and said okay How can we manage dependencies and software? Now in the late 90s and the early part of this millennium Bob was mainly concerned about Java and C++ programming. So he was mainly dealing with static statically typed languages Now we know that statically typed languages have stronger dependencies Than a dynamically typed language like Ruby So it makes you wonder these solid principles Do we just throw them out the window when we're dealing with Ruby since Ruby is dynamically typed Do they do they apply at all and this is the purpose of this talk? I want to kind of get down into Some of these questions and look at each of these principles and see how they apply to a dynamic language like Ruby So let's just start with the first principle single responsibility principle says that a clash would have one and only one reason to change Here's a good example of this in the rake project that I maintain There is a module called the application and Then this is the top-level object that kind of sits and starts up when rake starts up and it goes and parses all the command line arguments and then also Reads the rake files and and defines the task and holds those tasks in a hash So it actually has two jobs So it has two reasons to change number one if we change the way the command lines are Handled it needs to change number two if we change the way that we store tasks. It needs to change So the single responsibility principle says we should probably break up these two reasons to change into two separate modules Maybe maybe perhaps something like this We have an application object and he uses a task manager and the task manager Manages the storage of all the tasks and keeps track of them and the application then is just dealing with command line arguments Now there's actually a good advantage to doing this because once I broken the task manager off into a separate object I can create nested Structures of task managers to handle named scopes in rake And although rake doesn't do it that way right now This might be a good way of handling that and I might want to change rake in the future to do this So splitting things up gives you a lot more flexibility in the way you build your code Even in a dynamic language single responsibility principle is an important principle to follow I feel and any disagreement You guys are an easy crowd An easy way to tell if you're violating the single responsibility principle is to write down the purpose of That module and you have to if you have to use the word and or the word or in the description of your task Then it's probably doing two things. So that's a nice easy rule to follow. I love these slides Okay, open close principle is I was first thought up by Bertrand Meyer who designed the Eiffel language And he says that module should be You should be able to extend a class's behavior without modifying it should be open for modification But close for change I say that right open for extension close for my modification. Thank you. Yes Which sounds weird what what in the world does that mean? Suppose you someone wrote a really cool library that was really useful and a lot of people use it and You are writing an application now and you would also like to use this really cool library But there's a problem because you see this really cool library works 90% But there's 10% of things that it doesn't do that you would like to have it do So you need to change it to work for you Well, the typical way we do this in software is called Copy and paste and this is a valid design technique so I've been told and then you have a modified version of that cool library and Unfortunately any bug fixes applied up here will not be applied to your modified version So you learn you lose all that kind of advantage. What is a better way of doing this? Fork the modern copy paste hire someone to fix up the back fill all the bugs yes A better way is to subclass the one Change the the behavior you need in your subclass and then your application can use the subclass version that you have somewhat of a Control over and yet the original one is still there and any fixes that go into that will be picked up by yours automatically Okay, now now there's an extra dependency here And that's fine. I mean we you can't write software without dependencies But but you gain that you gain great benefit by doing it this way okay, but You say Ruby has open classes So let's let's just monkey-patch it Think about that one Here's this is a real a simulated real-life example This is not the real code, but this is very very very similar to something that happened in real life To a rather outspoken former member of the Ruby community Who could kill you with his thumbs? I think you know who I mean Here's a logger the interesting thing about this logger is it provides a way of changing the format string so you have a way of going into the logger and You have some control over the the format not a great deal of control but a little bit of control by specifying a format string And we're going to do that in our application Yeah, here we go. So we set the log string here So we see LOG colon in our log string and it comes out down here. We can see that and we run it Now we include that really cool library and we run our program again and whoops What's happened here our LOG colon message is gone. It's disappeared and what they've done is Gone into the cool library has opened up the class and replace the format command because they needed some more Explicit control over the formatting well They went in a clobbered software that your code depends upon and has broken your code so Rather than the principle of least surprise are Infamous former Ruby community member called this the principle of most heinous arsenic injection So is there a better way? Yes Derive your cool logger from the simple logger implement the behavior you want then in your cool library You can use your subclass one and you won't affect anybody else using the original logger at all This is a better way better way to design so open-close principle still important in Ruby prefer to subclass rather than opening a class and modifying it now this one begins to get interesting the dependency and version principle and I love I love this would you solder directly into a socket? You know it scares me but I know some people who would it says depend upon abstractions and not Concretions, I love the word concretions is that it where it is now because I said it is I like to mess around with hardware Did anyone see the blimp demo in here the other day? Okay? This is my Arduino project not nearly as impressive It just is a Pomodoro timer that raises a flag when you start your 25 minutes and that at the end of 25 minutes It will lower the flag Yeah, the blimps more impressive, but this is fine The reason I introduce this is because my next example is going to be a hardware based example And I actually use this in telephone interviews I ask people to do a design problem over the phone It's a very simple problem to design a embedded hardware system software for an embedded thermostat that controls a furnace So you have thermostat software and you have a furnace software and you might do something like this You can turn the furnace on and you can turn it off So it has on and off methods and the thermostat calls that Now remember solid was written with Java in mind. So let's just look at the Java Solution here to kind of motivate where it comes from Here's your furnace class. It's got an on method and an off method and then the thermostat Here's the run thing and if the furnace should be on you turn it on it Otherwise you turn it off really simple control module here Anything interesting about this code Yeah, yeah, look we reference furnace explicitly in this code We are hard-coded to use a furnace object That means this thermostat can never run any other bit of hardware other than that particular furnace What we want to do instead is to introduce an interface between the two and we'll call this an on-off device Now the thermostat uses you on off device Which is just interface and if you're not familiar with Java interfaces are just method signatures with no Implementation with them. Okay, then furnace implements on and off and it inherits from the on-off device So here's your inheritance dependency Here's your using dependency and notice that the original one had all your dependencies going this direction and the new code has Dependencies going the other direction Yeah, turn towards the audience when I say that this way Therefore dependency inversion we're changing the direction of the dependencies now the nice thing about this is now We have this interface. This is what an interface looks like in Java and our thermostat code has removed all References from the furnace object. We now references on off device, which is an abstract interface It's not a particular device And the advantage of that is now we can implement all kinds of objects that conform to the on off device Interface and our thermostat can control them as well. That's really nice. I like that so Java people have it ingrained in their heads to code to Interfaces and I'm sure that the Java people who saw that original code is a no interface, right? Okay, so this is like ingrained in their minds So what does Ruby look like? Here's the furnace code very similar to the Java version and Here is the thermostat code Before I changed it to conform to the dependency inversion principle now watch very carefully the next slide will be this code After I've applied the dependency inversion principle. You ready? There's no explicit references to furnace in this to begin with so kind of the whole dependency inversion thing falls off automatically So we begin to wonder does does that mean that dependency inversion is not important in Ruby that we can just ignore it Well, you think about that. I've got a rant to do Some people feel that they must check The type of an object passed in in order to be type safe And so they will embed is a test throughout their code and there are exceptions unless that object is the proper type Notice what we've done We're back in Java mode thinking because now we have an explicit hard-coded reference to furnace and this code will no longer work with arbitrary on-off devices in Ruby you have to work extra hard to do what Java does normally Keep that in mind Okay, so What do we put here in our diagram for Ruby? Well Ruby doesn't have explicit interfaces, but it has something Conceptually very similar. I like to call them protocols now protocols are not coded There is no nothing in if you if you go into GitHub and you look throughout a code base You will not find a file that has the protocol in it, but that doesn't mean we don't use it Right? So thermostat is written with this idea of something I can turn off and something I can turn on and Then we write the code here that conforms to that idea that protocol So protocol is just a list of methods with certain semantics Certain semantics keep that in your mind too because we'll get to that in a little bit Okay, so protocols are important in Ruby So the guy who wrote that that that is a task now. He said okay protocols are the important thing Let's let's do this. Let's check for the protocol Okay, we no longer have an explicit reference to furnace But I'm not sure what value this brings to the table and in fact a friend of mine has suggested that instead of duck typing This should be called chicken typing because you're just scared to use the facilities Ruby Ruby gives you Okay, example XML builders library. I wrote it generates XML Very easily and it writes the XML to a target object So here is an example usage of XML builder. You give it a target and here I'm giving it a string So it's going to build the XML into the string and this creates a person Nested with a name nested inside of that with a value for that name so it builds up a nested XML It's a really nice library and we print that out here and it all works Because the target here is explicitly defined to take a shovel operator So let's go back and look at this. So this is the target. I got to give it an object What kind of objects can I give it the documentation says anything that responds to the shovel operator? And takes a string and returns itself so a Method with a signature with semantics That's a protocol So I've explicitly called out the protocol in the documentation for XML builder that means I can go and I can give it Say a file because files in Ruby respond to the shovel operator So XML builder can write into a string or it can write into a file or it can even write into an array There's some reason you want to do that or it can write into any object that supports the shovel protocol Which is kind of a standard protocol in Ruby anyway, so that's easy to define Okay, so code to protocols. This is our Mantra in Ruby rather than code interfaces code to protocols and think about protocols when you're designing your system protocols are important Barbara Liskov just won She just won an award at UPS law, right? I don't remember what that was. Does anybody know the Turing Award? She got the Turing Award and gave a speech at UPS law here just like within the last month or so Barbara Liskov a number of years ago came up with what's called the Liskov substitution principle and I'm not going to read that for you It's very mathematical very precise very academic But essentially what it says is that derived classes should be substitutable for their base classes This is a very Java like formulation. This is what Bob Martin says Personally, I kind of like this definition of the substitution principle Dave Thomas just calls it duck typing But walks like a duck and talks like a duck and treat it like a duck and you'll be good okay, so When is something substitutable when can I take one object and substitute it for another and expect my code to work? This is a very important question. I think it goes to the roots of The power that object-oriented design gives to you and so it's very important to understand Substitutability and OO design I'm going to start with this example here suppose we have a module called normal math opposed to abnormal math and It has a function called square root in it and this algorithm here I don't know if you recognize it or not. This is Newton's iteration a fixed point iteration for Approximating a square root and it stays in this loop until the answer your answer here is within Three decimal places of the correct value of square root So this is a very interesting piece of code we can Write another version of this we'll put it in a module called accurate math And it does exactly the same thing except its termination is Five decimal places So it is more accurate is the answer you get back should be it Well is guaranteed to be more accurate than the guarantee of the previous one We also got a module called sloppy math that does the same thing, but it's You know, yeah, we're close. That's good enough for government work You're called a government math, so now we can ask two questions about the square root method What does the square root require before you can call it and what does the square root method promise you? after it is finished and it returns Require and promise think of those things okay So square root requires that your input be non negative It must be greater than or equal to zero because if it were less than zero you'd get a complex number back We're not going to handle that Which is okay? It promises that the answer your return is going to be within Three decimal places of the correct square root value So you got a requirement and you got a promise two things. This is called the contract For this method Anyone worked with any design by contract? Okay, a few of you a few of you this comes right from Bertrand Meyer right from the language Eiffel Which I absolutely love I Think it's good to learn Eiffel for this reason Truthfully though I wrote more ruby code in six months than I did in three years of using Eiffel So take that in mind, but but but the disciplines the things you learn in learning Eiffel is well worth it Okay, so you asked a question about does it require what does it promise? Now here's some client code. We are using The square root here and this is how we'd use it you pass in the math object You call square root on it and you're finding the distance between point a and point b Now There's actually two views of the contract. There is the view from square roots point of view what I'll call the actual contract and that is What is actually being provided by that module? There's the expected contract over here and That is what the client expects Now if the expected contract its requirements and promises match exactly to the actual contract then everybody's happy It works software will work Well, that's a bold statement How about if we made a better promise what what if instead of saying? Three decimal places the actual contract says five decimal places If I substituted accurate math in for normal math and the client used it would this software still work Most likely yes because the promise is better It's like you make a contract with someone to mow your yard and the requirement is they mow it once a week and they do it Twice a week Hey, that's all the better right. It's a better promise. It's a better delivery than what you expected. So that's okay But what if we go the other way? What if the promise is weaker than what's expected? We're expecting three decimal points of accuracy And we're only getting one decimal point of accuracy Will this work? Maybe you don't know you don't know so because the contract is broken at this point So weaker promises worse promises are what make objects non substitutable So accurate math is substitutable for normal math, but sloppy math is not Substitutable for normal math. So now we've got some rules and substitutability. We're beginning to understand that yes in the back Possibly although You may also have Yeah, there there may be a performance aspect involved in that and if the performance aspect is important You should probably make it part of the contract and that way you can evaluate whether it's substitutable or not Yeah, okay. Yeah and good point there Okay, so now let's let's look at this. This is complex square root and It makes no assumptions about x because we explicitly check for it And if it's going to be less than zero we will return a complex answer rather than just the regular real answer So the contract for this guy. Oh same same accuracy guarantee the contract for this guy Says in the requirements anything goes. This is a looser requirement Because it accepts a wider range of inputs now is complex math Substitutable for normal math Yes, because normal math because the client will always pass in a value that is greater than or equal to zero He will never explore the area where the number is negative. So yes, you can substitute in Complex math and a less restrictive requirement is okay If it is a preconditioned the requirements of the preconditioner on the client If the client does not match the precondition there is no requirement on the supplier software to do anything if the client expects an exception It's got to be part of the requirement. It's got to be part of the contract So and and we can go we can go into deep discussion about that So I kind of want to not if you want to talk to me afterwards I'd love to talk to you on that one because that's that gets in the whole way you design exceptions And my take on exceptions just a little bit different than a lot of people So I'd love to talk to you on that one But yes, yes, if if you have a precondition that says you cannot pass me a negative one And I pass you a negative one then I might throw an exception or I might not But if you expect me to throw an exception then that is part of the expected contract Okay, that would be that would be the promise. Yeah, I promise to throw an exception Okay, if I actually wrote a square root module that required and to be greater than zero This is a more restrictive requirement This would also fail because I might pass it a zero from the client in which case who knows what happens We're breaking the precondition. So More restrictive requirements not allowed So the answer the question the reason we went into all of this was to decide when can I substitute one object for another object? Okay, and we can summarize these rules very simply Require no more promise no less so the contract I'm providing has to require no more than the contract you're expecting and Promise no less than the contract you're expecting if you follow these rules This is the definition of duct typing If an object follows these contract rules, then it is a duct type and can be substituted So I really like this. We've now got a formal definition for what it means to duck type I think that's a very powerful idea How are we doing our time? I got two more principles. Can I make it? That's a break keep going We're getting close. Okay We'll go through this fast Okay, make fine-grained interfaces that are client-specific is this rule Here's a hardware piece of hardware that has a keyboard and a displayer Connected you can actually buy this one. This is called the wild card keyboard and display So if I were to write this in software I might write a module that looks like this and an interface that looks like this and software that uses it like this What's wrong? Remember the rule that you want to depend upon things that tend not to change Here this display menu is depending upon an interface that has keyboard and display methods in it So if the keyboard changes that may impact your display menu It's not a good thing. So The interface segregation principle says segregate your interfaces and do separate ones So the display menu display depends upon only display things and the get selection method Only depends upon keyboard things and your dependencies are much nicer. So so this is a better design This is all ISP says The other nice thing about this is that you can Okay, okay, why why okay? you have fewer reasons to change and Easier to implement alternatives So if I get new hardware that has a separate keyboard a separate display I can implement them like this and it's easier to implement. I don't have to implement Display things in this keyboard that doesn't have a display, right? So that's good. But again In Ruby, we don't deal with interfaces. We've got these protocols So the Ruby formulation of this is really quite simple. It says clients should depend upon as narrow a Protocol as you can get away with Okay, you can see that in the builder Builder only uses the shovel operator to put things into the target So anything that implements shovel that's good enough. Suppose you have an array that you're using as a stack I would suggest sticking to push and Pop operations on that array and not general-purpose array operations allowing you more flexibility In substituting objects in there. So depend upon narrow protocols is the Ruby version of this principle Okay, I Got through all five. I only had one left instead of two I Got five. I got four minutes Now instead of questions from the audience, I'm gonna ask you questions For discussion Active record Implements a domain concept and a persistence concept in one object Does this violate the single responsibility principle discuss? Yes, it does Absolutely, does it bother us? Okay, yes, it does. Okay, why I'm curious So designs become more complex the interactions between all these different interfaces Become more complex and it is less convenient to do that. Good point. Anyone anyone else have feedback on this. Yes Makes it harder to test. Yes. Yes, it does Especially if you want to segregate database related tests from just business logic tests and since they're going against the same object inconvenient Okay So we heard from three people who are kind of negative on this. How is anybody okay with this? You're okay with it. You want to defend your position you can move the Domine layer to other place. I mean Active record. Maybe it's not a Partner Some kind of really huge pattern, but it's good. I don't know what I mean Okay, the key the key to these design principles that they they are just principles They are not ironclad rules that you must follow under all conditions But they give you objective reasons for discussing designs and you can say I don't like this design because it kind of violates single Responsibility principle or you got kind of a dependency inversion problem over here, you know, you can talk about these things Okay, this is this is in with regard to contracts If you decide you're going to write contracts for your methods or at least think about them sometimes you can't express them as logical Conditions, what should you do about that? We think requirements. Okay, right tests. Okay, that's good It's really interesting post conditions in particular post testing testing Testing there we go. Um, I've table problem. There we go. It's a dependency injection problem You I'll try this again. I'll hold this. Okay. I'm not touching the court Post conditions and unit tests are very similar. They do the same thing. However, they do them differently Unit tests give specific very understandable Examples, but it's a finite number of examples post conditions state the truth for all Possible invocations of that method. So it turns out that a lot of times post conditions are harder to write than unit tests But because they are more general sometimes they provide more value. So that's an interesting point Sometimes are harder to not implement in fact sometimes force for simple for simple methods The post condition is essentially The body of the code as well. So yeah, you're just restating what the implementation does. Yes Okay Just a couple more questions. I'm going to go through these real quick because we're out of time But I want you to think about these how do you verify that your client software only uses a narrow protocol on the objects that you're using How if you only want to use push and pop, how do you make sure your software doesn't use anything else? Think about that one Solid principles were written with static languages in mind are their principles That might apply to a dynamic language like Ruby That do not apply to a static language like Java Think about that one. I think if you have any answer to this one I'd love to have a discussion with you afterwards because I'd love to hear your thoughts on that Okay, so summary I Think the solid principles are important. They don't map exactly but there's a lot of good information in them that we can Apply to our Ruby designs Okay Thank you