 Take it away Sandy. Thank you. Can you hear me? Yes, Sebastian. Can you hear me? So I'm Sandy Metz. I'm from Duke University. I know that you guys some people think of the as as universities as being just like the enterprise except with lower standards. There's a little truth in that. Honestly, there's a little truth in that. But also universities are places where we get more freedom to do to to be out on the leading edge of software and because of that at Duke, I'm sorry, they told me to stand back here in the light. At Duke, we've been writing small talk apps since the early 1990s. So I have 10 years of experience of small talks. There's if this were a weekday, there would be someone in my office doing maintenance on a production application that's written in small talk that does grants management at Duke University. They would be working on it right now. We're a little bit different than a lot of you in that we don't have millions of users. We don't have high transaction rates. We don't have huge piles of data. But what we do have is applications that have been around a long time and that get changed really every day. So the apps that I write, I don't actually use myself. So you can't really say that we eat our own dog food, but I look at leftovers every day and I have for a long long time. But enough about me. Let's talk about you. Because you're here, I'm guessing that you've written an application. And even though I've never seen that app, there's something I can say about it. It's going to change. And when it changes, what will happen? The experience that you have when your application changes depends on its design. For example, your application might look like this. It might be rigid. Rigid applications. In a rigid app, everything is connected to everything else. And when you make a single change over here, it changes this thing and this thing and this thing. And it's attached to the thing at the far ends of the app. Any change you make, no matter how small it is, causes a cascade of related changes. Or your app might look like this. It might be fragile. Now fragility is a lot like rigidity. The difference between fragility and rigidity is fragile apps are rigid, but you can't tell by looking. So in this case, what happens if you move a wire? You can't predict. This application is both rigid and fragile. Or your application might be immobile. Now immobility is that quality where you'd like to reuse some code somewhere else. But you can't extricate it from where it is. And so what you end up doing is you copy a section of code and you take that same section of code and you put it somewhere else. You don't actually have reuse. You have reuse through duplication. Now just in case you find this image a little wiggling, let me assure you that no dolls were harmed in the making of the conjoined twin mummy. You do have to wonder about the guy who made the doll took the photo and put it on flicker. Anyway immobility. And finally there's viscosity. Now viscosity is when it's easier, when you want to make a change and you can tell how the original designer wanted you to behave. But it's easier to do the wrong thing. And so in this case you just throw another piece of tupperware in that cabinet, shut the door and tiptoe away. And you try not to pay attention when the door opens and stuff falls out on the floor on someone else. And you pretend that that didn't happen. So how is it that your app is rigid and fragile and immobile and viscous? It didn't start out that way. In the beginning your application was perfect. It was perfect. It was like a beautiful flower. It was a joy to work on. And then it changed. And that's all that happened. It changed. When you make changes that introduce unexpected dependencies in your app, they will kill you. And I'm here today to talk about how design can save you. Ooh that was fast. Okay I'll slow down. This is a picture from Martin Fowler's website. Across the horizontal axis is time. The vertical axis is accumulative features. And this is a picture, there's two lines plotted on this graph. The red one is good design and the blue one is when you write code and you don't do design. And as you can see there's a point where those two plotted lines cross. And what this graph illustrates is that as time passes and you add features, there's a point in time when you're better off to have done design. Design was like TDD, right? Early on design takes time and tests take time. And at some short interval in your app you can get more done feature wise if you don't spend time on those activities. However, and actually this graph suggests that you should skip design if you plan for your application to fail. But if you think it will succeed, it's going to continue to cost you money and design will pay off. And actually the interesting thing here is if you get to the point where you're in a parent success and you haven't done design, you push enough features out the door in a short period of time, you can guarantee that you'll fail later when they ask you to change it. Because at that point you'll be unable to do so if you have not done good design. So today I'm talking about the solid object oriented design principles. This is an acronym that was made up by Robert Martin. And the four terms I just used to describe rotting software, rigidity, fragility, immobility and viscosity are directly from this paper of his called Design Principles and Design Patterns. It was written like in 2003, the early 2000s, he didn't make up all this object oriented design stuff. He didn't make up some of it, but not all of it. A lot of what he did, he's rightfully, he's famous and rightfully so for taking a lot of ideas that were floating around and giving them all names and making it so that we could talk to each other about them. So he deserves a lot of credit for that. The acronym that they use, the principles that he made form an acronym that they've called SOLID. And so here they are, the five solid design principles. Now you've probably heard of some of them, but I doubt that you've heard of all of them. I'm going to run through them and give you a brief definition of each and then we're going to come back and talk about them a little bit. S is for single responsibility. There should never be more than one reason for a class to change. O, open, closed. A module should be open for extension and closed for modification. It's interesting, on the face of it that seems impossible. It should be open, you should be able to change it without actually changing it. L is for Liskov substitution which is about subclassing and in case you're confused there's this handy formula to make it more clear. I is for interface segregation. That seems total gibberish. I completely agree. D is for dependency inversion. Dependency inversion says you should depend upon abstractions and not concretions. Okay, there they are, the five of them. Now, they seem so academic, right? How can this help? Like so much of the stuff that we talked to you about, these things tell you a state that you should be in when everything is right. But they don't give you much guidance about how to get there. I'm going to turn them around and talk about it. Well, actually, let me say one more thing. They seem like they're all about different things. Some of them seem orthogonal to each other, right? Open, closed is a goal. Single responsibility and dependency inversion are coding strategies. Liskov is that confusing formula. And interface segregation doesn't even seem to apply to us. But they share a common theme and they're about managing the dependencies in your application. These principles are strategies that you can apply to lead you to a place where your application has minimal entanglements with each other so that you can change things. Everything that we do design-wise is really about dependencies, no matter how people talk about it. If you're entangled with another object and it changes, you have to change. These entanglements are what makes your code rigid and fragile and immobile and viscous and it's a death spiral. If you get caught where things are tangled, it's very difficult to make changes without those changes cascading throughout your entire application. These are words, actually, these are not Robert Martin's words. These are Steve Freeman and Nat Price's words. And it's just another way of describing this exact same goal. These guys say that your code should be loosely coupled, highly cohesive, easily composable and context-independent. Now these are just different ways of saying the solid design principles. Loosely coupled is about dependency and injection. You should inject dependencies. Highly cohesive says a class should all be about the same thing. It should have a single responsibility. Easily composable and context-independent, it's exactly more talk about dependencies, right? Your app should be made up of a bunch of context-independent objects that you can rearrange to get new behavior without changing the actual code. Oops, sorry, here, wait. So all of this is just about strategies, actually, we're going to move on. All of it's about strategies to achieve independence. So I'm going to rearrange them so that we can talk about them. And I'm going to talk about them from the bottom up. And fortunately for you, we're going to throw some of them out right now so we can just skip them. Interface segregation is something that you care about if you're statically typed language. If you're writing C++ or you're writing Java, when you have to deal with another class, you have to deal with an instance of that class and have an interface. And there's a bunch of rules about how to make the interfaces smaller so that when they change you don't have to recompile the whole system. Because you are smart enough to pick Ruby and you're using a dynamic language, you don't have this problem. The language itself, in this language, your dependency on another object, you depend on the signature of the method you're calling. And otherwise the object is a duck. And so dynamic language is by their very nature, obey this principle in the most extreme way possible. So we can just quit talking about it. Oops, sorry, over here. Sorry, too many clicks. There we go. We're not going to talk about it again. ListGov is about subclassing. Now tell me, I would be interested in knowing who in here has written a class that they then created their own subclass of. This is outside of the active record stuff. Somebody wrote a domain object, some kind of a model object, so a few of you, but not very many. Right, less than half of the people here. Okay, ListGov talks to you. What ListGov says is if you make a class foo and you create a subclass foo-ish, any place you use a foo, you ought to be able to substitute a foo-ish. Simple as that. If you don't do that, if you subclass it and then you return like different things from the method calls such that your callers have to say is kind of to figure out what to do with the object they have, you have violated ListGov and you've created a dependency. Is kind of is your code smell in that case? Don't do it. If you subclass objects and you find that they have to violate the contract that the superclass has, what you're saying is it's not really that kind of thing. And so you should think about your design more and avoid that problem where else you can hang dependencies on yourself that are going to ripple through your code and make people say is kind of in order to know how to deal with you. But that's all we're going to say about ListGov because I don't think you guys mostly don't do subclasses. All right. So mostly today what we're going to talk about is open close, achieving open close through applying single responsibility and dependency injection. So we're going to write some code. Now I'm sure this is going to, this strikes fear in your heart because I know you can't see the code. It's unfortunately, I think we all had an idea, a vision of how big that screen was that was wrong. So I've flung all my code samples up on a website this morning. And if you follow that top URL, you'll get to the page that we're on now. Okay. So it's on, if you look at the tag for Gruco on Twitter, that link is on there so you can click on it. Now promise me you won't read ahead. Stay with me now. All right. I know what you're going to do now that you can see it. So I'm going to make a few ground rules about this code. I was tormented about whether or not to show you an involved code sample because we're going to look at code now for 20 minutes. But I really believe that it doesn't help you to have me talk to you. It doesn't matter. You can be telling you how it should look once you're done with it doesn't really help. We're going to take some code and refactor it using these principles and try to get to the point where it's open closed. Some ground rules. And there's, I'm just going to state what they are and not really defend them. But smarter people than me and people that know more about mocking and subbing than I do will have better defenses of these rules. But I'm going to do this. I'm only going to mock classes that I own. And I'm only going to stub in the object under test. All right. And I'm happy to have a discussion with anybody about that who thinks there's a different rule out there. But these are my rules. And so we're going to obey them in the test that we write today. This is the app that we're writing. There's a FTP server somewhere that has a CSV file on it. We're trying to get that file over, parse it, and throw it in a database on our side. The data involves patents. And so I'm writing a thing called patent job. You're going to see this data in the slides. There's a little file of test data. There's an FTP server that has some configuration information that'll let us get to it. And there's an active record target. You can tell I've been, I'd had a long bicycle ride the day I was working on this presentation. I was really tired. I wanted the NAP compressor badly. Okay. So let's write the spec for patent job. It should do these two things. It should download the file. And it should store it in the database. I'm going to just drive through the spec in the most naive way possible. It seems like job ought to have an API that includes download file. It seems like job ought to have a method that says run. I know that I have more than one assert here, but I would assert to you that they're all for the same feature and that I'm allowed to do that. And here's the class. The simplest possible implementation. It says run, which calls a few more methods. Okay, let me stop for a second. Can everybody either see the screen or see the code on their PCs? Yeah? Tell me if you're, okay. You're staying caught up. I see heads nodding. I like that. So here's the class. If I expand the download file method, you'll see that it calls net FTP and passes all those arguments. This slide you probably can't see, but it's, this is the entire class. So I just put it up here to give you some sense of how much code was involved. This is my entire application at this point. And it works. It passes those tests. The tests run green. Here's what the app looks like. Just what we said when we started with. And now I guess we're done. Well, I don't know. Maybe we're not done, right? I don't know how you feel, but I don't really like that code very much. It works. It does exactly what it's supposed to do. It's probably the simplest possible implementation. My tests run green, but I'm uneasy. This code won't tolerate change. And I have to download the file every time I run the test. So there's a bunch of things that could happen that would cause me to have to go in the source code and edit this code. Maybe I'll never write another job. Perhaps you should quit now. It's possible, right? Use your best judgment. Just because I can refactor this doesn't mean I should. However, it's also possible that if you refactor it, you'll find something that you didn't know is true. And it's like TDD in that way. Design is emergent when you follow the object-oriented principles, just like features are emergent when you follow TDD. So I don't like some code smells about this, and I'm going to change them just because I don't like the code smells. And here's the first lesson. Resistance is a resource. In your personal life, with your spouse and your children and your dog, right? It's information you didn't have. You can push back or you can listen to it and try to hear what it's really telling you and fix it. In this case, I want to listen to the code smell even though I don't really know what's wrong. I can't articulate what's wrong and I don't know where I'm going. But I know that something's not right and I believe in the rules. Also, if testing seems hard, there's something wrong with your design. That's the bottom line. If you reach the point where you don't know how to test what you're trying to test, your design is incorrect and you should examine your design in order to make your test simpler. In this case, well, so tests depend upon the design of your code. They reference the names of the classes of your code, the order of the arguments of your code, and they also reference the body of things that a class does. If you find your test has a lot of setup or is unduly complicated, the class that you're trying to test has too much stuff in it and is not well-factored. If you can't write the test, the code is wrong. So in my case, well, actually here's one more thing. And TDD will make your life a living hell if your design is bad. And if you're new to TDD and new to Ruby and you find that you're having a very difficult time writing test, it does not mean you should stop writing tests. It means you should learn more about object-oriented design. It will help you. So I want this to be open for extension and closed for modification, and that's just a goal. That's a far-off goal and I don't know how to reach it, but there is something very specific and concrete I can do right now. I can apply the single responsibility principle. Oh, sorry. Damn it, I get that wrong every time I practice this too. Okay, here, let's look at this. So we've been taught that you should write tests that are red, write code to make the test run green, and then refactor. And at the point of refactoring, the question that they always tell you to ask is, is it dry? Does it not repeat itself? I want to suggest to you that you ask yourself a few more questions at this point. I want to know whether the class has just one responsibility. I want to know whether everything in the class changes at the same rate, and I want to know whether the class depends on things that change less than it does. So ask these four questions every time you reach the green refactor point. The correct answer is yes to every one of them, and if you find that the answer is no, you should go change something in your code. In this case, when I look at the patent jobs spec, I find that I would read this as it should know how to download the file and it should know how to, whatever it says, it should know how to update the database. That AND is the code smell that says it has more than one responsibility. If I read that and said OR there, OR is even worse. OR says it has more than one responsibility and they're not even very related. All right? When I read this and it says AND, what I know is that I need to refactor this and pull a responsibility out of that class. Perhaps I should have been smart enough to recognize this to begin with, but the beauty of this is you don't have to be smart to start with. You just have to know the rules and you can apply them and dig yourself out of these holes. So I'm going to move the downloading code out of the job and put it in a class by itself. Now, now I can mock. And when am I allowed to mock? I'm not allowed to mock behavior that's in myself, so I couldn't mock this before I was stuck downloading the file, but now I have a virtual other object that I can define as a role and mock in my system. It's going to have the API of download file and I can use it in my patent job class. This is the perfect place for a mock. It will make your tests faster without making your system more fragile. If I had 10 jobs, they could all use the mock. When I write the actual download code, I can write a real test that runs against the real external resource and I can use something like Micronaut by the Relevance Guys to have a class of tests, the long-running slow tests that are sanity tests that really do check the external resource but they don't have to run every time I run my big batch of tests. So the first thing I'm going to do is mock out this in the patent job class. This was the second test that was originally in that spec. Now I'm going to create a mock and put in there. Now this mock, it's just a mock. I'm mocking a role, right? Had I been smart, I could have done this to begin with but I didn't know it then. I didn't recognize it then. So now I'm going to put a mock in there and it's going to be a stand-in object, be a stunt double that sits in at this place in the diagram and I'm going to use dependency injection to get that behavior back in the original object. So you notice when I get a new instance of patent job, I'm going to pass in the mock downloader just like in real life. I'm going to pass in the real object. The code used to look like this when we originally wrote it. Now at this point, if I wasn't injecting the dependency, you might be tempted to do that. I used to call the download file method that was my own method. Instead I can go change just that one line of code and say get me a new instance of this other class whose name I know that I now am making a dependency on and the download file method is over there. Instead, you should do this. I'm going to inject an instance of the new class with the other responsibility back into the original calling class. Right? I got an accessor. I injected it and I took a default at injection time so that this class doesn't have to create one and my tests will all run any place I want to use it. Any place that I want to use it can take the default but I can also inject the mock in from the test without going in and changing partial objects. Now, you could make an argument, quite a successful argument, that this code is shorter than this code. And if you're only going to have one job ever in your life, maybe you don't want to do this. But let me ask you, do you expect your application to succeed? Because if it succeeds, you're going to want to change this later and if you don't inject the dependencies now, you're going to be unable to add behavior to this without changing the code later. All right, so now we need to write this new class, right? We mocked it in the test a minute ago but now I need to create this object. I'm going to do the dumbest thing possible. I actually wrote this upload test file method here but other than that, this code is just completely ripped out of the other class. The spec is an exact copy of the old spec. The code in the class is an exact copy of the code in the old class. Resist the temptation to do more than you need to. Refactoring in this way is just like writing code with TDD. Don't guess where you're going to end up. Just follow the rules and see what happens. So now we have an application that has, where I've separated the downloading responsibility out of the patent job. And so I guess we're done. Oh, you know, the thing I meant to say, we're going to do this four times. And I always imagine in the next slide that that man is swimming. Surely he is. Those boats can't be stable. Okay, so now that we're back to green, let's ask these questions again and do this exact same refactoring. Is it dry? Yeah, it's dry. There's no duplicated code in either one of those classes. Does it have one responsibility? Well, probably. Does each class contain only things that change at the same rate? Class, again, it's hard to find words for these when you haven't done this very much. This class makes me uneasy. It's unlikely that net FTP will change, but it's pretty likely that the login or the path or the file name will change. This class seems to be a combination of completely static, very unlikely to change things, and pretty dynamic, pretty likely to change things. And I just hate that. I hate having the strings right in the method. I mean, you know it's going to come back and bite you. The chance of me having to edit this class to change that value is almost 100%. So the code smell is just that feeling of unease and the sense that everything in the class is not going to change at the same rate. And so now the first thing I'm going to do is try to pull out a responsibility. And we're doing this pattern of refactoring that happens over and over again. You identify the responsibility that you want to remove. You make a new spec in a new class that has that code and no other code in it, and you take that code and you inject it back into the original class. You just follow this pattern over and over again until you get to the point where you don't have any more responsibilities to take out. Do the simplest thing possible. Don't guess where you're going to end up. Okay, so in this case, I'm going to pull the configuration data out and put it in another object. The last time I did this, we mocked at that scene. And I know it can be really confusing to know when to use that whole mock everything that was a big idea for a while, right? And I don't know if anybody followed that path, like I followed that path for a little bit, and you can really ruin a bunch of code by trying to mock everything. You can make your code really fragile by doing that. In this case, I won't mock config in Downloader. There's no reason to do it. Config is not an external resource. It runs really fast. It's a value kind of object. It's really just a container for static data that it's not going to change during the life of my code. There's no reason for me not to use the actual production object here. And so instead of mocking at this scene, I'm at a point now where all my tests run green, I'm going to just go write the config object. And when I sit down to write this back, I realize I didn't have a test for this. It didn't make any sense to put this in my other test. It seemed far too detailed for the granularity of the test I was on, but it's actually something I probably should be testing, and I didn't know it until I drove through this cycle a couple of times. Now, and it also turns out, this is a conundrum often in tests. When I'm in the test environment, I have a test path, but mostly the reason I write tests is to make sure that my production environment will be okay, and it's different. So sometimes while you're in test, you need to pretend that you're in production so that you can make sure that it will really be okay in production and not just in test. So in this case, I injected the environment back into this class. Now, probably this is wrong. Probably it's wrong. If this ever changes in your real production code, you should inject it. If this is never actually going to change in your production code base, you should stub it in your test. Now, I kind of need that argument, so we're going to pretend for today that it is reasonable to expect that I might someday change the production environment while I was actually using this code. But what about that? This is an if statement, and if another environment appears, you can bet your bottom dollar I'm going to have to change this code. It is a code smell in any object-oriented class if you have an if statement. I realize, and sometimes if makes sense. But I contend to you that I'm an object. I ought to know what kind of thing I'm in. I ought to know what kind of thing I am, and I ought to do the right thing because of what I am. And when I have to start implementing this, no matter how well it's hidden, this is a code smell that says the design of the object is not yet correct. But that's okay because right now it passes that test. The bigger problem I have with this code is that now I'm starting to feel like if I had to make another one, if I needed some other configuration information that was environment-based, what would I do? Well, I'd just copy this class and change everything in it and make another one just like it. There is no abstraction here, and it's starting to feel to me like the idea of a configuration is a useful abstraction. Here's the next big point. You don't have to know where you're going to successfully refactor. Lots of times if you haven't done this very much, if you're new to Ruby or you're new to object-oriented programming, you can look at really nicely factored code and your immediate reaction is to give up. You think, I could never do that. Look how beautiful that is. I don't know how they thought of it. And the cold hard truth is they didn't think of it. They just wrote some crappy code to begin with and refactored it until it looked really good. You have the exact same ability to write really great code. Don't expect that it's going to spring full-blown from your head like Zeus, right? I mean, it happens because you know the rules and you have faith in them and you apply them over and over again. And then you end up with a good code. So I'm going to go back and scratch the itch about the strings that are the names of code. It feels more like it's something like this. It looks like my database you handled to me. Like that's the thing where I can put data that's configured based on environment. I sort of want to rip all this code out and put it someplace where I can change it and have it be in a text file. And if I were to do that, then I end up with this. Now, my tests are still running green, right? My tests have been running green all the time. So I can write this code and this code sucks too, right? It reads the ammo file. I mean, I got the data out of it. And now we're back to something. I've refactored far enough so that I've arrived at a place that I recognize is not dry. Like now I'm failing on that first question. This code's not dry. And what's not dry about this code? This is where, okay, don't be intimidated if you don't know enough for Ruby to do this. This is the most reachable piece of metaprogramming you can do in Ruby. The way my YAML looks is a hash of hashes, right? And you see all this code, the method names and the names of the keys are the same thing. Let's do this. There's the whole class. Don't worry about it for a minute. I need to read the file. I'm going to push two arguments into the initialize method. Now, this is the same thing that took the Rails environment as an argument a minute ago, and it was an argument that had a place, right? Argument 1 was the envy. Now you see I'm taking a hash. And you can see the first two lines of code in initialize are to take the Rails environment and to take the name of the YAML file that I'm reading. The order of arguments in your method calls is a dependency. As soon as you hit resistance, when I got here I had to change my test because I wanted to change the order of the defaults. As soon as you hit resistance about your argument orders and you're trying to say, well, I need the default when the first one works. You don't know what order to put them in because you don't know what the defaults are. Believe me, it's a hash. Give up and make it a hash at that point. Once you make it a hash, you remove the dependency both from your tests and from all the code that calls it. And if you don't like writing those two lines of code, I'll tell you, you'll learn to love them. You'll learn to love them because they will save you so much trouble later. Do not resist at this point, right? If you look at well-written code in any framework, you'll find out that they have given up and they take hashes for all the arguments. They give you order independence and you don't want to be bound to that dependency. And then finally in this method, there's a little bit of metaprogramming. Now, if you don't understand what that means, this is the code on the left writes the code on the right. And this is no longer a patented config class, it's a config class. This is a class that will take any environment-based YAML file that you can write, and it will dynamically generate an instance of a class that will have methods in it that return the values at those keys in your YAML file. Any kind of configuration in your system that you want, now you can write a YAML file and the Ruby code never needs to be tested again. You can absolutely rely on it being correct. That's so cool. I had no idea we were going to end up here. I just followed the rules, right? Everything that smelled bad, I fixed. And I fixed it over and over again. And then, you know, we arrive here. So that slide, I'm sure, meant something. Oh, so now I have to go back and inject config into PatentDownloader. It's easy. It used to look like this. And now I just inject it. And then I use it. And now here's my app. But wait, there's one more thing in this refactoring. Now I realize that PatentDownloader has nothing to do with patents. And all of the code in it just uses the configuration data out of its config. So I'm just going to change its name. I'm not going to assume anything else, but I know it's generic enough so that it ought to have a name. The name change is a dependency in my tests. As soon as I recognize I'm going to have to make that name change, I'm going to change everything in the system that depends on it while there's just a few of them. And now this is beautiful, right? There we go. And it really does feel like I should be done. But let's go back and check one more time with the rules. Is it dry? Right? Does it have one responsibility? Does everything in it change at the same rate? Or does it depend on things that change less often than it does? And so let's just look at the initialized methods of the classes that we wrote. Job depends on Downloader. Downloader depends on config. And Downloader also has this reference to the YAML file. Is that right? So here's how you decide. Put your object on a scale. Things that change from things that change a little bit to things that change often, right? The likelihood that they are to change, left to right. And you should depend on things on the left. That's all there is to it. It might not work in here, but it works in your code. Here's why. If you hang a dependency on it, there's always an object to scale about how likely it is to need to change. And low numbers are good, right? Low numbers mean your objects are loosely coupled, highly cohesive, easily composable, context-independent. You want everybody to have a low number. Unfortunately, something has to know about something else in order for your app to work, right? So there's always objects. Some objects are going to have to change more often than other objects. You want to change, and you hang a dependency on it of an object that is more likely to change. You raise its score. You can screw up a perfectly good object, an object that will never, ever have to change. You can screw it up by hanging a dependency on it for somebody that changes more often than it does. You want the dependencies, you want highly changeable objects to depend on objects that don't change much, not vice versa. And it's easy to do, right? You don't have to understand this what you have to do is inject your dependencies. Because if you don't inject your dependencies, you're screwed at this point, but if you have injected your dependencies, you can just go back and fix them. So what's wrong here? Now that you've looked at that other graph, what's wrong here is that that's wrong. That downloader file, FTP downloader, shouldn't know about that patent. As a matter of fact, now that I have this idea in my brain, I realize it shouldn't take a default at all. It's a very generic class. It shouldn't know this stuff. Pat and Job should know this stuff. And all it had to do was just put them on the chart and you know who should point each other and you can go move them around. So how am I going to fix Pat and Job? It takes two arguments in its hash. It provides reasonable defaults to those arguments. And I've created a dependency here about the order of these things and that's okay with me. It'll break right away in development if you get that wrong. It does not have bad consequences. So now my app looks like this. I have a reusable config class that I can use anywhere. I have an FTP downloader class that can download any file for a config that I inject into it. And I have a Pat and Job that's the most changeable thing in the system. So now, surely, I'm nearly done. Oh, I have three and a half more minutes and we can do this. What's left? What's left is things change. Things change, right? Now I've got 10 jobs. I've got a couple of different kind of downloads, right? Some are SFTP, some are FTP. I've got one that's a mechanized script that scrapes a website. I've got one that gets a database connection. It turns out I only have 10 jobs because I made this abstraction and when they came to me with the next thing, I said, oh, that's just like that other thing I did. I have an empire now in revealing hidden data. I've got a lot of times to have that. So now what they tell me is that some of the jobs that are using FTP are going to have to switch and use SFTP and we can't really tell you when. So I have this new requirement that says we don't know what the requirements are. Now you're probably all familiar with that requirement, right? And when you hear that requirement, you don't say, well, I can't write your system because I don't know what the requirements are. It's a feature of your app that is adaptable in the face of unknown requirements. And here, I don't know the name of that class. That's what they just told me. But I'll just put it in my config file and we'll use it dynamically to create the class at runtime that I should use for a downloader. Now, so the architecture of my app didn't just change, but the flexibility did. So here's all the code. This is the original job class. This is the new job class. It's about the same size as the original. But the initialized method is there and the downloader method isn't. Here's the downloader class. You can use it for anything, right? You can download any stuff you want. Here's the config class. You can make any kind of ammo file in your system and create an object that can answer all the values in it. So now we're done. So what does this all mean? What it means is that test-driven development is good, but it's not enough. It's not enough. BDD is great, but it's not enough. Dry? It's an excellent thing, but it's not enough. When you first came to object-oriented programming from whatever language you came from, those things seemed incredibly intimidating, but they're training wheels. Now you've reached the point. Now that you know those things and you're trying to apply them and use them in your daily life, you're more with design. If you follow the design principles, you'll find that your code remains flexible, easy to change, and fun to work on, 10 years from now. It's as fun later as it was to begin with, and then you get to keep your jobs and they keep paying you. All right, here he goes. Look at her credits. You should read both of these papers. They're excellent. They're really excellent. All right, thanks for hanging in there with the code. Appreciate your attention. Two minutes. Questions? I beat y'all to death, didn't I? You really did pay good attention, I have to say. I was quite impressed, yeah. Does metaprogramming somehow contradict with liscoff substitution if you're generating new methods and things like that? Oh, I'm sorry. He asked about metaprogramming and if it was somehow a violation of the liscoff rule. I said no. And here's what I would tell you. If you're designing code that other people are using and you're following these principles, what you're going to find is your code gets pushed into greater levels of abstraction. And it becomes black boxes that people ought to have a well-defined API and people don't need to understand the code to use an API. As a matter of fact, it's better if you do that. There's tension between having an easily, really incredibly straightforward code, which is just a long stream of procedural code and well-factor of abstractions. Well-factor of abstractions are what's easy to use. And so it's perfectly fine with me if you want to do the most, well, pretty extreme metaprogramming, as long as you hide it well. You have a clearly defined API and if you subclass it, the class is a Bayless call. Don't make me say it's kind of to use your code. Bad, bad, bad. Everybody will hate you. You're going to end up like a well-known website having to refactor all their Ruby code. You know, it's not that Ruby's bad. It's that if you write code and you end up doing those things, it means it's not well-designed. And if you don't know how to fix it, it's perfectly okay to not know how to fix it, but there are people in the community that can help. And you should go find help, ask them the use groups about how to fix those problems and get your code to where you can, it's easily composable and changeable later. Okay? Anything else? Yes? Cross-off interfaces. You actually need interfaces in order to do dependency injection. Same thing? He said, I crossed off interfaces. He said, I crossed off interfaces and asked if I needed interfaces to do dependency injection. You do not need interfaces to do dependency injection. That interface segregation rule says that if everybody knows something about Java, like, so if you have a Java interface, you can either put, like, 200 method signatures in the interface and then make everybody dependent on it, or you can make a bunch of different interfaces each that have sort of, you can group it maybe in sets of 10 or sets of 50. That's just about breaking up big interfaces into smaller related interfaces so that you don't have to recompile your whole system when you do it. Dependency injection is a different thing. Dependency injection says, and here's my 30-second spiel about dependency injection, if you're going to name a class and get a new instance of it, but in your initialize method, and if you've done it in your initialize method, you may as well inject it as an argument and provide that new class name dot new, say, inject it as an argument and say equal class name dot new. If you do that, you can change it later. If you don't do that, you're stuck. And if you go down in the middle of your class somewhere and get a new instance of something, here's the deal. You think you know what use is going to be made of your code, but you don't know. These are all easily composable objects. Somebody will do you the favor of reusing your code later, and if you're getting new instances of helper classes inside it, they can't change them, right? Inject the dependencies and give the guy that follows you a break so that they can reuse the part of your code that you want to use while injecting some other behavior into it. We're done. Thank you.