 And we're back. So that was the first talk with Sebastian and we have a second Sebastian for you So that we can offer you a choice of different speakers with the same first name. What a coincidence Hello, Sebastian Hi Where where are you dining in from? I am from Poland as well Okay, so Let me introduce you a little bit Your full name Sebastian Buczynski. I hope I got that right Yeah, it's close enough. It's fine. I know it's each tricky one. Yeah, yeah And you work for a company called Web Interpret as a principal software engineer yes, that's right and I can I saw that you've been using Python for nearly a decade and today you are talking about old applications and Something that of course if you've used Python for a long time, then you might have to fix your own ones But are you ready to present your talk? Yes, I'm ready So the topics refactoring a legacy Django app using OOP the stages use Okay, thanks So let's let's start so apart from all the facts that already were said about me I also happen to write Book about implementing the clean architecture, of course in Python I write a blog under bread scums closer that tech and I'm on the mission of advocating software engineering in Python world and that's the reason why I'm here and also moves us smoothly to the topic of the presentation, which is a Refactoring and the simplest definition. I could find is the improving just you just improving the design of existing code From the state it is in the current and we do not like it and we wanted to make it better And if you think about it well refactor is a kind of a journey when we move from one place to another But on a very technical in a cold level Let's see a simple example. So you have a bunch of code and Well, you see that the piece of it can be reused so You name it you extract it to a separate function and you just reuse it Whenever you want to so this transformation has an name and it's called extract function and Believe me or not, but there are dozens of such transformations recognized already But I assume you already know that because this is an intermediate session Maybe you don't know all the names, but you certainly have used them before you have ever rewrite wrote some code in the past So I won't be focusing on that if you're looking for some reference guide There is a refactoring by Martin Fowler or refactoring that guru, which is a catalog of these Transformations so if this individual transformations are Steps in our refactoring journey that we must remember that like for every journey or for every activity There are some basic hygiene rules and the same is true for refactoring So the first rule is that we do not refactor without tests Because we need to have some a way of verifying that the code still works or does the same thing that it did before we started Refactoring the big bank rewrite. This is not a refactoring. This is something that went very wrong and we just are working hard to undo damage and So what's the best moment to refactor? Well, I can't beg advices and this is an author of TDD book that we do refactor right before implementing new feature So if we are about to add or change something then first we do the hard work of refactoring To make the change easy and only then we implement the change however This is a great piece of advice However, don't get me wrong It works best in a clean code basis when you have a discipline when you're using TDD at so on and More often than we would like to we start off in the places that doesn't look like that or look like a pile of trash But don't worry. I got you for the purpose of this talk. I prepared Example jungle legacy jungle application, which is inspired by a real world one. Of course, it has been obfuscated to know after all it's inspired after a production code base and The application would be Some booking it will have some booking logic in it This will be a Django applications. I mentioned with Django REST framework So let's assume there is an endpoint when you can book a few things at once and what are they? Well, let's assume that you can for example reserve reserve some tickets to amusement park to a museum or a book a table in a restaurant in advance and pay for it At once so this is like a checkout process some sort and There are also a couple of other fields, but they are not relevant. So just skip them. This is not important for this talk and moving forward Into the serializer we see that it does its standard job And if you don't know the Django REST framework, it will just grab the data that's come with the request and we'll just create an object in the database to save it eventually and The logic is as follows first We try to validate payment card with zero authentication if you don't know what that is is that we can just try to Authenticate zero amount of given currency to check if the card details are correct If it failed then we just set on the booking that that something went wrong and we just finish the control Otherwise we check if there's anything to pay in advance because for amusement park and for museum You can pay in advance. You just well You can have a ticket for children or for adults whatever but for a Table in a restaurant. You don't pay in advance. You will just pay the bill after you finish the meal So that's kind of logic that sometimes this branch of logic will be entered sometimes not and then you have a Transaction and then you will try to authorize Payments once again using the booking object and if it failed and we'll just send some email about the failure And now the control moves to a mysteriously named function calls finish booking and if you enter it It instantiates a class called finish booking command Which has yet another method called the same as the method of a serialized your finished booking then we see some error handling namely if the booking itself failed that we have to undo the payment and send the email and Well, if we captured if we Authorize some money in advance that we need to capture it So this is an operation when we really take the money and in the end we Sync the booking with the CRM so that other departments in the company know that something such as this has happened And I want to draw your attention a bit to the decorator you can see here And this is static method So I wrote a blog post about static method in particular called static method considered the cold smell This doesn't mean it's necessarily a wrong thing But if you look at if you have a static method in your class It probably means that maybe it doesn't belong here or on the other hand It can be taken out very easy because it doesn't use anything from the class. Maybe it's there for another reason So let's just think about it as an opportunity to refactor but not necessarily a bad thing Coming back to the code If you look into the finished booking command this mysteriously mysterious class You can see that here we call the API of the booking provider We do some error handling although it's pretty useless and yet another method is used and Update booking another static method and we just set some field in the booking and we save it and Well, if you look at this class It doesn't really I would say it's really a function in disguise because all it does It's just someone had a good gut feeling that the serializer is becoming too big But just move it to another class that well Isn't really isn't really a good modeling example, but it's not everything yet In that system there was a situation that we had to support a few payment providers at once and to steer it We used a so-called feature flags. These are simple boolean Flags that can be set in the Django admin panel to steer which payment provider we use So one of them had a dedicated SDK library which we could use but it has its own API it has its own arguments and the Signaled errors by rising exceptions and by the same time we had to support the old legacy one and This also had some SDK, but it was much worse and it didn't rise exception But it's a return to errors as a risk as a return a return values from the from the calls and If you ask yourself, how could this happen? How people let this code deteriorate so so bad Well, you such things usually happen Thanks to several factors one of them is that we are not given time to refactor Maybe we don't make the time as I mentioned in this method when we always refactor before introducing a feature Maybe we aren't knowledgeable enough about good software designs So we just you know try to stay afloat and this is at certain point of career pretty pretty normal You should have mentors and we are always learning all the time. So so it's also fine and Sometimes we're working under extreme time pressure. So just to add new features So it's always easy to just scramble some code into existing structures instead of thinking how to refactor them And we were also work on too many tasks or projects at once So which also is not good for our concentrations and focus We cannot just think about better ways how to do things but these things mostly contribute to so-called accidental complexity so, okay, this code base is bad could be better and the The relief there is to just to refactor to apply these transformations Okay, so these are things that can be refactored away and we can just do better. For example this useless error handling we could just not use it and it would be good but if we look at the system again and We see there's pretty much pretty lots Other systems that we have to cooperate with and these are like these two payment providers There is a booking provider. There is this there is this internal CRM and there is this email gateway and So it means that well It will be just complex because there's a lot of moving things in this system And if we look closely if we zoom in into the code base, you will see that the design doesn't really Doesn't really support this in any way literally every class talks to every other system or like exceptions may be leak to serializer which from the booking provider, which was not like doesn't show Intentions of this design and also if we Try to transform this algorithm. They were just more browsing through it in the code We can visualize in a form of a decision tree. This is a bit simplified because it doesn't have this feature toggle, but my point is that this code is complex not only because Someone was working under time pressure or wasn't knowledgeable enough at the time of writing But it's also that this feature is just inherently complex and this is called essential complexity So we have no way to escape it we all the only way to deal with it is to manage it and if you don't want to manage it then the Only solution that's left is not to implement it at all. So well, it's not an option Obviously, so we need a way to cope with the essential complexity and so just happens that this object-oriented design folks have been dealing with it for well decades now, I guess and We will use it to to to manage this complexity Just a short reminder what object-oriented programming is so we model a data and behavior Together in a form of objects and we'll have plenty of it But an important note here is the fact that you are writing classes doesn't make your code object-oriented yet Look that code that I just showed you had classes yet the design Well, didn't look like an intelligent one for me and it's not the as the whole. Yeah, it wasn't very intentional Because the good object-oriented design would be like like cells creating an organism Well, of course, it's still a system composed of small parts But each individual part is pretty amazing and pretty smart and the cells in the bio Biologic organism are pretty smart and there are a lot of mechanism inside which makes individual so pretty pretty pretty great Or like a community members. There are individuals. They do cooperate with each other but they still are smart and Well objects are smart when they can do something with the data they know and we'll see some examples In a moment or another example on analogy of what the good object-oriented design is like is like actors performing a play Maybe this is even better than the community because actors are like there for a reason they are they are cooperating together to achieve some goal of well performing a play and But each of them is also Individual. Yeah, so if we think about object-oriented programming, we often find ourselves with a basic Basic features of object-oriented programming like inheritance composition, encapsulation, abstraction, polymorphism Or if we dig deeper for maybe good practices, we'll find a bunch of Smart acronyms like solid grasp and the bunch of rules like tell don't ask or law of the matter and Well, don't get me wrong. It's all true and it's helpful yet there's another take on object-oriented programming that has been overlooked a bit and I think it's the right one to start with if you only want to learn what the object-oriented programming is all about and you Want to unleash its power and this is? responsibility-driven design and Don't get me wrong. This is not wrong. This is not Not old This is not oh, oh my god. This is not new. This is 20 years old concept, although at all It has been described in a number of books that I will just show in the end of the presentation at the responsibility-driven design Focuses on three aspects of modeling Objects, of course. So first of all, we have collaborations meaning how do objects talk to each other which objects see each other, etc and responsibilities of the individual objects and Responsibilities are three things what object knows what object does and what it decides about So we in the responsibility-driven design. We are very conscious about assigning responsibilities to individual objects And in the end we have roles. So roles are basically just sets of responsibilities And it's all to answer one burning question. Where do I put that code in an object-oriented code base? So it makes sense and in order to do that responsibility-driven design gives us a very handy tool which is called role stereotypes And if you think about Some movies you watch in the past You should notice that there is a hero a good good guy usually He has a companion Sometimes the companion is funny and there's also a bad guy Which who makes the life of a main character miserable or creates a conflict or some crisis to tackle And if you look at another movie or a tv series Maybe completely different You can see the same patterns like there's a hero He has a companion and there's also a bad guy who threatens the entire world Don't tell if you don't see patterns here. So this is called in storytelling Characters archetypes. So these are like very basic blueprints for different types of characters when you're constructing a story and The same thing for object-oriented programming is called role stereotypes of rdd There are six of them And we'll just focus on three because I want to show you something practical that you can use and apply in your project and I won't be like Just reading you out loud the definitions And so regarding information holder. Well, the name is pretty self-explanatory, but let's dig a bit deeper So information holder is something that knows it provides information for others and well In case of jango models, it's pretty It's pretty obvious that they are fitting into this role stereotype because they do have do know information by Keeping the fields inside eventually get to the database But look about other things that are less a bit less obvious this total property It's meant to provide a total for this booking by Assuming all the Pre-booking totals, so we'll just do some calculations So it doesn't only keep the data inside, but it also can do some basic calculation Remember the part about making objects smart. So this is one side of it If and if we make an object keep the data, but also Let it calculate something based on the data it has we make it smarter And this is called a law of the matter which I showed you Showed you a bit earlier. So In this example A law of the matter is because we do not iterate over our Pre-bookings outside of the booking. We let it do inside. So Now one has to do booking that pre bookings and then iterate over it We just go one object further into the hierarchy So that's the law of the matter And the second thing if we have to change some data inside an object Maybe we can just change it without checking any conditions, but maybe There are some I don't know Validations we have to make some conditions we have to check So for example in this file payment, we must assume that the status is known So meaning that the booking is probably new and hasn't been like Is new and hasn't been Hasn't been done anything on it And only if the status is known we can change the status to file payment And this is another object oriented Principle or an advice so to speak tell don't ask Which means we don't just directly change the data from the outside of an object Especially if there are some conditions to check So we don't ask For the state of an object then change the state we tell the object to do something So it can well it can remain smart. So following just these two pieces of advice namely tell don't ask and law of the matter We favor Creating objects or classes that are more intelligent Okay, so that was the first role stereotype and the second one interface there Yeah, it's something some code structure that transform information and request between system parts Well, we don't have it anything like that in the code, but you will see Result after refactoring in a moment But well this code base actually screams for interface error because he has a lot of external systems to integrate with Well for emails, it's not that critical because as far as I remember Django has something for emails So we can just leave it to the framework, but for custom crm booking providers new payments provider legacy payments provider Uh, I think the code base would benefit from from introducing interfaces because if you look at the code We are doing it manually under if statements and we have different apis different ways of signaling errors And as you may imagine the same code with this if statement is repeated inside the code base It was repeated inside the code base And the third role stereotype, which we are going to use Is a controller a controller is just an object that closely directs the action of other objects so like a conductor it says other what to do in watch sequence and uh right now, uh, well There is something like this in the code base, but it isn't concentrated in one object It's split between booking serializer, which I wonder if it's the best place to keep it And it's also in the full finished booking command. So in order to comprehend the whole flow You need to jump from between two files between two classes to only be able to visualize this whole thing and before we run into Rewriting this project, uh, let me do a small disclaimer that we are not trying to work against Django. Uh, if you ever tried Doing something against philosophy of Django framework, you know, it hurts and if you haven't had such an opportunity yet Please don't do it. It's not the best idea. We do not want to Be smarter than Django authors were uh, we want to Do something on in areas when Django building blocks fall short because in the end If some responsibility we identify fits into some building block of a Django, which are model view in Django rest framework also serializer view set, etc And the object manager we should just use it and not invent our creations because it won't look good In fact, you can also assign some stereotypes to existing Django building blocks like information holder Of course it's a model a coordinator is a signal handler and coordinator is some code structure that reacts to events in this in Django's Terms it's called signals and will just uh delegate some Some tasks to other objects. So this is coordinator and the controller is a view, of course And please take a note that you don't have to write classes to to see these Roll stereotypes in your code as an example of coordinator signal handler is usually just a function Okay, so now that we know what are the options. Let's plan our refactoring And I have a proposition of a three steps Refactoring and this will be first of all we introduce interfaces And this will be a refactoring that will have probably the most most return of investment on on all refactorings because also always External system are something we do not control they put some non determinism to our code We cannot test with them. Usually we have to mock or patch them anyway So why not just make it a bit easier for us? And then we'll consolidate controller role because as you saw it was split between two classes I don't see any reason why it would be so it just makes it harder to read And the code is eventually written for other people to read it not for Not for the computer to to just execute it because computer doesn't really care Let's just you know feel a Feel some sympathy for the people that will be reading the code afterwards and eventually Just strip side effects of information holders and this is something I Usually advise to people But this is my rule of thumb that the object like booking should not communicate with other External systems in inside it should be offloaded to controller And the so the model remains just an information holder It can do some calculation the data itself can still check conditions But it won't communicate with external systems because that's where I think it belongs to controller Okay So regarding the interfacer our goal is to make it easy to use from controller So we can limit the amount of information needed to use it And provide a uniform to handle errors because I repeated for the third time One of the libraries used exceptions and the other one Used results to signal that something went wrong. So to make it easier to use from the outside. We'll just introduce An interfacer which will be an well abstract class and we'll have two implementations one phone for the other 41 library and and second for the other And also will limit will provide some simpler api for example this perform zero authentication Normally, we would have to call a method of one of these libraries Some methods for authorization and passing. I know float zero or decimal zero to just Tell it how much money it should authorize With interfaces we can get a more tailored interface So we'll just write a perform zero authentication method that can accept less arguments and well, that's always better The lesser the function accepts arguments the code will be easier to read and easier to reason about so we'll just make it easy Now about classes We not always need them An interfacer can be as simple as a bunch of functions thrown into some module And like this could be the case for the interfacer for crm system We can just have a single or a couple of functions thrown into a module And it will be our interfacer. It's not important if it's a class or not In terms of object oriented design The more important thing Is that we have this responsibility covered by some code structure And it's not like not covered at all like it was the case for interfaces or it's covered into a bunch of other other objects Yeah, so another cool thing about interfacer is that just like I mentioned external systems often contribute to no determinism There are difficult to test. We need to stop them anyway. So with interfaces We are creating such a stable stable point that we can use our patching and mocks on them so And also using static code analysis and type hints with tools like my pie We get extra Extra security and these points become becomes more even more stable and we do not have worried too much about the bugs So that's it for the for the interfaces. Let's move on to the controller. So The goal would be to have whole flow and overview of a whole flow in one place So we'll just make it by merging finish booking command class with booking serializer And we'll start using interfaces there already. So it also becomes a bit smaller a bit easier And in the first line, you can see I'll just be using this interface server payments and perform zero authentication Then handle error. This is an exception that we just introduced And if we need to pay anything now Look about the difference between before we had booking that amount that No booking that total that amount and now we just use a property if we need to pay anything now So it's also more more expressive And okay, if we go we go Forward we do call to the booking provider. We also can handle errors there And and that that would be it for the booking serializer Now I'm showing you an optional refactoring that you may use but you don't have to and this will be introducing Service like a service layer for Django. Now, I don't really want to start an existential Argument whether a service layer is a good idea for Django or not I'm just showing this to you as an example of what we can do further. So I mean View is something that in my mind at least it's coupled very much to the HTTP requests and so on and serializer Where the current logic lives inside? Well, it's meant to serialize or deserialize and not perform some Communication with third parties is at least that's how I see it and that's what its name would hint us to That it's not meant to do this stuff. So I will just leave the serializer as in the highlighted line with just the saving the booking model and then will offload the The responsibility for orchestrating all these external services and the booking itself with a booking service And its implementation would look just like a few slides before so I won't be showing it. It will just be calling these third party Third party services via interfaces And we can call it as a function And well, I won't be showing the exact implementation, but just pay attention to the To the outline of this class that it will just be Containing inside the payments Interfacer and the booking client. So in object oriented design This is known as composition and also encapsulation because as you can see the name starts with a single underscore So it's not meant to be used from the outside. For example, by charm ID won't hint you about it Okay, and last but not least the most The most pleasant part would be just removing all the side effects causing methods from the model Because as I mentioned and my rule of thumb is that such things do not belong to models at all With one exception, but I will tell you about it in a moment I think that these methods do not belong to to the model because this is due to Something that in object oriented design is called cohesion So cohesion is a measure of how like methods are Are using the fields inside and so on So Usually the booking will have all the information that are needed to cancel the payment or capture the payment At least from itself because it knows it's total and so on But suddenly It has to also know about the payment provider with about the crm about the email sending And so on and so on. So suddenly Booking needs to know a lot of other things a lot of other things and this will in the end Cause this class to grow unbounded and it will grow just bigger and bigger However, there is one very exception to these If our model is some kind of a process manager. So the process manager Is this kind of pattern that will just coordinate some Distributed multi-stage process and in this case It also has to keep Keep state inside to know at which state state it is And in this case our model will be like Not fulfilling just one stereotype of information holder But would be also kind of a controller or a coordinator depending on the implementation and so on So it's pretty normal for the objects to fulfill sometimes more than one role stereotype and well for the And well for uh, and it's also normal in storytelling when one character also fulfills multiple Archetypes like in the Shrek the donkey is not only a companion, but he's also a gesture So it makes people laugh all the time Yeah, so and well, that's it about the refactoring. So let's Think of for a moment what we just achieved So we just switch from a well procedural like style of programming to object oriented like We it's important that we didn't change behavior of the system At all it still works the same the same way it worked before the refactoring But now, uh, we have all these things nicely Smartalized, you know backs called objects. So and we also Used some patterns or all stereotypes to you know, make them a better metaphor because Eventually like I mentioned the code is written for humans and humans And that's also why the reason why design patterns are Are so popular in some communities because these are common metaphors They're understood widely by other people. I mean, it's not that easy I mean for in languages like python when we have a lot of freedom and we can also achieve A lot of a lot of things without implementing patterns by some 25 years old book But it's true, especially in storytelling archetypes I mean we quickly recognize some patterns and we can get more hooked onto this story They're just wondering who is who in this in this very narrative So everything that I told you has been inspired by the book object design roles responsibilities and collaborators by rebecca where's brock And this as I mentioned, this is almost 20 years old book. It was released in 2002 and It's a very nice introduction to object oriented design which stop you Which will stop you thinking about it like some mechanical stuff You'll see that a lot of examples I use are from this book So I warmly recommend it and well, of course, I showed you some piece of code And maybe you could tell on the spot that it was bad. Maybe you felt some emotions But if if not, and you're looking maybe for some more solid proof that this design Wasn't good and the object oriented one is indeed better Then rebecca also wrote an article called how designs differ and in it she uses a different Metrics to compare one project written in procedural style and the other one written in an object oriented style And she shows some differences between them, but and then there is a conclusion which Style proved to be better in taming essential complexity. Of course spoiler are at object oriented was better and Since we are getting close to the ending of this presentation Let me just reiterate that all these object oriented Rules or principles or patterns how people like to call them are rather heuristics and not commandments These are good piece of advice May hint you towards a better design But the more you know about them, you will see that you can sometimes break them and nothing but will happen That's just that you have to do it consciously and It's not that the code that for example, I design always have to like Meet all of these criteria from solid or from grasp or whatever It's just that they give me hints about where to put what like it was with this side effects methods in in booking model Well, my knowledge about patterns and grasp For example tells me that this is not a good idea And also be aware that the design is more like an art and So there is no one right design There is no one right way to produce a design or to to code the project And there are many good possible solutions And some of them will be a bit better than others But a lot of them are just feasible which just work fine and also be advised that What today is a robust design and what works today tomorrow may not because a new requirements arrive and you will have to refactor anyway And in the end, please don't do a long refactoring journeys like that Rather do it daily and improve your code bases gradually So in the end, well, you don't have to do some big banks refactoring when you have to stop delivering for weeks or months Because that's not how it's done. It's very risky So that's it. That was the presentation about refactoring legacy jungle application using object oriented programming That's my photo when my hair are cut and birdscrumbscollector.tech is the address of my blog. Thank you Thanks very much for this talk That was a lot of information In something which did not seem such like 45 minutes I would like to remind everybody who watched that that the slides and the example code are both linked on the schedule So if you want to look at this again in your own time You can find it there. We had some nice discussion going on while you were doing the talk And there were some questions, but these would be too long to discuss now, but some nice comments For example, somebody like seerjan really liked your analogy for Object oriented programming is like actors performing a play So you got a good comment for that and of course there was stafani saying that you should not be doing this without tests um But uh, if you and and I see a lot of applause going on in in the Optor room at the moment. So yes, just just maybe quickly Adding something to these comments. I don't remember if this analogy about actors came from this book I supposedly saw But I know that these rebecca words broke contain these analogies for the cells and And also the the community. So that also is a great source of inspiration for that Okay, thank you very much If you have time, maybe you can log into the matrix chat in the optor room And answer some of the other specific questions or clear up some things in the discussion Um in this room, uh, we would now Take a short break and before we do that just a reminder that during the breaks We can you can always go into the hallway in the lobby You can check out the wonder meet chat where you can talk with cameras with other participants And if you have not done this yet, there is a virtual swag bag for you on the website that you can grab So again, thanks Sebastian and we'll see you all. Thank you very much. Thanks