OH MY GOD!!! He is back!:) How much time has passed since I learned design patterns using your playlist. How many things have changed! So happy you are here again! Thumb up in advance!🙂
I think Christopher came to us from another planet to help humanity understand the fundamental knowledge of software engineering, drawing upon our bright minds. He possesses pure pedagogical talent to distill the most abstract ideas into something digestible at the initial stage of consumption. Thank you, Chris, and please keep going! 😎
Best day ever! He's back! I'm looking forward to your unique way of making boring or difficult topics interesting! Your approach has really helped my career!
@@ChristopherOkhravi Really useful! I was a "Senior" dev but didn't keep up with dev stuff (learning) and couldn't get a job after I was fired. I landed at a low paying dev job and worked on remaking myself. Your content made it easy to get caught up and turn things around. I'm a team lead now and I make sure anyone that is on my team knows about your channel and especially the design patterns content. (and to not do what I did. LOL)
@@ChristopherOkhravi it has indeed. I assume by now, you have completed your Ph.D. I don't know if other subscribers are interested in this topic, but I would love it if you could make a video (or series) where you talk about your journey in getting your Ph.D. I took a class or two towards my doctorate, but had to drop out due to professional and family responsibilities being too overwhelming. My youngest son pleaded with me a few years back to resume that journey. But now, as an almost 56 year-old guy, I am not certain if I should or not. I would love to hear your honest take on your particular experiences during that time. I hope others share my curiosity as well.
The problem is that in a lot of commercial projects, "It's more important to move forward" is also true the third time you need to duplicate code, so you never have time to stop and refactor
that’s what Unc Bob try to alert in the Clean Architecture book, within the first couple chapters. for the business folks, behaviour is more important and urgent than architecture, and that’s exactly why we should “fight for the architecture”, to prevent legacy and crap code in our systems
There is such thing as too much refactoring... adding too much abstraction and anticipating "future" modifications is a nightmare in the long run. Thank you for gr8 vids really helped a LOT! 😄
You provided the best argument for when to use composition over inheritance I've ever heard in this video with the 3 strikes rule. All the other times I've seen people arguing for composition they are either using inheritance wrong or stuck in a strictly typed language, which greatly reduces the power of inheritance. While good use of inheritance doesn't fit in with the SOLiD principles perfectly it still fits with them with a little leeway for interpretation, and with inheritance over composition it is what makes the most powerful pattern thrive, the strategy pattern.
People say he's back.... I am a new viewer seen few of his videos... "Any good coder can make UA-cam coding videos, but it takes a great mind to make practical coding videos like this"..... Great work Chris 😊
Man! I’m really glad you’re back! Your videos helped me insanely. And you made a huge impact on how I think and treat things. I am who I am as a senior developer partly (and it played a big part) because of that influence. I’ve also helped a few people by using examples from your videos. Especially ducks stuck in my head tbh, for some reason. :) Thank you very much, will be waiting for more of your uploads!
This book is a gem, and I swear by it on my bedside! Though in my workplace, they have SonarQube enforced which blocks at the first sign of duplication, thereby making it a "One strike then refactor" rule.
As someone who has refactored several commercial Android application projects, these rules do apply. The only abstractions I would consider initially are architect-level abstractions, such as defining the repository interface in a domain module.
This should be mandatory watch for management, especially the 3. point. Move fast, don't touch other parts, move fast, get the thing out and then have endless meting why things fail after you complained multiple times that there are problems that need to be addressed.
Really good book. I also recommend it. Could you maybe do some videos in futures with concrete refactoring examples. I have so many things that I stumble on daily basis and I never found the proper way how to solve them. Many times it feels like there is a better way and I just didn't find it yet.
I feel like a lot of these things can already be solved by keeping to a single responsibility principle. Each function has a single responsibility and is named accordingly. Makes it much more human readable. If you already have a function for something, you can automatically reuse it, you don't have to double code. If you have sorted your functions like that, and you need to do something else, you can easily just call another function, rather than rewriting a whole block of code. Granted, there will be times where this won't do, because the system is more complex, and the refactoring takes place at a higher level. But you can catch a lot of things early on, by keeping this in mind.
For nr 3. I would refactor "first make the change easy" to "first make it easy to change". It should be easy to make unknown future changes and not just easy to do the current change. If you only think about the current change then you might do some stupid abstractions that are likely to come back and bite you. The result might often be the same with both mindsets, but not always.
You are a great (GREAT) teacher! And speaking of valuable software (the ability to change quickly to reach valuable software), what's your idea of valuable software?
(3) is great but here's how it bite you in the back:You can't make *any* change easy, and refactoring to make certain type of changes easy might easily lead to making other changes harder. Also the projection that 1 change is likely to be followed by next one does not always work. For example, there's a page that lists orders and allows filtering that list. You can make it easy to add new filter expressions, but the same refactoring might make it harder to catch corner cases. If the first request after release or beta testing is "add new filter expression", sure, you probably missed something in the spec, and great, you're improving, but who's to say the next change isn't going to be "deal with empty subset properly"? You must keep in mind that you probably don't know what the next change request is going to be! (After all, if you did, you could probably make the change right away, right?)
Thanks for the detailed perspective. We are in completely agreement on the fact that you can't foresee what the next change request is going to be. This is why I chose the formulation that a change today is "evidence supporting the hypothesis" that something along those lines have to be changed in the future. But that too is a hypothesis 😊 The point about one abstraction making another abstraction harder is a very challenging one that has been occupying my brain a lot over the years. I've come to think that in most cases we just haven't been thinking long enough about our problem and that in most cases an elegant abstraction exists that allow both "axes of change" to be "simple". But, of course, the cost of finding that abstraction might be very expensive and therefore not justifiable when compared to simply duplicating code. So that's why I think the second point actually serves a very important purpose. Sometimes the "easy change" (in terms of number 3) is to duplicate code. But once we've duplicated too much (in terms of number 2) then it's time to start digging and spend time to find a better abstraction. What do you think? 😊
@@ChristopherOkhravi I agree that (2) is a great antidote to the problem I described. To balance these I usually end up doing kind of both at the same time: For example (and this is what I've been actually working on for past 2 weeks), I have 2 pages which are both listing items in a table and have set of filtering elements. Initially, following (2), I made both implementations separate. But notice: *blindly* following (2) could easily mean that their implementations could be separate but different, perhaps even done by 2 different people. What I did is (kind of anticipating (3) in my mind) is that while I kept them separate, I would go great lengths to keep them very similar. Every time I would make change in page A, I would go and try to figure out how to make the same change in page B, without changing the functionality. I would actually pull out bcompare to make sure that even coding style and naming is basically identical. Yet still keeping them structurally separate. That is, I would already be refactoring B, to make change easy---but not just any change, a change that was actually happening for its cousin, page A. It would be through series of such changes (always first A, then B, or vice versa) that the true similarities and differences would become so apparent that eventually, I could actually go and merge them. A side note, that the process and methods were different on front-end and back-end. On FE this meant merging the listing into one Vue component, on BE, it would mean defining a common paging/filtering/sorting interface (typing.Prototype) and a having the repository as well as middleware to support that. Being able to work simultaneously on FE and BE helped a lot, but I suspect that in theory, with good communication these sides could have been done independently. That's a whole another interesting discussion.. Back to my balancing (2)(3). A generalized version of it that I usually have this hunch that something is going to be (or ought to be) a common pattern (eg. pages with filtered/paged/sorted items). The trick is to follow this hunch but not by doing *actual* abstractions but starting to organize things in such a way that they can undergo this period where things that are affected by this "hypothetical pseudo pattern", can exist as forks of each other, exchanging ideas and requirements. This allows me to test my pattern hypothesis, and once I get enough confidence in it (this can take days or years, depending on project!), I start actually implementing it as code. But not sooner.
Also note that what can be really critical in this "pattern-testing" period is having feedback from real use cases. For example, run it with an empty result, slow-loading result (not just slow loading items but perhaps slow loading filter options). Run it with errors (can you handle errors?). Have a (beta) tester (or your product manager) play with both pages and see what they feel like. This will provide you with invaluable feedback about what are some necessary changes, as well as help identify places where you could have opposing requirements clashing and disproportionately adding complexity -- that can be a moment to re-evaluate if there's indeed is a pattern, or there are multiple smaller patterns that need to be proven separately and then composed into an intuitive experience. I feel that the moment when I have to abandon a "pattern hypothesis" can be painful, but also liberating, often times the very next thing I will realize is either that I discovered set of smaller patterns that I can test more easily, or that I can take from eg. a well-tested third party library.
@@ChristopherOkhraviI think that is what data oriented design is all about, modeling problems in the way that allows for the most flexible and efficient structures with minimal overhead. I've found it makes change quite easy in most circumstances once you learn the patterns
Thank you very much for providing this perspective. I assume there's a lot of people in this camp as well and it's difficult for me to tell which camp is the bigger camp 😊
“programming languages are about communicating with humans. if it weren’t, we’d still be punching holes in cards” or something like that, was said by a wise man one time
About idea nb 2: the thing is the duplication is likely yo be flagged in the PR. I get the idea but in practice in my company we tend to almost never allow duplications...
I am not the fastest programmer at the company, but I leave better code behind than I started with. I don't care about the duplication rule. I make sure that the next person will have an easy/easier time to understand that part of the software.
I don't particularly like 2nd point, since people often abuse that rule. Instead of writing simple 3 line function in same class, they copy paste it instead.
I agree. I think you are right. We should add some context to it. It should perhaps be something like: unless the abstraction is obvious and simple, three strikes then refactor. What do you think?
Thank you so much for your videos. I like them very much! One additional thing that has been very important to me is. Make bigger changes in small consistent steps. From one working state to another working state - so the systems keeps working at all time. In my old days, when I needed a change, I broke everything for DAYS, changing million different things at the same time. Until the change was SO complicated that I could not understand it anymore, and certainly couldn’t merge the code back, so I ended up reverting the whole thing. What I waste of life! 🥲
Thank you for sharing. This is imho such an important point and I relate to this so much. I agree that it's definitely also one of the key ideas from this book. Writing it down in the notepad and will try to bring it up in a future video 😊
In principle this is great, in practice I've seen this thinking often leads to abstractions/indirection that 'makes things easier', but only for the person that writes the abstraction, or only for the state of the project at that time (been on both ends of this). The heart is in the right place though. Much better than overengineering systems, solidifying your job position, and declaring 'skill issue' at people that don't understand it (only been a spectator on this so far).
I find these rules a bit contradictory. I like follow the rule of moving as fast as possible to the valuable software (rule #2) but i was several times critisized for this and my colleagues made decision to make another iteration of the development just to make the code more clear. So basically i would propose to wait for the second or third strike (when we need to change that code) and only then refactor the code to make it simple for humans (because it may happen that this code never fails and the time on improving it is just wasted). But the first rule says we should write code that (other) humans can understand. Same for the third rule. It suggests to start refractoring to a simpler version at the second strike, while rule #2 says "do it later".
Subscribed after the second video. You have a fun style, your presentation style is quirky but refreshing. If you want to get better I'm this regard, you might like Richard Feynman
i got a rule like this, focus on the 90% program lifetime. a lot of devs focus on making program up front, with class diagrams and "reuse" of code with inheritance, with long chains of classes. but a program will Always change, so you can't make a program as if it was a building. refactoring is a pain with software like this, you need to break chains, move code a round. if you don't use inheritance, if you use functions and pipes(FP), then you will always be able to add a function to the pipe. you only need to change few places. so don't focus on the start of the program lifetime, just get start, make stuff so they always is easy to change, and move fast.
Regarding the point number 2, while it makes sense, it is hard to follow when working for a company! You would usually get criticism for refactoring other pieces that are not related to the ticket you were assigned to do, or your manager would notice to difference in the time spent working on somewhat similar code/functionality, or you would be blamed for writing a code that is "not as efficient" to begin with! Those things need to be discussed among teams before being practiced individually.
I have the same edition. Shame the second edition (the black one) didn't EMPHASISE on the cover that the code examples were rewritten in javascript. Such a bold move to make it more approachable.
Good video. Please try not to get too close to camera. Once or twice would be okay but more than that, it forced me to look away and just listen to the audio. Apart from this and constant slicing of the video, the video was awesome.
Don't write TODO comments, they are awful. They are supposed to be resolved in the same session, otherwise I'd write it down in your task list, Kanban or scrum w/e. Just a minor nitpick, good video tho.
I watched this video thinking i was gonna disagree with it (since I disagree with a lot of "clean code" ideas), but these are good ideas. What I dont like about clean code is the dogmatic insistence on refactoring to tiny functions. Maybe it makes code easier to understand at a high level, but debugging it is a PITA.
Glad to hear. I think these points strike a pretty good middle ground. And I agree that’s it’s very problematic to get too dogmatic. And I’ve surely been guilty of that more than once myself 😊😊 Thanks for the thoughts.
I don't think this rule of thumb is really valid. There are too many examples where it either isn't true, or actually contradicts the goal, compared to the process. Software is meant to change, everywhere. No code can be isolated and said "it will never need to change". The whole point of software is that it has the ability to change. To then claim there is a part that is permanent, would defeat the purpose of making it in software. If your code will literally never change: Make it hardware. No point in making it software. (Hint, no one is asking for hardware components to be built to solve your specific tasks that software can solve, because, software is easier to change, when it turns out the operation did need to be altered) -> Going towards inifinity, all code will change. This means that you shouldn't need to refactor at all. One way to accomplish this is to technically implement the strategy pattern as a basic baseline. (With factories for whenever you need multiple implementations) This will allow you from the ground up to extend your code where ever it is needed, and simply creating an interface, for behaviour, and then dependency injecting it into the class, will solve most of your overhead problems, from this basic abstraction. (It has the side effect of also making your code much easier to test) "Are you seriously suggesting I generate the implementation of all methods on a class as dependency injected interface implementations?" Yes, I am. YAGNI, is, in my opinion, a bad principle. Because it encourages programmers to no think about a solution, for the purpose of getting a fast implementation under the guise that it "likely" won't change". (Thinking in terms of forever, that statement is logically false) What makes you think you have time to write it properly later, if you don't even have time to write it properly now? Over-abstraction is only a thing, if you work from the bottom up, the inversion of control, should prohibit you from thinking this way, and ensuring you have the correct abstraction for any task you solve, as you will solve it from top down, not bottom up. Copy a file via from a persistence layer. Read file, parse file, alter data, store object in persistence layer,, f.x. is a process most of us have been through at some point. The abstraction for tasks such as this is basic: Example: IReceiveFile.CopyFile() -> IFileHandler.ReadFile() -> IParser.ParseFile() -> IDataHandler.AlterData() -> IPersistenceLayerHandler.StoreResult() -> Think like this is headlines for a scientific paper -> Do you wish to know something about the Receiving of the file? The file handling itself? The parsing of the file? How the data is altered? Or how the results are stored in your persistence layer? Where would you look? Simple right? Which Abstraction layer do you need to look into to find what you are looking for (Same as if you are bug hunting or trying to figure out where to make a change in a change request) Also simple. As you step into each layer, and define new contracts that this abstraction layer needs to again, furfill your wishes, you abstract behaviour again. You keep going until you reach a point where you have no more abstractions needed because your method now, does 1 thing. (You are in a leaf) You recognize a leaf, because you don't need to DI anything in, to do something, the task you are trying to complete, is the only task that makes sense when looking at the name of the method you are in. Think of the next abstraction layers as "sub-headings" in your paper. And so fourth. Once you have reached the specific point you are trying to make, from trying to prove some arguement, then you are at a "leaf" level. This is where there are no dependency injection required to complete a task -> All leaf's must do only 1 thing. (They either return something -> Ie. a "read" or they have an effect -> Ie. a "write".) I don't want you to "trust me". I want you to try it, and see how well it works out for you, as I started doing this, in the beginning it was a little tricky. But as I got better at naming, and separating behaviour, it became extremely simple to alter my code, when changes/bugs occured. My estimates are now much more accurate. Other programmers can take over my code, and also fix issues, without me needing to "introduce them" to the code. And I theirs since I have required all our team members to do the same. Claiming that there is a reason to not write the code properly from the beginning, because you are trying to be fast, is a big mistake. You will be slow (eventually). You might get the code out fast, but you won't get the code out that is wanted, the code that is flexible, the code that is agile. You will get rigid code, that can't be altered without weeks or months or refactoring and testing. YAGNI is as prophetic as someone claiming you absolutely will need it. There is no way to tell, which bits will be require a change. Therefore you must assume that any part of the system can change, and therefor have an extendable implementation available to handle new implementations. Do you want a system that highly coupled and has little coheision in terms of what it's classes do? Or the alternate situation where you can handle basically any change that comes at you, because you wrote the required abstractions to create code a professional would be expected to write? If you write your code extendable to begin with, refactoring won't be "required" or only extremely minimally, you will only ever write extensions, and potential modifications will be trivial to handle. I Know that this reads as a "He is trying to solve everything with a hammer, even when he has a screw". But if you try it out, you realise that this isn't true. This does make your code extendable, easy to test, easy to read.
Apologies if that felt like clickbait. I put him in the thumb since I thought his picture would be more recognisable than his name. But I can see how that might feel misleading. Will try to do better. 😊
I assume you were being slightly tongue in cheek with your original comment. But I actually think that you might be entirely right. I have to think more about it but it seems like a phrasing like that might be very useful going forward. Thanks for sharing.
Humans can read all code that isn't machine code right? ...or maybe some humans can read that, I have no idea, but what does that mean "humans can read"? There's some pretty complex code sometimes, and yeah you might have to step through it and figure out what's happening, but I think you'd have to do that whether you used all variables with single letters or big silly phrases like thisIsTheVariableThatDoesTheSubtractionBit which is probably equally bad. Now, I've never read this particular book you're talking about, is that what you're driving at when you say "humans can read"? You didn't give any examples. Even my own code, which follows all these little rules about how you should write or name things, even if I leave and come back after six months, sometimes I still have to step through it and figure out what the point was. I think that will always be the case. The 2nd idea, I don't use that usually, but I have on occasion said, oh hey I keep writing this same thing, I'll just make it into a class or something and reuse it. But if it's just a quick function that isn't so involved then I'll just copy/paste and forget about it, especially if it has no real impact to the overall program. Something in a game that is heavy on memory, as an example, maybe instead of creating a copy of something over and over, I can reuse that copy intelligently or something. If it makes no impact, I'll just leave it. Also this is all saying "humans" can read and well, no other human than me will likely ever read my code as it's a game (I don't write other code), so do I really need anyone else to understand what I wrote so long as I do? Is it still important then that humans can read it?
Thank you very much for sharing your thoughts. 😊 The idea of code that humans can read, imho, applies irrespectively of whether we ourselves or our colleagues are the humans. Maybe it makes more sense if we approach it from another perspective: The more difficult it is to understand the code, the more difficult it is to change it. And since change is the only constant, meaning since we know that we will have to change the code in order to stay in business, then it is really important that we make the code as understandable as possible. Do you think that makes more sense?
Yes that makes sense. I feel like I understand my code pretty well, though again even after stepping away for say, four months, I still come back and go.....what the heck was this again? Ha. It just happens, I don't think there's a good way to prevent that without tons and tons of comments, and I just don't feel like it's worth it. I can sit down, stretch, grab a coffee, and crack it in 15 minutes. Not a big deal for me anyhow.
I am gonna give you a hint - do not slice your videos so much. Literally every world is a cut. Its's really hard to watch I am dizzy from looking and listening to this
Thank you for saying this, and thank you very much to those of you who upvote. It helps me get an idea of what you all think. Since I myself like things that are fast it's very difficult for me to tell how much is too much.
The substance here is great, as with all your videos that I've seen. However, there is one issue about your delivery: You're too much up in the screen. That was like watching a horror movie. It's distracting and actually makes watching the video unpleasant. Your exaggerated animation and enthusiasm are awesome - absolutely keep that, your style is great for teaching these ideas - but they become too much when your face and hand gestures are blowing up inches from the screen. My suggestion is to change nothing, except just do the things you're doing a few feet farther back from the camera.
Lol, Fowler saying we must read code that is maintainable but yet says that he prefers to code with dynamic languages ... I don't trust this dude anymore, it is because of dude like him that we have to use languages like python for big projects that does not require machine learning that would be 10 times less painful with statically typed language having good type system. I prefer 100 times how John Carmack see software development
OH MY GOD!!! He is back!:)
How much time has passed since I learned design patterns using your playlist. How many things have changed!
So happy you are here again!
Thumb up in advance!🙂
Happy to be back. Thanks for watching. 😊
@ChristopherOkhravi great to see you back! Love your videos.
I think Christopher came to us from another planet to help humanity understand the fundamental knowledge of software engineering, drawing upon our bright minds. He possesses pure pedagogical talent to distill the most abstract ideas into something digestible at the initial stage of consumption.
Thank you, Chris, and please keep going! 😎
Hahah. Thank you for your kind words. 🙏😊
First make it work, then make it clean, and finally make it fast (if needed indeed)
Best day ever! He's back! I'm looking forward to your unique way of making boring or difficult topics interesting! Your approach has really helped my career!
Makes me very happy to hear that the content has been useful 😊
@@ChristopherOkhravi Really useful! I was a "Senior" dev but didn't keep up with dev stuff (learning) and couldn't get a job after I was fired. I landed at a low paying dev job and worked on remaking myself. Your content made it easy to get caught up and turn things around. I'm a team lead now and I make sure anyone that is on my team knows about your channel and especially the design patterns content. (and to not do what I did. LOL)
@@ChristopherOkhravi it has indeed. I assume by now, you have completed your Ph.D. I don't know if other subscribers are interested in this topic, but I would love it if you could make a video (or series) where you talk about your journey in getting your Ph.D. I took a class or two towards my doctorate, but had to drop out due to professional and family responsibilities being too overwhelming. My youngest son pleaded with me a few years back to resume that journey. But now, as an almost 56 year-old guy, I am not certain if I should or not. I would love to hear your honest take on your particular experiences during that time. I hope others share my curiosity as well.
The problem is that in a lot of commercial projects, "It's more important to move forward" is also true the third time you need to duplicate code, so you never have time to stop and refactor
that’s what Unc Bob try to alert in the Clean Architecture book, within the first couple chapters. for the business folks, behaviour is more important and urgent than architecture, and that’s exactly why we should “fight for the architecture”, to prevent legacy and crap code in our systems
Bro is cooking videos nonstop 🔥
🚀😊
There is such thing as too much refactoring... adding too much abstraction and anticipating "future" modifications is a nightmare in the long run.
Thank you for gr8 vids really helped a LOT! 😄
Yagoni
Yes, humans suck at future predictions. Pretending like we are not is a big issue.
3rd strike rule - agree almost completely, there's rare cases when 2nd strike is enough too
You provided the best argument for when to use composition over inheritance I've ever heard in this video with the 3 strikes rule. All the other times I've seen people arguing for composition they are either using inheritance wrong or stuck in a strictly typed language, which greatly reduces the power of inheritance. While good use of inheritance doesn't fit in with the SOLiD principles perfectly it still fits with them with a little leeway for interpretation, and with inheritance over composition it is what makes the most powerful pattern thrive, the strategy pattern.
Single responsibility helps to reduce duplication. A class, a function must have a single responsibility.
Great to see you back. I got recommendation on youtube today and you were my first click !
🙏
You genuinely give people excitement to be doing what they do and to do it better. Rare combination to strike!
Welcome back Legend
People say he's back.... I am a new viewer seen few of his videos... "Any good coder can make UA-cam coding videos, but it takes a great mind to make practical coding videos like this"..... Great work Chris 😊
Thank you very much for the kind words. And welcome 😊
Good to see you back here, brother! It was about time! Thanks.
I like ideas of moving forward, make changes simple for the future, and refactor later
Happy to watch your videos again, you really have a knack for explaining stuff in a clear and engaging way.
Happy to see you again, Christopher
Yay! Christopher is making more videos!
Please make a video on the Flyweight pattern!
So good that you are back!! I really hope you continoue the series about object oriented programming. That would be awesome😁
Man! I’m really glad you’re back! Your videos helped me insanely. And you made a huge impact on how I think and treat things. I am who I am as a senior developer partly (and it played a big part) because of that influence.
I’ve also helped a few people by using examples from your videos. Especially ducks stuck in my head tbh, for some reason. :)
Thank you very much, will be waiting for more of your uploads!
Thank you. I’m very glad to hear that. 😊
I am really happy to see you again ! Thanks for your high quality content again !
Glad to see you've returned.
The third point is exactly how I think most of the time. Welcome back Chris! 🔥
Thanks, and glad to be back 😊
He's back! Love your videos! :)
Nice to see you back Christopher!
This book is a gem, and I swear by it on my bedside!
Though in my workplace, they have SonarQube enforced which blocks at the first sign of duplication, thereby making it a "One strike then refactor" rule.
😆😆
We're happy to see you again
I am so agree with the author of the video. I came to this myself through experience and it is 100% true.
great content dude! long time without your great vids! awesome advice!
As someone who has refactored several commercial Android application projects, these rules do apply. The only abstractions I would consider initially are architect-level abstractions, such as defining the repository interface in a domain module.
Thank you very much for sharing your experience.
love the continuous stream of videos! Keep it up 👍
thankyou for sharing these. the patterns have helped me a lot over tehe years
That book helped me a lot. Thanks for bringing up refactoring topic
So much energy! 😊
❤❤❤ great video man, again welcome back
This should be mandatory watch for management, especially the 3. point.
Move fast, don't touch other parts, move fast, get the thing out and then have endless meting why things fail after you complained multiple times that there are problems that need to be addressed.
The third one was really good. I loved it. Good to see you back
Omg you're back!!!! Loved this book
Great to see you back
Love the second one. Premature abstraction and over-engineering are really killing productivity in many projects.
Really good book. I also recommend it.
Could you maybe do some videos in futures with concrete refactoring examples.
I have so many things that I stumble on daily basis and I never found the proper way how to solve them. Many times it feels like there is a better way and I just didn't find it yet.
I think it was a helpful video. Succinct and to the point. Good job!
Thanks for the video. Enjoyed your series about non-coding books you have read. Would be super awesome if you could continue it.
Wow. I'm happy to hear that. Such a long time ago. I'm still a fairly avid reader so might consider sneaking a video in once in a while 😊 Thank you.
I am waiting this video for years.
Thank you for this valuable video.
welcome back man
I missed your videos so much!
I feel like a lot of these things can already be solved by keeping to a single responsibility principle.
Each function has a single responsibility and is named accordingly. Makes it much more human readable.
If you already have a function for something, you can automatically reuse it, you don't have to double code.
If you have sorted your functions like that, and you need to do something else, you can easily just call another function, rather than rewriting a whole block of code.
Granted, there will be times where this won't do, because the system is more complex, and the refactoring takes place at a higher level. But you can catch a lot of things early on, by keeping this in mind.
For nr 3. I would refactor "first make the change easy" to "first make it easy to change". It should be easy to make unknown future changes and not just easy to do the current change. If you only think about the current change then you might do some stupid abstractions that are likely to come back and bite you. The result might often be the same with both mindsets, but not always.
You are a great (GREAT) teacher!
And speaking of valuable software (the ability to change quickly to reach valuable software), what's your idea of valuable software?
(3) is great but here's how it bite you in the back:You can't make *any* change easy, and refactoring to make certain type of changes easy might easily lead to making other changes harder. Also the projection that 1 change is likely to be followed by next one does not always work.
For example, there's a page that lists orders and allows filtering that list. You can make it easy to add new filter expressions, but the same refactoring might make it harder to catch corner cases. If the first request after release or beta testing is "add new filter expression", sure, you probably missed something in the spec, and great, you're improving, but who's to say the next change isn't going to be "deal with empty subset properly"?
You must keep in mind that you probably don't know what the next change request is going to be! (After all, if you did, you could probably make the change right away, right?)
Thanks for the detailed perspective. We are in completely agreement on the fact that you can't foresee what the next change request is going to be. This is why I chose the formulation that a change today is "evidence supporting the hypothesis" that something along those lines have to be changed in the future. But that too is a hypothesis 😊
The point about one abstraction making another abstraction harder is a very challenging one that has been occupying my brain a lot over the years. I've come to think that in most cases we just haven't been thinking long enough about our problem and that in most cases an elegant abstraction exists that allow both "axes of change" to be "simple". But, of course, the cost of finding that abstraction might be very expensive and therefore not justifiable when compared to simply duplicating code.
So that's why I think the second point actually serves a very important purpose. Sometimes the "easy change" (in terms of number 3) is to duplicate code. But once we've duplicated too much (in terms of number 2) then it's time to start digging and spend time to find a better abstraction.
What do you think? 😊
@@ChristopherOkhravi I agree that (2) is a great antidote to the problem I described.
To balance these I usually end up doing kind of both at the same time: For example (and this is what I've been actually working on for past 2 weeks), I have 2 pages which are both listing items in a table and have set of filtering elements. Initially, following (2), I made both implementations separate. But notice: *blindly* following (2) could easily mean that their implementations could be separate but different, perhaps even done by 2 different people.
What I did is (kind of anticipating (3) in my mind) is that while I kept them separate, I would go great lengths to keep them very similar. Every time I would make change in page A, I would go and try to figure out how to make the same change in page B, without changing the functionality. I would actually pull out bcompare to make sure that even coding style and naming is basically identical. Yet still keeping them structurally separate.
That is, I would already be refactoring B, to make change easy---but not just any change, a change that was actually happening for its cousin, page A.
It would be through series of such changes (always first A, then B, or vice versa) that the true similarities and differences would become so apparent that eventually, I could actually go and merge them.
A side note, that the process and methods were different on front-end and back-end. On FE this meant merging the listing into one Vue component, on BE, it would mean defining a common paging/filtering/sorting interface (typing.Prototype) and a having the repository as well as middleware to support that. Being able to work simultaneously on FE and BE helped a lot, but I suspect that in theory, with good communication these sides could have been done independently. That's a whole another interesting discussion..
Back to my balancing (2)(3). A generalized version of it that I usually have this hunch that something is going to be (or ought to be) a common pattern (eg. pages with filtered/paged/sorted items). The trick is to follow this hunch but not by doing *actual* abstractions but starting to organize things in such a way that they can undergo this period where things that are affected by this "hypothetical pseudo pattern", can exist as forks of each other, exchanging ideas and requirements. This allows me to test my pattern hypothesis, and once I get enough confidence in it (this can take days or years, depending on project!), I start actually implementing it as code. But not sooner.
Also note that what can be really critical in this "pattern-testing" period is having feedback from real use cases. For example, run it with an empty result, slow-loading result (not just slow loading items but perhaps slow loading filter options). Run it with errors (can you handle errors?). Have a (beta) tester (or your product manager) play with both pages and see what they feel like.
This will provide you with invaluable feedback about what are some necessary changes, as well as help identify places where you could have opposing requirements clashing and disproportionately adding complexity -- that can be a moment to re-evaluate if there's indeed is a pattern, or there are multiple smaller patterns that need to be proven separately and then composed into an intuitive experience.
I feel that the moment when I have to abandon a "pattern hypothesis" can be painful, but also liberating, often times the very next thing I will realize is either that I discovered set of smaller patterns that I can test more easily, or that I can take from eg. a well-tested third party library.
@@ChristopherOkhraviI think that is what data oriented design is all about, modeling problems in the way that allows for the most flexible and efficient structures with minimal overhead. I've found it makes change quite easy in most circumstances once you learn the patterns
I personally disagree that its hard to understand words and there are too much slices, everything is clear and fast, as I like, great vid)
Thank you very much for providing this perspective. I assume there's a lot of people in this camp as well and it's difficult for me to tell which camp is the bigger camp 😊
Thank you! I really like your mustache.
Thank is good tips ❤
my favorite dude is back! shit! valuable content! thanks!
Thats what I shouldve done thanks!
better cuts, thanks!
“programming languages are about communicating with humans. if it weren’t, we’d still be punching holes in cards” or something like that, was said by a wise man one time
0:46
From the heart to the SUN!
About idea nb 2: the thing is the duplication is likely yo be flagged in the PR. I get the idea but in practice in my company we tend to almost never allow duplications...
I am not the fastest programmer at the company, but I leave better code behind than I started with.
I don't care about the duplication rule.
I make sure that the next person will have an easy/easier time to understand that part of the software.
More videos from you please !!!!!!!!
I don't particularly like 2nd point, since people often abuse that rule. Instead of writing simple 3 line function in same class, they copy paste it instead.
I agree. I think you are right. We should add some context to it. It should perhaps be something like: unless the abstraction is obvious and simple, three strikes then refactor. What do you think?
Thank you so much for your videos. I like them very much!
One additional thing that has been very important to me is. Make bigger changes in small consistent steps. From one working state to another working state - so the systems keeps working at all time.
In my old days, when I needed a change, I broke everything for DAYS, changing million different things at the same time. Until the change was SO complicated that I could not understand it anymore, and certainly couldn’t merge the code back, so I ended up reverting the whole thing. What I waste of life! 🥲
Thank you for sharing. This is imho such an important point and I relate to this so much. I agree that it's definitely also one of the key ideas from this book. Writing it down in the notepad and will try to bring it up in a future video 😊
Yeah, my coding viewpoint is that I'm writing to make it as easy as possible for the programmer who comes after me.
Great rule of thumb 🙏 Thanks.
Chat gpt
In principle this is great, in practice I've seen this thinking often leads to abstractions/indirection that 'makes things easier', but only for the person that writes the abstraction, or only for the state of the project at that time (been on both ends of this).
The heart is in the right place though. Much better than overengineering systems, solidifying your job position, and declaring 'skill issue' at people that don't understand it (only been a spectator on this so far).
As someone who write code that i could not understand week later i learned it the hard way
Let’s just say that I relate to this more than I want to admit 😊😊
I find these rules a bit contradictory.
I like follow the rule of moving as fast as possible to the valuable software (rule #2) but i was several times critisized for this and my colleagues made decision to make another iteration of the development just to make the code more clear.
So basically i would propose to wait for the second or third strike (when we need to change that code) and only then refactor the code to make it simple for humans (because it may happen that this code never fails and the time on improving it is just wasted). But the first rule says we should write code that (other) humans can understand.
Same for the third rule. It suggests to start refractoring to a simpler version at the second strike, while rule #2 says "do it later".
Thanks your video
Subscribed after the second video.
You have a fun style, your presentation style is quirky but refreshing.
If you want to get better I'm this regard, you might like Richard Feynman
Thank you very much. And welcome 😊😊 And thanks for the feedback 🙏
i got a rule like this, focus on the 90% program lifetime.
a lot of devs focus on making program up front, with class diagrams and "reuse" of code with inheritance, with long chains of classes.
but a program will Always change, so you can't make a program as if it was a building.
refactoring is a pain with software like this, you need to break chains, move code a round.
if you don't use inheritance, if you use functions and pipes(FP), then you will always be able to add a function to the pipe.
you only need to change few places.
so don't focus on the start of the program lifetime, just get start, make stuff so they always is easy to change, and move fast.
We are in agreement 😊
Regarding the point number 2, while it makes sense, it is hard to follow when working for a company! You would usually get criticism for refactoring other pieces that are not related to the ticket you were assigned to do, or your manager would notice to difference in the time spent working on somewhat similar code/functionality, or you would be blamed for writing a code that is "not as efficient" to begin with! Those things need to be discussed among teams before being practiced individually.
Thank you very much for sharing your experience 😊
@@ChristopherOkhravi btw, good to know you are back! I got so much knowledge out of your vids when I was doing CS in uni
damn u cooked damn well pie, so tasty I want more!
I have the same edition. Shame the second edition (the black one) didn't EMPHASISE on the cover that the code examples were rewritten in javascript. Such a bold move to make it more approachable.
100% agreed. There was imho no obvious need to replace the first edition which was still great 😊
Please stream on twitch, i think it will be awesome just hanging out with you
Agreed
you are the best
he is back with a a salesman attitude xDD NICE
I suppose I consume too much finance and entrepreneurship content 😊😊 Thanks for watching 😊😊
@@ChristopherOkhravi its always great to see someone who is self aware of the things he consumes < 3
Hello, Chris!
That's one sexy mustache right there.
I belief that today I started with point 2, followed by 3, resulting in point 1. Hmm, interesting.
😊😊
Good video. Please try not to get too close to camera. Once or twice would be okay but more than that, it forced me to look away and just listen to the audio.
Apart from this and constant slicing of the video, the video was awesome.
Thank you very much for the feedback. Makes a lot of sense and I agree.
Don't write TODO comments, they are awful. They are supposed to be resolved in the same session, otherwise I'd write it down in your task list, Kanban or scrum w/e. Just a minor nitpick, good video tho.
What was that accent at 1:43 😂😂😂
The term was: YAGNI 😊
I watched this video thinking i was gonna disagree with it (since I disagree with a lot of "clean code" ideas), but these are good ideas.
What I dont like about clean code is the dogmatic insistence on refactoring to tiny functions. Maybe it makes code easier to understand at a high level, but debugging it is a PITA.
Glad to hear. I think these points strike a pretty good middle ground. And I agree that’s it’s very problematic to get too dogmatic. And I’ve surely been guilty of that more than once myself 😊😊 Thanks for the thoughts.
I don't think this rule of thumb is really valid. There are too many examples where it either isn't true, or actually contradicts the goal, compared to the process.
Software is meant to change, everywhere. No code can be isolated and said "it will never need to change". The whole point of software is that it has the ability to change. To then claim there is a part that is permanent, would defeat the purpose of making it in software.
If your code will literally never change: Make it hardware. No point in making it software.
(Hint, no one is asking for hardware components to be built to solve your specific tasks that software can solve, because, software is easier to change, when it turns out the operation did need to be altered) -> Going towards inifinity, all code will change.
This means that you shouldn't need to refactor at all. One way to accomplish this is to technically implement the strategy pattern as a basic baseline. (With factories for whenever you need multiple implementations)
This will allow you from the ground up to extend your code where ever it is needed, and simply creating an interface, for behaviour, and then dependency injecting it into the class, will solve most of your overhead problems, from this basic abstraction. (It has the side effect of also making your code much easier to test)
"Are you seriously suggesting I generate the implementation of all methods on a class as dependency injected interface implementations?" Yes, I am.
YAGNI, is, in my opinion, a bad principle. Because it encourages programmers to no think about a solution, for the purpose of getting a fast implementation under the guise that it "likely" won't change". (Thinking in terms of forever, that statement is logically false)
What makes you think you have time to write it properly later, if you don't even have time to write it properly now?
Over-abstraction is only a thing, if you work from the bottom up, the inversion of control, should prohibit you from thinking this way, and ensuring you have the correct abstraction for any task you solve, as you will solve it from top down, not bottom up.
Copy a file via from a persistence layer. Read file, parse file, alter data, store object in persistence layer,, f.x. is a process most of us have been through at some point.
The abstraction for tasks such as this is basic:
Example:
IReceiveFile.CopyFile() ->
IFileHandler.ReadFile() ->
IParser.ParseFile() ->
IDataHandler.AlterData() ->
IPersistenceLayerHandler.StoreResult() ->
Think like this is headlines for a scientific paper -> Do you wish to know something about the Receiving of the file? The file handling itself? The parsing of the file? How the data is altered? Or how the results are stored in your persistence layer? Where would you look? Simple right?
Which Abstraction layer do you need to look into to find what you are looking for (Same as if you are bug hunting or trying to figure out where to make a change in a change request) Also simple.
As you step into each layer, and define new contracts that this abstraction layer needs to again, furfill your wishes, you abstract behaviour again.
You keep going until you reach a point where you have no more abstractions needed because your method now, does 1 thing. (You are in a leaf) You recognize a leaf, because you don't need to DI anything in, to do something, the task you are trying to complete, is the only task that makes sense when looking at the name of the method you are in.
Think of the next abstraction layers as "sub-headings" in your paper. And so fourth.
Once you have reached the specific point you are trying to make, from trying to prove some arguement, then you are at a "leaf" level. This is where there are no dependency injection required to complete a task -> All leaf's must do only 1 thing. (They either return something -> Ie. a "read" or they have an effect -> Ie. a "write".)
I don't want you to "trust me". I want you to try it, and see how well it works out for you, as I started doing this, in the beginning it was a little tricky. But as I got better at naming, and separating behaviour, it became extremely simple to alter my code, when changes/bugs occured. My estimates are now much more accurate. Other programmers can take over my code, and also fix issues, without me needing to "introduce them" to the code. And I theirs since I have required all our team members to do the same.
Claiming that there is a reason to not write the code properly from the beginning, because you are trying to be fast, is a big mistake. You will be slow (eventually). You might get the code out fast, but you won't get the code out that is wanted, the code that is flexible, the code that is agile. You will get rigid code, that can't be altered without weeks or months or refactoring and testing.
YAGNI is as prophetic as someone claiming you absolutely will need it. There is no way to tell, which bits will be require a change. Therefore you must assume that any part of the system can change, and therefor have an extendable implementation available to handle new implementations.
Do you want a system that highly coupled and has little coheision in terms of what it's classes do? Or the alternate situation where you can handle basically any change that comes at you, because you wrote the required abstractions to create code a professional would be expected to write?
If you write your code extendable to begin with, refactoring won't be "required" or only extremely minimally, you will only ever write extensions, and potential modifications will be trivial to handle.
I Know that this reads as a "He is trying to solve everything with a hammer, even when he has a screw". But if you try it out, you realise that this isn't true. This does make your code extendable, easy to test, easy to read.
So, Martin wasn't in the video......
Apologies if that felt like clickbait. I put him in the thumb since I thought his picture would be more recognisable than his name. But I can see how that might feel misleading. Will try to do better. 😊
Maybe we have to learn how to write code that both humans and AI can understand.
Nvm, we should make it incomprehensible to AI to keep our jobs
I assume you were being slightly tongue in cheek with your original comment. But I actually think that you might be entirely right. I have to think more about it but it seems like a phrasing like that might be very useful going forward. Thanks for sharing.
Humans can read all code that isn't machine code right? ...or maybe some humans can read that, I have no idea, but what does that mean "humans can read"? There's some pretty complex code sometimes, and yeah you might have to step through it and figure out what's happening, but I think you'd have to do that whether you used all variables with single letters or big silly phrases like thisIsTheVariableThatDoesTheSubtractionBit which is probably equally bad. Now, I've never read this particular book you're talking about, is that what you're driving at when you say "humans can read"? You didn't give any examples.
Even my own code, which follows all these little rules about how you should write or name things, even if I leave and come back after six months, sometimes I still have to step through it and figure out what the point was. I think that will always be the case.
The 2nd idea, I don't use that usually, but I have on occasion said, oh hey I keep writing this same thing, I'll just make it into a class or something and reuse it. But if it's just a quick function that isn't so involved then I'll just copy/paste and forget about it, especially if it has no real impact to the overall program. Something in a game that is heavy on memory, as an example, maybe instead of creating a copy of something over and over, I can reuse that copy intelligently or something. If it makes no impact, I'll just leave it.
Also this is all saying "humans" can read and well, no other human than me will likely ever read my code as it's a game (I don't write other code), so do I really need anyone else to understand what I wrote so long as I do? Is it still important then that humans can read it?
Thank you very much for sharing your thoughts. 😊 The idea of code that humans can read, imho, applies irrespectively of whether we ourselves or our colleagues are the humans. Maybe it makes more sense if we approach it from another perspective: The more difficult it is to understand the code, the more difficult it is to change it. And since change is the only constant, meaning since we know that we will have to change the code in order to stay in business, then it is really important that we make the code as understandable as possible. Do you think that makes more sense?
Yes that makes sense. I feel like I understand my code pretty well, though again even after stepping away for say, four months, I still come back and go.....what the heck was this again? Ha. It just happens, I don't think there's a good way to prevent that without tons and tons of comments, and I just don't feel like it's worth it. I can sit down, stretch, grab a coffee, and crack it in 15 minutes. Not a big deal for me anyhow.
I am gonna give you a hint - do not slice your videos so much. Literally every world is a cut. Its's really hard to watch I am dizzy from looking and listening to this
Thank you for saying this, and thank you very much to those of you who upvote. It helps me get an idea of what you all think. Since I myself like things that are fast it's very difficult for me to tell how much is too much.
I personally like it, it allows you to stay focused
Yeah, actually I like the cuts as well. It’s a special kind of style and it feels different from the others.
@@ChristopherOkhravi don’t worry about having empty space. It gives your words importance and helps (at least me) the audience understand your point.
I kinda like cuts too, they keep me focused. Looks like its individual thing.
The substance here is great, as with all your videos that I've seen. However, there is one issue about your delivery:
You're too much up in the screen.
That was like watching a horror movie. It's distracting and actually makes watching the video unpleasant. Your exaggerated animation and enthusiasm are awesome - absolutely keep that, your style is great for teaching these ideas - but they become too much when your face and hand gestures are blowing up inches from the screen. My suggestion is to change nothing, except just do the things you're doing a few feet farther back from the camera.
100%. Other have communicated this too and I’ve moved myself further back in newer videos 😊😊 Tgank you all very much for letting me know 😊🙏
Lol, Fowler saying we must read code that is maintainable but yet says that he prefers to code with dynamic languages ... I don't trust this dude anymore, it is because of dude like him that we have to use languages like python for big projects that does not require machine learning that would be 10 times less painful with statically typed language having good type system.
I prefer 100 times how John Carmack see software development
Happy to see you back