 I am Anil Virgule, I work in company name Equal Experts, which is in Pune, India, and I am going to talk about solid design principles in 2D. So this talk is about design principles which I realized helped me to improve my code and ultimately write good object oriented code which is extensible and reusable. So all of us are software developers, so initially when we write our code, our application, it is all good, version 1.0 is very good, there are no issues. But after some time, your application becomes like this because of changes, you keep changing your code, you keep modifying your code because of client request and that makes your code bad. So your application will change and it will become messy and developers will head to work on it. It will become very difficult to actually go and add features, mostly I have seen this happening in most of the projects. So why that happened? It happened because of bad design. So if your application does not have good design, if it does not have good object oriented code, it will be very difficult to add new features to it. So after time, it become bad. So writing model code does not mean writing good design. So most of the Ruby programmers actually follow Ruby items, some of the modality concepts. So just because you follow dry or TTD, doesn't mean you are following good design. So good design is about managing dependencies. Ruby is object oriented programming language. So everything is object in Ruby. So whatever you write are nothing but objects and how those objects are interacting with each other, that makes a good design. And that will make your code extensible and ready for change. So in this diagram, A is dependent on XYZ, so XYZ is dependent on M, S is subclass of T. So all these are dependencies and if you don't manage these dependencies well in your application, it is going to hurt in future. So unmanaged dependencies are killing your application and a good design might save you. So what are the smells of bad design? First is rigid. Rigid means your application is difficult to change. One change is causing too many other changes in your system. If that's the case, it's rigid. Second is fragile. Fragile means your application is easily breakable. So if you change one part, like if you change controller and suddenly worker is breaking, so something is messed up. So that's fragile design. Immobile. So whatever you write should be reusable. If you're writing code which is not reusable, then it's not of any use. So if your code is hopelessly entangled with each other, that makes your application immobile. And viscous. Viscous means you probably have followed good design, but it's very easy to add hacks than actually follow good design. If that's the case, then that means your application is viscous. So initially it was good. So like whenever we write application for the first time, it was good. But after many changes, after like new changes, new requirements, it becomes bad. And that's a pattern. Actually that happens many times. So changes killed it. So software development is not a game of Jenga, right? We don't keep adding features. So if you keep adding features without actually thinking about design, it will work for like some time. But after some time, it will become very difficult to manage that application. And in enterprise world, this is a pretty common pattern. So why solid? So solid helps us to write loosely coupled, highly cohesio, easily composable, context-independent, reusable, easily testable code. So ultimately it makes your code easy to maintain and extend over time. So Robert R. Martin, who is famous as Uncle Bob, has written this paper in early 2000s, written principles and design patterns, which mentions about this principle. And also Michael Feather coined this term solid. So S means single responsibility, O means open-close principle, L means risk substitution principle, I means interface segregation principle, D means dependency inversion principle. Let's look at these principles in detail. First is single responsibility. So this principle says a class should have a single purpose. Class should serve a single purpose. There should not be more than one reason for a class to change. If there are multiple behaviors that you have wrapped in a class, that means it's not single responsible. The ways of achieving this principle are revealing intent. Find out what is the intent of your code, rename the variables, rename the method names, rename the, introduce constants, and extract methods into other methods. Extract behavior to other classes, extract methods to other classes. If you do that, it will make you, like, it will introduce new classes and that will have single responsibility. So following single responsibility code generates highly cohesive code and removes immobility smell. So let's say our client asks us to implement feed server application. So let's see how we can apply a single responsible principle there. So a developer could go and write, like, these methods. So feed server application basically fetches feed from a feed URL, parses that feed and saves the feed to database. So one class can actually have these three methods. A normal developer could go and write code like this. This will work. This class is not following single responsible principle. So to achieve single responsible principle, we could refactor like this. So we could extract methods into different classes which are single responsible. So feed fetch feed, RSS parcel will parse the RSS feed and feed server will save the feed. So if you do that, we can reuse these classes from other parts of your code and your code will become cohesive and also immobility smell will go away. So this is how we achieve single responsible principle. So next principle is open-close principle. As you see in this image, to wear a coat, you don't have to do heart surgery. So the principle says the same, software entities, classes, module methods should be open for extension but closed for modification. Is it practically possible to write such code which is open for extension but practically closed for modification? Yes, it can be achieved using open-close principle. Let's see how. It can be achieved using inheritance and composition. If your code has lots of e-fills cases or long switch cases, that means there is a chance to introduce this principle. Let's see an example for this. So now clients want to introduce atom parsing. So previously it used to do RSS parsing. Now it is also parsing RSS. So it is like a developer could go and add e-fills case and just delegate the atom parsing to atom parser. But this save field has now e-fills case and that is changing behavior. So tomorrow if a client wants another type of field to be parsed, he has to go and modify this code. So this method is not open for extension and also not closed for modification. So to achieve open-close principle, what we could do is that. So we could abstract this behavior of e-fills case. We could introduce a parser abstraction. In that parser abstraction, we could just in Ruby, we don't have to add that parser class because Ruby is duck type. But now we have RSS parser and atom parser. And we can just pass this parser to our save field class. So now our method is open for extension and close for modification. We can just add another type of parser if required and pass it to save field. And that way we can achieve open-close principle. Basically, if there is branching, you have to replace that with delegation. You have to inject changing behavior. Don't have changing behavior in your class. Just inject that from outside and wrap behavior with abstraction. So if there are different types of APIs for the dependencies, you could actually wrap that dependencies into one abstraction. And then pass that dependency to the user of that dependency. So that's how we achieve open-close principle. So we saw the code example. And this is how we achieve abstraction with parser and followed open-close principle. Okay, let's see next principle, which is Liska substitution principle. As this image shows, if it looks like duck, acts like duck but needs batteries, it might be a wrong abstraction. So this is a very different principle, actually. This was written by Liska Barbara. And there is a rule for this principle. Firstly, subtype must be substituted for their base types. And this rule is like this. Let qfx be a property probable about objects x of type t. Then q of y should be true for objects y of type s, where s is subtype of t. Basically, subclasses should not modify behavior of their parent class. This line also tells more about this principle. If a piece of client code works for a type, then it must work for all derived or variant types. A new subtype should not screw up client code. Rules implement inheritance based on behavior. Do not violate the behavior of your parent class. Do not do less than your parent class. It's okay to extend behavior, but it's not okay to actually stop the behavior of your parent class. And also, Liska Barbara mentions about pre and post conditions rule. We'll see what are those. Instead of our RSS parser example, we will take this classical example for Liska's substitution principle. Suppose our application using instances of this rectangle class. So rectangle has width and height. And rectangle also has method called area. So this is working fine. It's like there are thousands of inches of rectangle linear application. And now certainly our client says, I want to introduce square. So a developer could think square is perfect rectangle. And he could introduce square like this. Where he could just override, like he could just pass side. And instead of width and height, because square doesn't have width and height. And square is perfect rectangle. So he just changes initialized method to pass side. And this could work fine. But there is a high chance that some other developer could override this height method. And if you see right, square will return area as 100. Normally, but if somebody overrides this height, it will return 300. So that's breaking Liska's substitution principle. So you should follow Liska's substitution principle for better code. Let's see what are pre and post conditions. Here we will take example of base calculator. Base calculator actually calculates past input. The bear of base class is that the input should be greater than zero and less than 20. So a valid subclass can actually... So the rule is preconditions cannot be strengthened in a subclass. So a valid subclass can actually weaken the preconditions but it cannot strengthen precondition. So let's see another subclass. So here you can see this is weakening precondition that's allowed. If it starts strengthening preconditions, it's breaking Liska's substitution principle. So whenever you write subclasses, you should take care that you are actually not strengthening the preconditions. So that's how we achieve precondition rule. It's not by not breaking the... Sorry. So by not breaking the bear of base class. So this rule is about post conditions. It says that post conditions cannot be weakened in subclass. So we have base calculator. The contact is it will always return positive numbers. So currently a base class is returning positive numbers but if you write a subclass which will starts returning any kind of number which is passed. Suppose if you pass a negative number, if you start returning that, you are breaking contract and you are weakening your post conditions. So if that's the case, it means that you are breaking Liska's substitution principle. So I gave these classic examples because in normal way, this principle is hard to explain in regular applications but if you follow these rules, it is easy to follow Liska's substitution principle. Okay, so next principle is interface segregation principle. Like there is USB port, there are USB port and there is USB. So this principle says that many client specific interfaces are better than one general purpose interface. So it's better to have a client specific interface, separate client specific class. So in Ruby, we don't have interface. We have modules and classes. So we can say that many client specific modules are better than one general purpose model or class. Client should not be forced to depend on method that they do not use. So following this principle helps us to write a highly cohesive code and removes the mobility smell. Let's see two examples which will convey this principle. So we have a common model TV which has on off select channel up, down, record and play methods. And we have HDTV and normal TV. So HDTV includes this TV model which is fine and normal TV includes this TV model. But normal TV doesn't need record and play methods. So what we could do is that we could override these methods and say, okay, those are disabled. So like this works but it's breaking interface segregation principle. So you should segregate your methods into two different modules actually. Instead of common TV model, you could add a module named DVR and a module named TV. So DVR module will have record and play methods and TV module will have other methods other than record and play which are common. And HDTV module will include this, HDTV class will include these two modules and normal TV class will include just TV model. So that's how we achieve interface segregation with modules. Let's see another example where it's with classes. So let's say we have a class called car. Car has these three methods, open door, start car, change engine. Driver is making use of only two methods open door and start car. And mechanic will use a change engine. So the same object of the class is shared between two different clients. So instead of right, so this is breaking interface segregation principle. So you should have two different classes like this. Now a driver is making use of car object which is fine because car object now has only two methods which a driver will use. And mechanic doesn't need this car object. It can have separate class which is car internals and that has this change engine method. So this rule is like seems okay why we should do this but this actually makes your code highly cohesive and immobile and extensible for the future. Okay, next principle which is last principle which is dependency inversion. So as the image shows, instead of connecting wire directly we are using switch and that plug. The principle says depend on abstraction do not depend on conclusions. There is another definition of this principle which is more therapeutic. High level models should not depend on low level models. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions. Like this seems very difficult to understand but let's see how this is achieved with a classic example. So let's say there is a program which is copier which is reading from keyboard and writing to printer. So copier program could be written like this. Let's see how. Which has now hard-coded dependencies keyboard and printer. So this works but to follow dependency inversion principle, so the name of the principle is dependency inversion. So how to invert dependencies? Currently dependency direction is on downwards direction. So to revert dependencies, so tomorrow if you want to use disk instead of printer, it's impossible with this because you have to change the code. We can achieve that with this inverted dependencies. We could introduce abstraction. Copier should depend on these two abstractions which is first is reader abstraction, second is writer abstraction. And as the principle says, high level model should not depend on low level models. Both should depend on abstractions. So you can see both are now depending on these two abstractions, reader abstraction, writer abstraction. And abstraction should not depend on details. Details should depend on abstractions. So that is also achieved. So this is how we invert dependencies. And a common, like we can implement this using dependency injection, a common pattern to do that. But this can be very like abstract. Like you can have common contract which is common abstraction in like race services. We write contract right. So we are not dependent on actual implementation. We mostly have contracts there. So this is pretty abstract like principle, but if you follow this principle, you again make your code very clean and easy to explain. So let's see that same example where now this code shows it has used these abstractions and tomorrow we can change printer to disk. So common smells of this principle are a new keyword. If you see new keyword in your code, it means that there is a chance you can do like dependency inversion. And directly static or class method calls. If you have that, that also is breaking this principle. So what does applying solid principles lead? It converts your rigid, fragile and scary code to flexible, robust and curly code. And I've recently came across this tweet which says you cannot teach software design, but this is wrong. Actually, by learning these principles, I have one example to share. So in my company, five recent graduates had joined and they were writing, they were solving some problems using object-oriented code, but their code was not good enough. Since they learned about these solid principles, the code they wrote, they tried to follow these principles. It is not always possible to apply all five principles, but if you follow at least four principles, your code is good enough to go to production. And ultimately it becomes extensible and reusable. So TDD is not enough. Even TDD, people say it helps to evolve design, but it does not. And dry is also not enough. If you just follow dry, it's not that you are writing, it might be like just modular code, but it's not about design. So design because you expect your application to succeed because whatever we write today, if it succeeds, we have to change the code. And if your code is not ready for change, then you have problems. Also if it is having bad design, it is also bad for the feature of the application. So do design and design patterns. So design patterns are reusable solutions to commonly occurring problems. So if you follow design patterns blindly, it's a problem, but if you follow design principles blindly, I think it will definitely help. So I used to, I read about design patterns a long time back and I used to apply design patterns, but it was complicating my code. But following design principles never complicated my code. It actually helped to write code with better design. So finally, as in the last yesterday's talk by Constantine, abstraction is the key. You have to abstract things in all the principles. Somewhere there, abstraction is there. And if you abstract the things out, it will automatically follow these principles. So do follow solid principles. Thank you. I do recommend reading this book by Sandy Metz. My talk is inspired from her talk. So, and also Thoughtbot has this weekly iteration. Like Ben Orenstein has discussed about these principles in detail. And Uncle Bob has this site called CleanCoders where he also discusses about these principles. So as a Ruby programmer, we always believe in duct typing on all the things. But I think if you are working as an application developer for big application, we follow these principles. It will help your future. It will make you happy developer. Yeah, thanks. So we're running a little bit short on time, but maybe you can take one question. Thanks for your talk, Anil. All these principles sound great. My question is, which one of these principles do I need to apply to prove that rainbows are real? All the principles. All of them, okay. So thanks, Anil, again. Okay, thanks.