 So I will be talking about the solid design principles and how we can use them to write better code in PHP and essentially become better developers So a little bit of a background. My name is Katerina. I am a software engineer and co-founder of a diva I device at marketplace for remote working and we are focused on helping companies scale their teams with remote developers So working in an environment like this I've had the chance to work with a variety of companies and projects And be part from both the startup environment and also the enterprise and they're both very different But they are similar in the way of you know working with spaghetti code and trying to to find your way through messy lines in the code base apart from that I advocate for remote work because I've been doing it since the beginning so It's really great and I recommend it to everyone and I'm volunteering for the local tech communities also joining initiatives for inclusion equality. So you can You can talk to me about it later if you're interested in that, too Now I'd like to start with a Short PHP humor. I'm not sure about you, but I hate this so this is part of a larger comic it was published by by toggle a while back and It what it does it explains how you can rescue the princess by using a different programming language So every programming language has its rates. It's you know Downsides and all of those were incorporated in this comic, but when it got to PHP It was like, okay, this night is giving up. He is unable to save the princess and he would rather not try so instead he he gives up and he dies and and This is something that you know It was a irritating ending for me because I've been working with PHP for six years I've been working on software that's really high-scale with Teams from all around the world and you can really scale PHP and Demin also demonstrated how you can do it yesterday So it's it's not that it's impossible to save a princess So when you think about this, it's more of the approach we have towards programming and not so much the language itself so while I was preparing for this presentation and ran on to this research made by hacker rank and they surveyed developers on various topics one of those was what they hate most when Programming or what they hate most in their daily work and what they answered was spaghetti code So I'm sure we can all relate to this, right? It's it's something that we dread and whenever we join a legacy system or whenever we join a Team where you know, we're new and we need to get up and running with everything It's getting hard because we don't understand what the code does and we spend much more time reading it and trying to understand it Then we do actually developing your features or fixing bugs So that's what I will try to cover with this talk I wanted to go through a specific example of something I currently work on and There will be some code even though it's early in the morning. So I hope you had your coffees This is a screenshot of the platform for for a diva so Basically, it's the login page after the developer signs up. They're Accepted to our community. So this is where they can access all of the opportunities that they have and Update their profiles. So basically what it does It's a couple of links on the side and then a form where they can edit their profile details at their experience and at their Education so when we first started working on this We were still pretty small and we could manage everything on on slack So all of the developer communication all of the opportunities were managed directly But after a while since we started growing we decided we needed to place where everyone could log in and manage everything on their own So but we didn't want to spend a lot of time and invest a lot of resources at the beginning in building a large platform We wanted to do it step by step and that's how we how we decided to start So we were using a third-party system for managing all of our applicants It's called Zohar recruit and it's kind of like a CRM for candidates so you can manage all of their details attach CVs and Edits their experience their education history and all of that And it was fine our administrators were logging there and manage everything and here We wanted to just create a simple form and connect it through the third-party API so that we can save all of the details there So how this looks in the back? We started this was the only page that we needed so we decided okay We'll just create one class and put everything inside that This is a candidate class that handles It's like an adapter on our end that handles all of the API calls. So basically it has The methods for updating candidate details their education their experience and also several more that are not listed here But we added them afterwards like adding notes for the candidate and sending emails scheduling interviews So stuff that were more important on the admin side Then this is the part where we actually display this form It's pretty simple just makes a connection to that API and then calls of all of the the needed functions to return the the data we need and this is how it's displayed Interview so why this is important is to notice that we are actually manipulating arrays So everything we get is used as an array afterwards now this is a bit more complex and it's The actual update function. So it's how things work once we click the the update button and over here, we have a bit more messy code. So It has this part which handles the candidate details update basically gets all of the basic details like name email and sends them to to the system to the third-party system After that we have some logic for going through all of the experiences So we probably noticed that there was a button to add multiple experiences and it's also an option to delete ones that You're not interested in having in your in your profile anymore So what this does is it goes through all of the Experiences that read it checks if they exist in the system already And then if they do it updates them otherwise it adds a new record And at the end is there's a lead part where it deletes everything that's not used anymore And we have a similar logic for the education as well It's just we change the the calls and we change the variable names, but it's basically the same so after Completing this we realize it was a good approach. It worked for us We had this system on the back end in the background that we were using for Storing everything related to the developers and we had a nice interface for them to log in and edit all of the details and Once we started Moving forward we decided okay. We'll add one feature at a time for the admin side as well So we started with manipulating managing candidates adding new candidates deleting existing ones Qualifying and qualifying them and everything that's needed that needed to be done on the admin side finishing with Generating the profiles sending client proposals so everything that was related to the developer side To the developers data and we needed for the admin side was slowly getting developed and over time it became became Massive not that massive but for this use case it was it was really a large code base and Very messy code because we kept on adding and adding you Not well structure code So after completing all of this this third-party system wasn't flexible enough for us anymore So we decided that we needed to switch to a local database We wanted to have the flexibility of working with everything On our end and then adding your features as we needed So let's see what it took for us to move to a local database with this Code structure now. I'll be looking again at this larger updates method The first thing is to switch from using the Zoho API for Updating the candidate details to using our an eloquent model. So this is a larval code code But it's pretty similar no matter what you use So basically we changed the approach and instead of using this class for calling all of the API methods We're now using eloquent models and just using the methods they have for updating things in the database The second part was to update the experiences and over here again, we use the same approach but instead of Using separate model for the experience. We decided to rely on the relation between the candidate and the experience so that that part of the eloquent models and The education is pretty much the same so it replaces with a similar code like this one Now if you look at this a little bit closer We can see all of the changes that we had to make. So this is pretty simple piece of code but it has Several changes that needed to be made the first one is Here where we use a different approach to check if the experience exists So that's the one change that we need to make here We need to use a different approach to update it and then we need to use Again the different approach to create one and delete the existing experiences So it's three different changes and changes of logic not only changes of certain references in a simple code snippet, which is Several lines long and if we scale this to the whole system Even if we scale this to the to the method we have three more changes like this for the education part We have two more changes for the candidate details that we saw on the previous slide And it makes eight changes of code in a single method So scaling this to the whole system scaling this to the admin side would really take a lot of changes to go to get from The third-party system to using a local database and use the flexibility that this local database would offer So this is where the solid design principle principles come so I'd like to go through this example again by using the solid design principles and seeing how they can Help us simplify this code and make it much easier to change at a later point But first just as a little Overview again of what the solid design principles are So the first one is the single responsibility principle which basically states that every class should have only one Responsibility you shouldn't mix everything in one class like we did with our candidate API You should separate all of the specific responsibilities and specific smaller classes That are responsible for for each thing So you might think about is like instead of having a one large class with one generic name You would have several smaller classes with more specific names and have those names really define what that class is responsible about The second one is the open-closed principle This one can be confusing, but it's really very important and can can help a lot in in extending systems So what it stands for is having a system that's open for extension, but closed for Modification and this means that you should be designing your code in a way that allows you to add new features and Extend or change existing functionality by adding new code to it instead of modifying what you've already wrote Because if we keep on modifying what we have we would never get to a point where our system is tested enough So that it can become stable we constantly change something and we constantly need to retest it, right? So it's it's never at a point where it's resistant to bugs a good example about using the open-closed principle is with Open source libraries. So whenever you use you install a package with with composer you Hopefully you don't go in the vendor folder and edit everything there and then use it like that You just add another piece of code on top of it that Modifies what you used what you get from from that package and it adapts it to to your Specific Nate and specific use case and that's why the open source libraries are So stable because they get tested all the time and that they rarely get modified So this this is in particularly important for the core of your system Not not for every part of it, of course, but the core of your system has to be Resistant to bugs and you should change it less so that you can get to a point where it's more stable and the third one is the list of substitution principle and This states that you should always be careful about what a class does before you replace it with another one And this is not only about Interface that it implements or what methods it has it's also about what those methods do and what they return or whether they throw an Exception or not when you have a class that returns something and then replace it with another class that returns something else even though It uses the same method then your codes will eventually break The fourth one is the interface segregation principle and this goes closely with the single responsibility one It's essentially for separating your code in smaller pieces smaller interfaces that you will depend on instead of depending on a large class and Why this is helpful about is You would use smaller classes or smaller Interfaces to handle something in your code if you depend on a large class if we always depend on this candidate class We would depend on the ability to create notes or send emails when we actually just need to Unqualify a candidate. So your code is very coupled together and one change in one area can cause a lot of problems in another area and that's why you It's very important to set to separate those interfaces and depend on only things that you Need and things that you want to use and finally the last one is the dependency inversion principle this one states that you should always depend on abstractions instead of depending on concrete implementations and One good example over here is comes from the real world So when you want to charge your computer at your office or your home, you usually Plug it and use a socket to do that You don't go behind the wall and wire things up so that you can get electricity You don't hear how things are done behind that socket That's that's done for you if it happens in the background And you just want to use it and you use this socket as an interface to get electricity And this is how your code should behave it should it should use what you have in the background and Know that it's somewhere, but it doesn't have to know what happens in the background It doesn't have to wire things up in the background It just needs to rely on an interface that essentially enables them enables it to to complete a functionality So if we go back to our code now Let's see how many places we violate this principle This principle is the five of them First we obviously have too many responsibilities here. The first one is the Candidate update part. So this thing again updates all of the candidate details Sends them to to the third party API and it's a separate responsibility that should be moved away from this controller The second one is Updating the experience. So this is This part is again responsible for the experiences of the developers. So it should be probably somewhere else where it would be specific specifically Handling the experiences the third part is Manipulating the education similar to the experience part It has a separate logic and it's probably better off in another place and Finally the fourth responsibility Which is what our controller should actually be responsible for is returning what we have to the client So controllers are responsible for handling the flow of the application They they should get the details from from the client. They should pass everything to separate Entities who handle things for it and then they should return The proper response to the client. They shouldn't care about how everything is handled in the background So all of these responsibilities apart from the part where we get the request and that's that's past as a As a parameter to this function so the request is handled separately and it's validated separately So it doesn't mess up this code additionally But still we have three responsibilities that needs to be moved away from here And then we can only handle the the flow of the application So it's an obvious violation of single responsibility principle. Now, what about Extending this code with additional functionality. So right now we have education. We have experience but at one point we needed to add projects as well and For the developers the project snippet would be similar It would need to go through all of the projects update them or delete and or it creates new ones So it's essentially a similar code snippet that said it's right before we return the details to our view And this is modifying this function It makes it much messier It makes it much larger and it also has to modify the class a candidate class that we were relying on because we had everything Handled back there This valid open-close principle because if we keep on Changing this this method whenever we need to add something new to to this profile We would really get to a point where we would never get to a point where everything is stable We'll constantly need to retest everything and fix bugs when where we don't expect them and this part over here so we depend on The the candidate class Directly so we depend on a full class that has all of the methods which violates the interface aggregation principle because again We shouldn't rely we shouldn't depend on something that we don't want to use But it also violates the dependency inversion principle because we are depending on something concrete We're trying our controller is trying to wire things up So it can update the candidate details It doesn't rely on on the implementation that we have in the background And this makes it much more difficult to to change in the future and to replace it with The the local database and how we would handle it now this edit part It was pretty simple and short, but it still has some slight issues with it so again, we depend on this Concrete class for the candidate that has everything coupled together But if we need to change it, it's it's just a short change instead of depending on the third-party API We are again using our eloquent model and we handle everything in two lines after that we also need to Change our view. So instead of using a separate Variable we are now relying on the relation provided to us by the by the model and This should work, right? It's fairly simple three lines of code. It shouldn't break anywhere But it does and it breaks at a point where we forgot that we were using arrays And we're now trying to pass it an object of candidate experience So it violates the list of substitution principle because we weren't really aware of what we're using and How we need to access the data that we have we just replaced one class with another and we hoped it would work properly So what did it took to? Move to a local database First we needed to create the models for all of our database tables, which is Fine, we would obviously have to do it at some point Then we need and we needed to change all of the controller Dependencies so instead of depending on the third-party API We're now depending on these eloquent models and we have to go everywhere in the code and replace them manually Apart from that we also need to change the control controller logic because it needs to rely on The functionality provided by these new models instead of relying on the third-party API And this includes all parts of the system it includes the developer side where developers can log in and Edit their profiles or register. It also involves the admin side where we have management of all of the candidates and then Sending proposals adding profiles or everything else that was coming afterwards We had to change all the views because we were using an array previously and now we want to use objects and collections Of course, we need to adapt our unit as because we changed everything We obviously need to change them too and we need to retest all of it as a whole because We introduced so many changes in the system that it will probably break somewhere and what we estimated for this was actually 30 days It's probably an older version of the presentation with the wrong estimation But still it's something too long to to take to move from one One version to another so it's not something that we wanted to do because still we wanted to go lean and then Add things as we as we move forward So we can do better than this. How can we incorporate all of these principles into our code and make it work better and make it Easier to extend and and change in the future First we need to separate the responsibilities in specific classes So instead of relying on one large candidate class We would now separate everything into candidate experience candidate education candidate projects Candidate basic details. So separate all of them in smaller smaller specific classes that have names which Define what the class is responsible about The second step would be to design the code in a way that allows us to extend it by adding new code instead of Changing the existing one Third to be aware of the return type. So when we expect an array, we should never Send an object. So we we should think about this upfront and Use objects since the beginning We shouldn't depend on concrete implementations This would make it much easier for us to change to the new implementation afterwards And we shouldn't depend on unnecessary methods So we shouldn't depend on things we don't use because those things you can break code where it's not expected to break And this is the first step what we do here is actually creating an interface interface is for all of the The separate features that we have we have candidate experience interface which handle you which has all of the methods we need to to execute the update process all of the processes related to the experience and then we also have candidate education and candidate interfaces which are collapsed just for the simplicity but They basically do the the same thing and this is where we segregate the interfaces So this is where we comply to the interface aggregation principle because instead of depending on one huge class We are now depending on separate interfaces The second step is to create Repositories so each of these repositories would be the concrete implementation of our interfaces This is how things will work in the background This would be the the wiring and our interfaces will be the sockets So these classes implement the interface that we interfaces that we defined and they comply with the single Responsibility principle because they are small and they're specific third step would be to create separate separate classes that would mimic the Model classes which we would be using later. So over here, which would just have a simple class that Has methods for returning Relationships like education and experience in order to not have to create use separate variables and then change them with their relationships So this is where we think about the substitutions and we think about what our classes do and What their methods actually return so that we can know how to replace them at a later point And this is how our update function looks like so we got from Over 50 lines of code to a code of four lines where everything is delegated to separate Separate methods and separate classes and you will notice that this code is much more readable It's much easier to understand if you need to see how you manage the experiences You would probably find the Implementation of the candidate experience and see what's done over there if you don't need it Then you don't need to read through unnecessary code in order to get to the point where you need to change something So you will notice here that we depend on the interface instead of the actual repositories and this is where we do the Dependency inversion so we depend on abstractions and with this we would be able to just change the implementation in the background By adding new repositories and everything would work We would not need to go through all of our code and all of our controllers to Replace the dependencies. We would just need to add new Add new files new classes and everything would work and That's the open-close principle. We extend by adding new code instead of modifying the existing one At that point our code would get tested well enough and it would get much more stable than it would if we were just Going back and changing everything all the time There's one change that we need to do and that's where We bind the actual implementations to the to the interfaces and it's just a three-lines change So instead of depending on our third-party API We're now depending on the new repositories and that's it That's all we need to change in our code to make it work with the local database So let's see how this worked out Again, we had to create the eloquent models for the new database tables We had to create repositories now that Work with the local database because our new structure requires repositories to handle everything We had to write unit tests for the new classes But only for them because everything else works out of the box and we need to bind the repositories So that change that we need to to make just make sure that when we rely on the interface It uses the repositories the repository that we need And that's it and With this we estimated five days and you might think it's Too big of a difference and it's too unrealistic, but essentially we are relying on 10 methods or 12 methods from the third-party API We don't have too many methods that we rely on the problem is that we are using those same methods over and over again In our code and then when we need to change something we need to go inside our code and Change change it everywhere if we if we were using this approach where You rely on the solid design principles We would just need to go in our repository write 12 new methods in all of our specific classes and that would be it so it's been a long overview, but To sum it all up. What does it take to become better developers? The first thing to do is to incorporate thoughtful programming So you should always care about what you're working on You should always think about what's the best solution for your problem instead of what's the easiest solution to it You need to think about the bigger picture talk to other people try to understand what your task Requires. It's not just a story. That's On Jira or whatever you're using for managing the project It's something more and it's something that's related to the business logic of the of the application that you're building And if you understand what you're working on you would be able to Understand how to implement it better. You would be able to write code It would be extendable and easy to maintain on the long run the second one is to think about the UX of your code and this is Developer experience in our case so the users of our code are other developers or we are the users of a code and You should be thinking about writing code that another developer would Like to to continue working on you should write code that other team members would like to hop on and continue working on it with you or you would be able to go back to something that you've been working on a year ago and Know what you actually did there because your code is well structured and it's readable and understandable So always think about even if you're not using the solid design principle principles Always think about writing code. That's easy to read and easy to understand We talked about this a while so using the solid design principles you can write code that's easy to maintain easy to extend and Is it to change on the long run, but it's not the rule So it always depends on your use case and it always depends on what you need to do How complex your code is and whether or not it should be extended or changed in the future? So think about it use common sense think about your trade-offs Whether you should spend more time structuring your code and writing better architecture up front or that's unnecessary because Solid takes a bit more time writing code at the beginning so it can get easier in On the long run to change it and to to extend it So it's your tool. It's not your goal. So you should never try to achieve solid as a goal You should use it as other principles to achieve code. That's easier to Work with and that would make your lives easier And finally, please don't laugh on PHP jokes Let's show everyone that we are a really nice community and we can build a lot of cool stuff with PHP Thank you