DevQuestions with Tim Corey
I am Tim Corey. I teach coding online and my inbox is full of questions from current and future developers. I wish I could sit down with each of you and share the answers I know will help you go further, faster in the world of development. This podcast is the next best thing. On this podcast, I will answer the biggest questions people are asking! Send us to your suggestion for a future Dev Questions at https://suggestions.iamtimcorey.com/ To keep the podcast coming, like, subscribe, rate, and share it with your friends and colleagues. See why thousands of students have chosen to learn to think and code like a professional developer at www.DevForge.com.
DevQuestions with Tim Corey
211. When To Break The Rules as a Developer
Use Left/Right to seek, Home/End to jump to start or end. Hold shift to jump forward or backward.
When do I break the rules as a developer? How do I know when I should implement a pattern or intentionally ignore it? Should I ever violate SOLID or DRY? These are the questions we will answer in today's episode of Dev Questions.
Website: https://www.DevForge.com/
Ask Your Question: https://suggestions.iamtimcorey.com/
Sign Up to Get More Great Developer Content in Your Inbox: https://signup.iamtimcorey.com/
Welcome to the Dev Questions Podcast with Tim Corey. Join us each episode as we tackle the questions you are asking about a career in software development, understanding the industry, and new technology. If you're just starting out or you want to grow stronger as a developer, this is the place to get your questions answered. Now, here's your host, expert developer and online educator Tim Corey.
SPEAKER_02As you learn software development, you'll start to pick up rules and systems like dry, don't repeat yourself, or the solid principles, or any number of design patterns. Things like implementing dependency injection and logging also become fairly standard rules. So when is it okay to break the rules? Are there times when developers should ignore the best practices? Or are best practices too important to ignore? These are the questions we're going to tackle in today's episode of DevQutions. So is it okay to break the rules? The answer is yes, which probably shocks you that I didn't say it depends. But here's the deal: the key is when. And the answer to that is it depends. Okay, so it's yes, it's okay to break the rules, but when is it depends? Okay, so let's talk about when to break the rules. Now, normally I give you a list of answers or a list of ideas, but in this case, I'm gonna give you a clear, simple principle to help guide you. And then we'll talk about some examples to help show you that principle. So the principle is to ask the question does this rule make my life simpler? Now note that I did not say your application, I said your life. I also didn't say better, I said simpler. So let's talk about making your life simpler. So, for example, that might mean making your application easier to maintain. For instance, a single file. A single file is super easy to maintain as compared to having files all over the place. That's easier to maintain. Not just for you, again, for your team. Another example might be making your code easier to read through. So less navigating to get to the logic. You may have seen applications where you have a method, but that method calls another method, which you go hunt that down, that calls another method, which calls another method, and you're like going 10, 12, 15 layers deep to find out what that code is, or part of that code that the original call was talking about. And you have to keep all that in your head. That's a lot to navigate through compared to having the code in one spot, in one method. So making your code easier to read through is another way. Now, also it might mean making your code more simplistic. For example, easier for others to maintain. Sometimes you might know how to, for example, if you're trying to do a comparison to see, hey, does this word, is this word matched? Okay, so maybe you're looking for a specific word and you say, I can write this really complicated regular expression, a regex, in order to identify all the possibilities. That's complicated, but it will work. But how do you debug that in case you realize it's not quite right? Well, you have to figure out what each bit of the regex is doing and so on. Or you could maybe make that simpler by saying, if it's this or if it's this, then it matches. Well, that's a really simple way to identify because it's it's easier to read, which means maybe more people could maintain it rather than just the few who know regular expressions well. So far it sounds like what I'm advocating for is a monolith application with all the code in one file. But remember, I didn't say to put all your code one file. I said, does the rule make my life simpler? Have you ever opened a code file that has 2,000 lines of code? I have quite a bit, unfortunately. When you do, is that simpler? Well, no, because you've got all this mess to kind of sort through and figure out what's going on. And it's, you know, trying to track it down. And if you find out, you know, there's a code bit that may not be working, you have to figure out is that code repeated somewhere else? And because it probably is. And so then you have to figure out all those places, make sure you get them all, and they might be a little bit different in each spot. So that's not simpler, but that's the balance that you have to hit when you're looking at your code and following rules or breaking rules, is looking at that simplicity. Sometimes simplicity means multiple files. Sometimes simplicity means having some abstractions. Sometimes simplicity means that you have multiple projects, but there's that balance of does it make it simpler or does it make it more complex? What I'm doing. So let's talk through some examples because I think this will help kind of drive this home. So, first example, let's say you have a person model. Okay, the person model has a first name and a last name. It's all it has in there, right? And you use this person model in your data access class where you pull the first and last name from a database table somewhere. Okay, but you also use that person model when you're capturing first and last name from a form on your web application, let's say. So you could put that model in a shared class library that both projects could could access. That makes it simpler. Because then you only have the model in one place, dry, don't repeat yourself. So therefore both can access it, right? Well, that in theory fulfills the principle of dry. But does it make your life truly simpler? Well, let's evaluate. There's a shared project we have to maintain now. Okay? A third project, potentially at least, but a third project, does it make it much more difficult? Probably not, but there is a third project to maintain. But if I decide to add validation on the model, where I say, you know, please enter your first the first name or please make sure the first name is at least three characters, you know, in an error message, and I put that as part of the validation logic on the model, I'm putting UI-specific language on a model that the database uses. Or maybe another user interface uses that has different UI-specific language. Now I've got some complexity here. Do I have some kind of gating mechanism in my validation logic that says, well, if you're being called from WPF, then do this, you're being called from Blazor, do that. That seems more complex, right? So now you've got to say, is it making it simpler or not? Also, there's now a coupling between your UI and your class library. Both have to be recompiled if a shared library changes. So is that third library the better option? Because that's kind of the rule, right? The rule is you create a shared library. But sometimes the answer is no, you don't. But I'm not saying never do that. I'm saying that's that's the rule because it happens a lot, because that works in a lot of cases. But it doesn't work in every case. So you have to figure out when's it time to break that rule? Well, when it makes your code or potentially makes your code more complicated, when it makes your setup more complicated. Sometimes the easier solution is to have two models, one in the UI, one in the class library, that both have the properties first name and last name. I mean, that's not hard, right? Like copying and pasting, that's not real hard. You may say, well, Tim, yeah, but we copy and paste code, we're violating dry. Kind of. But when it comes to UI models versus you know DTOs or data access models, they're they can deviate, they can be different. UI models can have different validation. So all of a sudden they're not repeating themselves, they're different. Plus, the fact you're not doing logic here, you're just doing properties. There's a difference. So you have to think through which do I do? Is it easier just to copy and paste and have one less project to maintain, a less coupling, and potentially save myself an issue of having UI logic in the data access library? Or is it better to have that shared project? Again, you get to choose based upon what is simpler for your situation. Number two, example, let's say you're building an Azure function. And that Azure function is going to send an email to a user when it's triggered. Okay? Real simple. Dependency injection is a valuable tool that makes code easier to maintain and more loosely coupled. But does it make my life simpler in this case? Okay, so the answer is it depends. And you have to evaluate if dependency injection is the right choice or not. For example, most Azure functions really should be with 100 lines of code or less. I say should be because we all know we violate these rules. But that's the purpose of an Azure function is to be tiny, to be small, to be something that runs very, very quickly. Well, in 100 lines of code, do I need to have dependency injection? Or is it okay if I have my dependencies in the code? Because part of dependency injection is to you kind of surface your dependencies and say, these are the things I depend on. But when you have just 100 lines of code, you can see it really easily. Also, depend uh Azure functions most often are accomplished in one method. So you have one method that has dependencies. Maybe you don't need it. If you don't have it, you don't have to do a setup of dependency injection. You don't have to do the injection, all the rest of the stuff. You don't have to have that system running in your function every time it starts up. It can be much quicker, much more efficient if you don't have all that additional startup overhead. And it can make sense to just have inline dependencies with the new instantiation right in line. So even that, it depends because what is simpler in Azure Function might not be simpler in a larger application. So again, you can look at the quote unquote rule of use depending dependency injection because it makes your life easier, and say, well, yes, generally it does, which is why it's generally accepted. But there are specific times when it doesn't make my life simpler. Therefore, I don't use it in those times. Another example: design patterns. Design patterns are the buzzword of software development. But what they are is the result of real-world experience in building applications. People have built applications and said, hey, we see this thing as being useful, this pattern. And so they've codified these into named patterns that we say, hey, this pattern is helpful here, and this pattern is helpful there. But if I'm building a console application to automate a process, should I implement all the design patterns? Well, that sounds silly, right, when you put it that way. But does that mean I don't need any patterns? The answer is that the principle we are talking about, does the particular pattern make my life simpler? So maybe the facade pattern can be used to wrap an API so you that your application needs a call. There's a lot of APIs that are really squirrely in how they work and they even change often. But maybe you create a wrapper around that to make it easier to just say, you know, do work, do work, and it just does the work. You don't have to worry about how to implement a specific API. You have the facade that wraps around it that makes your life easier. That could be a good thing. Or maybe use a factory pattern to generate the objects your application needs to do the work. So maybe you generate different objects to do different work, and you can use the factory pattern to make that easier. So there's times when those patterns might apply in your application. But that doesn't mean you need, for example, CQRS or those kinds of things. You don't need everything that patterns provide. You can identify the ones that make your application simpler. Number four, and the last example I'll use today is let's say you want to build a complex application. Let's say you're building, I don't know, a student information system, and you decide that you're going to map this whole thing out. Do you build a monolith or do you build a microservice-based system? And I hear this get thrown out a lot because I've heard people that are very much set on I know which way is better. And often I hear microservice-based system. So let's talk about this because it does depend and you need to figure out what's going to make your life simpler. So a monolith will be easy to read through and it's gonna be more simplistic. If your UI needs something, then it makes a method call and gets it. A microservice-based system will have multiple easy to maintain applications that are easy to update. So that's a benefit for them. Now, the downside of a monolith is that it's harder to update and it can be more complex internally. So, for example, if you're gonna update a part of a monolith, well, you have to take the whole thing down in order to put the new code in because it's a monolith, it's it's one block. The downside of microservices are that they're gonna have more connections and more systems to maintain, and they're more complex in their connections between services. So, on one hand, you've got a monolith that you know has some upsides and downsides, but microservices have upsides and downsides. So, what's right for you? You choose the one that is simpler for you and for your life. So, in the end, often monoliths are the simpler option. You have to deal with one repo, one database, one deployment, and one code base. And you get what you want through method calls. That's pretty simple. Now, the only time that I see microservices becoming simpler than that is when your monolith is extremely complicated and hard to manage. So, in that case, sometimes a microservice-based architecture could relieve some of that complexity. But often, the simpler option is to make your monolith better, because you write good code or bad code in either scenario. So, write better code for your monolith sometimes saves you from needing to go with a microservice solution. The key thing here is you're looking at adding complexity with a design pattern or with any kind of rule or or principle, you're most likely going to add complexity. But is that add complexity making your life simpler? Is it making it easier for you to debug? Is it making it easier for you to update? Is it making it easier for you to maintain? Is it making it easier for you to give this to somebody else to do? These are all things to evaluate when you're looking at should I keep a rule or break a rule? Do not just follow rules blindly. Do not. My best piece of advice for you is know why you're making every decision you do. Not just because Tim told you, not just because some other random person on the internet says the way to do it, not just because Microsoft said do this. Know why you're doing it. All right. That's going to help you identify the simpler option. Now, as a software developer, you need to know when to break the rules. If you don't, you're gonna make some poor decisions. Knowing when to break the rules only comes through experience. And that's why practice is so important. You make the mistakes on projects that don't matter rather than in production systems where the mistakes can sometimes not be easily undone. I've seen too many people think that design patterns are requirements and they put them in and make a system complex. And it's too late if it gets into production sometimes, because it's very, very hard to unwind all of that when the system is actively running. So I would encourage you to practice. Practice will help you figure out these things. Figure out when breaking your rule is actually simpler in the long run. Just make sure you don't think that simple means just for today. We're talking about long term, not just even for you, but in general, is it simpler? But does it make your life simpler? Okay. So there's a lot to evaluate. Yes, rules are there because generally they are the better option. Generally, if you understand what the rule actually means, they apply. And generally, they will make your application simpler. But it's only when you first understand the rule itself and why it even exists, because a lot of design patterns don't exist for every situation. And they'll say that. But then even knowing that, the next thing is knowing your application and knowing if that change will make things simpler or not in the long term. Okay? So it takes a lot of work, it takes a lot of practice, but rules are meant to be broken in software development. That's why you call them patterns or principles. They're not hard and fast rules, they're not laws. Okay? You can break them if it makes sense, but just understand when it makes sense. Right? Thanks for watching and thanks for listening. And as always, I am Tim Corey.
SPEAKER_01Thank you for joining us for this episode of Dev Questions. Tim is committed to making it easier for you to become a developer. If you would like to help make more content like this possible, please like, subscribe, rate, and share Dev Questions. You can also send your questions to questions at IamTimcorey.com. Until next time, remember, you are too smart and your time too valuable to waste it making all the mistakes Tim did. When you're ready to learn to think and code like a professional developer, head over to IamtimCore.com and enroll in a course.