I don't love refactoring because I can't reliably feel the line between refactoring and overengineering What I love, tho, is to receive notification about new git-amend video upload ❤
Regarding what you mentioned about technical debt at the beginning, many years ago, during my first project as a lead, I had the opportunity to work with a very experienced architect, guy in his sixties I guess he must be retired by now. Well, one day, we were discussing a feature we needed to implement, but the timeline was pretty tight. I suggested we could do a basic, quick-and-dirty implementation and then "properly implement it later, after the release." He replied, "There's never a 'later'..." It took me some time and a few challenging projects under my belt to truly understand why he was so strict about technical debt... But Craig, if you're reading this, you were absolutely right!
This is really good! Nicely done. I actually learned the factory pattern from one of your previous videos. Now I use it everywhere possible. It ties cleanly with an object pooling system and jobs and burst. Please make more refactor videos like this one. And Happy Sunday!
This type of video is awesome .. not only will it encourage programmers not to start over "because I know better now" but also shows how to use all the important programming patterns you have already taught in previous videos. Great work :)
Hey there. I wanted to thank you from the bottom of my heart for all the amazing content you create. I'm a self-taught programmer and game dev, and after coding for 2 years, I hit a learning plateau. That is, until I discovered your channel. At first, I didn't fully grasp your videos, but I kept rewatching them over and over again, slowly refactoring my game's code. Nowadays, I feel like my code is on steroids, and it's all thanks to you
Absolutely love this refactoring video approach! It's so helpful to see the before and after, because I know so many of us work ourselves into corners and it can be quite hard to see a path out when you are too close to the project and have been looking at it from one perspective for too long. Thanks a lot for sharing these videos with us!
It's interesting to watch even though I knew the majority of concepts here, but there were some things that were new to me! It's a nice feeling to realize how much I've learned since I started. I really appreciate that you're giving good advice and habits right away despite being a bit more complex unlike many tutorials, that even I had to follow, which taught me some really awful habits that I had to spend a lot of time unlearning. It'll take time either way so better to take that time to learn than have to take the time later on unlearning and feeling unproductive because now you're kind of back to what feels like square one.
One of my favorite types of videos! I love seeing so many coding patterns in action. Some are still a bit tricky for me, but I was surprised to realize I’ve already used a few. Do you code like this in your own projects, or do you use more "niche" tools like R3 or other libraries/assets? I'd be curious to see some of your older code!
Thanks for the comment! I actually don't use too many niche libraries - mostly it's just regular C# when I'm coding. Of course I do use my own systems, like the Service Locator, Event Bus, Improved Timers and the Unity Utility library that we keep adding to as well.
Had a lot of refactoring to do this week as well. I ended up moving the factory pattern to the scriptable object directly, so it doesn't use a factory instance, plus I can override it in the child classes. I wonder what's your opinion on that. As always, keep up the good work, your code is top notch!
Having a static factory method is a viable option, especially if you don't need to mock the factory for testing purposes. I do it fairly often actually. Thanks for the comment!
Yeah when I can I love going static. But I actually meant moving the object creation to the WeaponConfig as a public function. That way you can make it virtual and override it for child Config classes. You could for instance load the projectile prefab for BowConfig or StaffConfig, or any kind of additional stuff. But in your scenario it was perfect because it was simple enough to not be required at all, since there's only WeaponConfig class for now.
Nice video content mate! Where would we need enum in that case? Can we solve that differently, every time we add new item type, we also need to add enum, thats what i dont like about enums.
@git-amend Sensei! Great video! Great way to showcase refactoring workflow. I have two questions regarding your setup. 1. How are you getting those Hierarchy Folders? 2. When you're doing null checks in Rider, I noticed there's a little icon there. What is that for?
The folders are an Asset created by Sisus that is extremely useful, I'll add a link in the description of this video. Actually, I recommend (and own/use) all Assets made by that creator, especially InitArgs and Debug Extensions. The icon in Rider indicates an implicit check on Unity object lifetime, which is different from regular objects.
Could you elaborate what the point of the interface for the Weapon is, when you just load it into the abstract class anyways? You could remove the interface from your code and have the exact same functionality.
The interface ensures a clear contract for any weapon implementation. This approach comes from the "start from interfaces" principle, rooted in the Interface Segregation and Dependency Inversion principles of SOLID. While it may seem redundant with an abstract class, I almost always start refactoring classes that have things in common by first defining the interface they must all adhere to.
Well, keep in mind that over engineering means adding features that were NOT requested. Refactoring is improving code so that you can facilitate changes that were requested. It's important to keep this in mind so you don't build things that are unnecessary, while still building the things that you do need in the best way you can for your skill level.
As it stands right now, you certainly could. However, one of the nice things about a Factory or Abstract Factory is it's use in testing - if you added an interface, it would be very easy to add a test version of the factory and just produce exactly the kinds of output that you need to executing test logic. We don't necessarily need it in this simple example, but injecting a factory is an extremely common practice. Another option you could go with, instead of a static factory, is have a static factory method on the WeaponConfig object so that it can create itself.
I have no problem refactoring the code to the state you've shown in the video, but then things get more complicated and the patterns start to feel insufficient. For example, what if the bow needs additional config variables that the sword doesn't need? What if the staff needs the same? What if I make variations to the staff that all take different elemental attributes and their configs? Soon enough, each weapon would need its own "WeaponConfig". Its not the end of the world, but different weapons would need to cast the given WeaponConfig to their own config type, e.g. StaffConfig, BowConfig. Maybe there is a better way to handle this? The biggest pitfall I encounter is that the inheritance hierarchy that made sense the first time I'm writing it starts to make less and less sense as the project gets more complicated, and refactoring an inheritance tree is a mess. I really liked the IWeaponState refactoring that allowed each weapon type to implement its own Down/Up/Update functionality and still be driven by the WeaponController. Because the bow would probably need to aim before shooting, etc. And if a weapon needs additional variables to function correctly (instead of the simple target we get from Use(Transform target)), they can collect it during these 3 states. As I was watching, I was wondering how you'd handle that, and the solution makes a lot of sense to me! I would love to see more videos like this as the project gets more complicated. Even though your example has great coverage, the game is not really 'playable' from the POV of a consumer. At the later stages, I feel like patterns become a hassle to maintain as well. Nonetheless, they are much better than the initial code you showed at the beginning.
There's always generics. For instance I'm working on an inventory system, and I decided to make the item config as a generic parameter in my item class. So something like *Item where TConfig : ItemConfig*. That way the definition is always of the type you want it to be. So you'd have Bow : Weapon, Sword : Weapon. And they all inherit from the contract, either IWeaponConfig or WeaponConfigBase. But generics aren't perfect because sometimes you want to use a Weapon as a parameter, but you can't because it requires that generic argument now. You can't make an equip function that takes in Weapon, or Weapon, it won't compile (which is understandable). Solution? Weapon should inherit an interface or a base class that has no generic argument. Now you're good to go. I know this is a mouthful but the takeaway is : if you're going generics, it's great, but make sure you have no generics at the top.
@@bodardr Yes, but as you said, functions still need to take the non-generic definition. How would your example work with the WeaponFactory shown in the video? The factory also needs to be generic then. Even so, i feel like there needs to be casting somewhere down the line. Same with the UI buttons which take in a List. How would that work? In my mind, if I need to cast the non-generic definition to one of the generic and concrete types, its not much different than casting at the weapon level. Maybe it does help, but Id need to sit down and try to code it to see. Nonetheless, I agree generics are great, especially for EventNotifier, Messaging etc patterns where by nature of the design you need to be able to work with any type of event, message, or payload.
How would I like it? Haha, well I have to do that a lot in my current work, and I can't say I like it too much. Nobody likes it, which is why it should be avoided in the first place. Of course, in the real world it does pay very well. But if you are asking me to consider looking at a specific project for a video, maybe before the next refactoring video I'll ask on Discord for some submissions.
Interface question??? I see in most of your videos you write an interface then never edit it. This vid. You edited the interface three times then had to update all the scripts that implement it. My questioni is...Isn't it bad practice to ever edit an interface?
Editing interfaces isn’t inherently bad practice, but it can lead to maintenance overhead as all implementing classes must be updated. If it’s your own code, it just requires some extra work. However, in published or shared code, changing an interface can break dependent systems, so be careful if you are doing that.
Happy Sunday! I really enjoy refactoring because IMO it's the best way to keep your programming skills sharp! I hope you do too! 👍
I don't love refactoring because I can't reliably feel the line between refactoring and overengineering
What I love, tho, is to receive notification about new git-amend video upload ❤
Regarding what you mentioned about technical debt at the beginning, many years ago, during my first project as a lead, I had the opportunity to work with a very experienced architect, guy in his sixties I guess he must be retired by now. Well, one day, we were discussing a feature we needed to implement, but the timeline was pretty tight.
I suggested we could do a basic, quick-and-dirty implementation and then "properly implement it later, after the release." He replied, "There's never a 'later'..."
It took me some time and a few challenging projects under my belt to truly understand why he was so strict about technical debt... But Craig, if you're reading this, you were absolutely right!
Hahaha, those are VERY wise words! Thanks for the comment!
Super cool type of vieeos. Just let viewers send messy code and refractor it. Best way to learn something
Thanks! More to come I'm sure!
like that you bring advanced things and videos like these. Excellent work as always 😃
Thank you!
This is really good! Nicely done.
I actually learned the factory pattern from one of your previous videos. Now I use it everywhere possible. It ties cleanly with an object pooling system and jobs and burst.
Please make more refactor videos like this one. And Happy Sunday!
Great to hear! Thanks for the comment!
This type of video is awesome .. not only will it encourage programmers not to start over "because I know better now" but also shows how to use all the important programming patterns you have already taught in previous videos. Great work :)
Hey there. I wanted to thank you from the bottom of my heart for all the amazing content you create. I'm a self-taught programmer and game dev, and after coding for 2 years, I hit a learning plateau. That is, until I discovered your channel. At first, I didn't fully grasp your videos, but I kept rewatching them over and over again, slowly refactoring my game's code. Nowadays, I feel like my code is on steroids, and it's all thanks to you
Thanks for the kind words, very motivating!
Absolutely love this refactoring video approach! It's so helpful to see the before and after, because I know so many of us work ourselves into corners and it can be quite hard to see a path out when you are too close to the project and have been looking at it from one perspective for too long. Thanks a lot for sharing these videos with us!
You are so welcome!
It's interesting to watch even though I knew the majority of concepts here, but there were some things that were new to me! It's a nice feeling to realize how much I've learned since I started. I really appreciate that you're giving good advice and habits right away despite being a bit more complex unlike many tutorials, that even I had to follow, which taught me some really awful habits that I had to spend a lot of time unlearning. It'll take time either way so better to take that time to learn than have to take the time later on unlearning and feeling unproductive because now you're kind of back to what feels like square one.
I agree 💯 - thanks for the great comment!
This is the type of youtube channel where u see ur first video and hit subscribe within 10 min
Haha nice!
Love that series :) My game code is so much better thanks to you !
Glad to hear it!
One of my favorite types of videos! I love seeing so many coding patterns in action. Some are still a bit tricky for me, but I was surprised to realize I’ve already used a few.
Do you code like this in your own projects, or do you use more "niche" tools like R3 or other libraries/assets? I'd be curious to see some of your older code!
Thanks for the comment! I actually don't use too many niche libraries - mostly it's just regular C# when I'm coding. Of course I do use my own systems, like the Service Locator, Event Bus, Improved Timers and the Unity Utility library that we keep adding to as well.
Beautifully explained
Thank you!
Thank you for the great video! I'll enjoy listening to it
You're welcome! Thank you!
Grest video as always. Cheers
Thank you!
amazing content as usual 10/10
Much appreciated
Awesome. will watch again. could apply this to a toy i am working on.
Nice! Glad you liked this one.
Had a lot of refactoring to do this week as well. I ended up moving the factory pattern to the scriptable object directly, so it doesn't use a factory instance, plus I can override it in the child classes. I wonder what's your opinion on that. As always, keep up the good work, your code is top notch!
Having a static factory method is a viable option, especially if you don't need to mock the factory for testing purposes. I do it fairly often actually. Thanks for the comment!
Yeah when I can I love going static. But I actually meant moving the object creation to the WeaponConfig as a public function. That way you can make it virtual and override it for child Config classes. You could for instance load the projectile prefab for BowConfig or StaffConfig, or any kind of additional stuff. But in your scenario it was perfect because it was simple enough to not be required at all, since there's only WeaponConfig class for now.
Nice video content mate! Where would we need enum in that case? Can we solve that differently, every time we add new item type, we also need to add enum, thats what i dont like about enums.
@git-amend Sensei! Great video! Great way to showcase refactoring workflow. I have two questions regarding your setup. 1. How are you getting those Hierarchy Folders? 2. When you're doing null checks in Rider, I noticed there's a little icon there. What is that for?
The folders are an Asset created by Sisus that is extremely useful, I'll add a link in the description of this video. Actually, I recommend (and own/use) all Assets made by that creator, especially InitArgs and Debug Extensions. The icon in Rider indicates an implicit check on Unity object lifetime, which is different from regular objects.
Could you elaborate what the point of the interface for the Weapon is, when you just load it into the abstract class anyways? You could remove the interface from your code and have the exact same functionality.
The interface ensures a clear contract for any weapon implementation. This approach comes from the "start from interfaces" principle, rooted in the Interface Segregation and Dependency Inversion principles of SOLID. While it may seem redundant with an abstract class, I almost always start refactoring classes that have things in common by first defining the interface they must all adhere to.
Teşekkürler.
Teşekkürler! Çok sağ ol, Süper için!
This feels like someone wanted help transitioning from stephen Hubbards arpg course to something more scalable....
Could be, I don't think I'm familiar with that one.
How do you draw the line between refactoring and over engineering?
Well, keep in mind that over engineering means adding features that were NOT requested. Refactoring is improving code so that you can facilitate changes that were requested. It's important to keep this in mind so you don't build things that are unnecessary, while still building the things that you do need in the best way you can for your skill level.
8:29 Would it be acceptable to make WeaponFactory and its methods static?
As it stands right now, you certainly could. However, one of the nice things about a Factory or Abstract Factory is it's use in testing - if you added an interface, it would be very easy to add a test version of the factory and just produce exactly the kinds of output that you need to executing test logic. We don't necessarily need it in this simple example, but injecting a factory is an extremely common practice. Another option you could go with, instead of a static factory, is have a static factory method on the WeaponConfig object so that it can create itself.
I have no problem refactoring the code to the state you've shown in the video, but then things get more complicated and the patterns start to feel insufficient.
For example, what if the bow needs additional config variables that the sword doesn't need? What if the staff needs the same? What if I make variations to the staff that all take different elemental attributes and their configs? Soon enough, each weapon would need its own "WeaponConfig". Its not the end of the world, but different weapons would need to cast the given WeaponConfig to their own config type, e.g. StaffConfig, BowConfig. Maybe there is a better way to handle this?
The biggest pitfall I encounter is that the inheritance hierarchy that made sense the first time I'm writing it starts to make less and less sense as the project gets more complicated, and refactoring an inheritance tree is a mess.
I really liked the IWeaponState refactoring that allowed each weapon type to implement its own Down/Up/Update functionality and still be driven by the WeaponController. Because the bow would probably need to aim before shooting, etc. And if a weapon needs additional variables to function correctly (instead of the simple target we get from Use(Transform target)), they can collect it during these 3 states. As I was watching, I was wondering how you'd handle that, and the solution makes a lot of sense to me!
I would love to see more videos like this as the project gets more complicated. Even though your example has great coverage, the game is not really 'playable' from the POV of a consumer. At the later stages, I feel like patterns become a hassle to maintain as well. Nonetheless, they are much better than the initial code you showed at the beginning.
Thanks for the comment. I'm sure we'll look at some more challenging scenarios in the future, maybe some works in progress from Discord.
There's always generics. For instance I'm working on an inventory system, and I decided to make the item config as a generic parameter in my item class.
So something like *Item where TConfig : ItemConfig*. That way the definition is always of the type you want it to be. So you'd have Bow : Weapon, Sword : Weapon. And they all inherit from the contract, either IWeaponConfig or WeaponConfigBase.
But generics aren't perfect because sometimes you want to use a Weapon as a parameter, but you can't because it requires that generic argument now. You can't make an equip function that takes in Weapon, or Weapon, it won't compile (which is understandable). Solution? Weapon should inherit an interface or a base class that has no generic argument. Now you're good to go.
I know this is a mouthful but the takeaway is : if you're going generics, it's great, but make sure you have no generics at the top.
@@bodardr Great advice. Maybe a good topic for a future video as well.
@@bodardr Yes, but as you said, functions still need to take the non-generic definition. How would your example work with the WeaponFactory shown in the video? The factory also needs to be generic then. Even so, i feel like there needs to be casting somewhere down the line. Same with the UI buttons which take in a List. How would that work? In my mind, if I need to cast the non-generic definition to one of the generic and concrete types, its not much different than casting at the weapon level. Maybe it does help, but Id need to sit down and try to code it to see. Nonetheless, I agree generics are great, especially for EventNotifier, Messaging etc patterns where by nature of the design you need to be able to work with any type of event, message, or payload.
Nice! Only 50k more views.
So close!
for the algo!
Nice thank you!
帅
谢谢支持!😊
How would you like taking a look into a project that already became a mountain of tech debt?
How would I like it? Haha, well I have to do that a lot in my current work, and I can't say I like it too much. Nobody likes it, which is why it should be avoided in the first place. Of course, in the real world it does pay very well. But if you are asking me to consider looking at a specific project for a video, maybe before the next refactoring video I'll ask on Discord for some submissions.
Thanks a lot can you upload 2K or 4k please :), High quality videos deserve higher resolution.
I'll see what I can do!
Interface question??? I see in most of your videos you write an interface then never edit it. This vid. You edited the interface three times then had to update all the scripts that implement it. My questioni is...Isn't it bad practice to ever edit an interface?
Editing interfaces isn’t inherently bad practice, but it can lead to maintenance overhead as all implementing classes must be updated. If it’s your own code, it just requires some extra work. However, in published or shared code, changing an interface can break dependent systems, so be careful if you are doing that.