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
289. Common Code to Build and Reuse in Business
Use Left/Right to seek, Home/End to jump to start or end. Hold shift to jump forward or backward.
What code should I build into a library for reuse? Are there specific tools or libraries I should build to save myself time at work? How should I reuse code at work? 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/
What code should I build into a library for reuse? Are there specific tools or libraries I should build to save myself time at work? How should I reuse code at work? Let's talk about how to reuse code at work as well as what code to consider building into a common library in this episode of DevQuestions.
SPEAKER_00Welcome to the DevQuesti podcast with Tim Corey.
SPEAKER_01Software development is more than just writing code. So let's talk about the rest of it. Specifically, let's talk about code reuse at work and how to build a common development library. Learning to code and writing code at work are two different things. When you're learning to code, you're learning about design patterns and algorithms and perfect architecture and the latest version of everything. When you're building code in production, you're typically working on a less than ideal code base that's years out of date, is messy, and is generally a dumpster fire that you're trying to keep from bursting out of containment. But there are things you can do to help alleviate some of the issues. One of the big things is to develop a series of shared libraries that you can use to reduce the amount of code you have to write and debug. Now, on the other hand, one of the things you can do to make that dumpster fire worse is to create the wrong set of code and shared libraries that actually increases the amount of work you have to write and debug. So let's talk about how to go about this safely and effectively. So number one is use NuGet packages. It used to be when we shared code between projects, we'd create a DLL and we just pass that DLL around, or even worse yet, put it in the global assembly cache, which I don't believe is a thing anymore. Um, but that's a bad thing, really. It's a messy way of sharing code. Don't do that. Use NuGet packages. One of the benefits is that with NuGet packages, you can have different applications using different versions of the same package. That way, let's say you have 10 different projects that use your library, your you know, Acme library. So maybe all those packages use you know Acme Lib version one. And then you make a change to Acme Lib that maybe it's going to add a new feature. And then you you upgrade the first in of your 10 projects to use Acme Lib 2.0. Well, you can upgrade just that one project if it needs Acme Lib 2.0 and leave the rest alone. You don't have to touch and recompile and redeploy and all the rest, those other applications. So using versioning is a really big deal. It also helps you when you go build your application, say, hey, we did a bug fix in Acme 1.1 that fixed a critical error. Does this application use 1.1 or higher? You can go look at that and see, oh yes, it uses package version 1.2. Or you can say, oh, nope, it uses 1.0, we need to upgrade that package. Whereas if using DLLs, you're trying to go to the properties and look and it's messy, and it's there's caching and there's all these other problems surrounding it. Make sure you use NuGet packages internally. Number two, keep libraries small and focused. I have worked as a consultant for a number of years. I've dealt with a lot of different companies, and by and large, a lot of them use some type of shared library. But I've seen some that's it's one library and it's everything. It's a junk drawer. It's everything that they ever wanted to do is in that one library. The problem is if they find a bug, well, then they in one little piece of that, they have to upgrade the hopefully NuGet package. I've dealt with a lot of DLLs, but they open, they upload a new version, but now everything has to update to that new version because it was a bug fix. It was a security issue. But it was a security issue in one little piece that not every application uses, but we don't know which applications use that, which ones don't. So we have to update every application. Before you know it, anytime you change that library, you're changing every single application that depends on it. So instead of being a separation of concerns and separating out your different libraries, you're really building one big monolith that's badly distributed. Don't do that. Create small focused libraries. Say, this is the Acme library for communication or for this or for that, and have different ones that you bring in. That way you know what your application depends on. That way you know which applications need to get updated when a bug fix or a security patch goes out for those packages. Number three, don't build things you don't need. The idea of a shared library excites people and they start dreaming. Oh, you know what? I could add this to it, I could add that to it. Before you know it, they've built this massive library of all these things that they're never going to use. Where maybe they're going to use it someday. Someday we'll use that. And someday you might. The problem is that maybe you start using it two years after it's built. And all of a sudden you find out that that had a bug for two years. You just didn't know it because you didn't use it. But now you don't remember how you built it or who built it because that person's already gone from the company. That's a problem. Don't build things you don't need, don't add complexity. Number four is don't put things in a library simply because they might also be used somewhere else. I see this a lot. We're going to talk about, I'm going to step on some toes later on. Um, there's something down here that you probably do. Microsoft has probably shown you that you should do that I would recommend you don't do. Because just saying, well, we're using it in two places, we need to have it in a library. Not a good answer. Not a good reason, because that can cause some significant problems that make your applications worse. So don't put things in libraries just because you're going to use it in two places or three places. You need to think through is this right for a library? And the answer isn't just yes, because we use it somewhere else. So now let's talk about what to build. To do that, I'm going to give you three examples of things that I've put into shared libraries. I want you to look for the common thread. Number one, I have built a data access library that I use a lot. So I use Dapper for my data access almost exclusively. I think that it provides a much better solution for data access than NT Framework or other ORMs. Not because it's more efficient, although it can be, it usually is, not because it is simpler code, although it can be in some cases, but because of the fact that it allows me to put my SQL in SQL and allows SQL to do what it does best while I do what I do best in C sharp, the C sharp stuff that C sharp does best in C sharp. So I keep both separated and make sure that each does what it does best. So I use Dapr. Well, when you do Dapr, it takes about five to eight lines of code to set up and initialize a connection to read data from SQL and somewhere on the same for writing data to SQL. Well, I have a couple of methods that make that into one-liners, where you give one line of code and say, hey, I want to read from this store procedure and I want to put it into this model. Done. One line. Well, that's something that I reuse in a lot of different places. So I put that into a shared library. That's number one. Number two is when I do communication. So sending out emails or sending out texts. Usually, let's use email as an example. I have a method that has uh, you know, I use SMTP, that's SMTP library from Microsoft, usually, I believe. Um, it will be I have to have the the header or the um the two and the CC and the BCC and the subject line, the body, and if you have text body versus HTML body, and you have a bunch of other things. And there's a lot of setup with that, make sure you have all those parts in place and error checking and all the rest. I wrap all that up so that again, it's one line where I say, here's my model of the data for an email. I skip the model and say, send that email. Done. Okay. So my library handles the taking the model and saying, okay, we're going to fill in all this information this way, make sure we have all the things checked, and we're going to validate that model, et cetera. And they're going to go through the process of actually sending that email. So a communication library. Number three is a standard logging configuration. So I do logging in a similar way for most of my projects, if not all of them, where I have a certain setup where I say, okay, these are the things I want to do. And I do it in code as opposed to app settings for the most part. Now there's some things like, what's your logging level? That goes into app settings. But there's other things like we're going to use a SQL logger, we're going to have have these settings, et cetera, that you can do in code. I prefer to do it in code. So I'll put that into a library and we'll have a standard logging configuration. Because while some settings for logging, like again, logging level, might be different per application, the overall configuration of the loggers are going to be the same. So I'll put that into a NuGet package. Okay, so those are three examples of the shared libraries that I have built. So what's the common thread? What do these things have in common? I'm taking something I do everywhere or in a lot of places and making it even simpler for my use case. So NuGet packages, let's say for Dapper, it's a pretty simple thing to do to have a connection to SQL from your configuration and take that and have a using statement that says, okay, um using this connection to SQL and then say, okay, let's read the data, let's put it into the model, let's turn that model into a list and send that list back. That's a pretty simple thing to do, but that's still quite uh that's still quite a few steps where I take it and say, this is how I use that Dapper library. And I turn it into one step. So I'm not doing all the work in the library. I'm just eliminating the simple or the common setup work. I'm trying to make it as simple as possible for me to call those resources or use those resources. So those are the things I do when I look at building out libraries. That's how I build them out. I build them out to make my life as simple as possible by making sure that the common things I'm doing over and over that are going to be the same, I'm going to put into a library. And I'll just call that with a little bit of configuration that is going to be different between every project. Now, here's what to avoid. And this is where, again, I'm going to step on some toes. The first thing, and the thing that's going to be the most controversial, I would recommend do you avoid putting models in libraries. I can already hear the, no, no, no, that's wrong. You need to put models in libraries. Yes, I know that's what you think. But here's the deal: the model that you use to access your SQL database should not be the exact same as the model you use to display that data on your UI. Almost certainly, and even if it is at first, it won't be forever. Why? Well, because you're going to put validation logic on your UI model, because it's actually a model for inputting data as well as displaying data. Whereas your data access model, your DTO, that's not going to have data validation because it's coming from the database. And there's other things like that. And then people get caught up in the idea of okay, I've got a table that has ID, first name, last name, and email address. So I need to have a model that's ID, first name, last name, and email address. That may be true for accessing that data, but it might not be true for displaying that data. And it might not even be true for accessing the data. Imagine for a minute, I'm going to create a list for a dropdown. And I want to show the user a list of potential people to select from. And so I'm going to pull from that person table. Well, the dropdown is going to need the ID in order to look that person up. Okay. And that's going to be the name of the person, probably, maybe last name, comma, first name. Cool. Does it need the email address? No. So guess what? I'm going to either, if I use that model as it is, I'll be bringing down extra information from the server, potentially secured information that shouldn't be displayed. Extra information from the server, I'm going to pull it from the database, transmit it across the wire to the client. The client's going to have to hold that in memory for no reason. And I say, well, but I'm saving time by not having to have three models. You're saving a little bit of design time in order to have a worse experience every time that page is called. That's not an optimization, that's a de-optimization. That's making things worse. Now, there are times when you might put a model in your library. For instance, that example I used of sending email. That would have a model in there because this is the standard I'm expecting for sending an email. It's going to have the two, the CC, the BCC, the subject line, the body, et cetera. But I would not, in most cases, put your models into shared libraries. I don't like shared projects for those things. Because again, you might have a case where you say, well, I want to modify this for the front end, but if I do that, then it's going to affect the back end too. Now you have this one big model if you don't have separation of concerns. And now you're saying, uh-oh, well, maybe I can modify, have a different model. And before you know it, you cause all these kinds of problems, trying to do lowest common denominator, where you're transmitting too much information across the wire, you're capturing too much information, you're passing on models that are half empty. It just creates a mess because you're trying to somehow save cycles. It does not hurt to have two different models that have the same or similar properties. You're not going to cause your application to be too big or something like that. This may actually save your application in the long run. So I would avoid putting models into your libraries. Again, that's a general principle, not an absolute. Next up, I would avoid a lot of business logic. Typically, business logic is specific to an application or specific to a certain situation. So if you put business logic into a shared library, that might cause problems because you might have to update that library all the time. And again, you might have to tweak it for one project, not another project. And so now you have two different versions of that same logic, but now you're trying to maintain that in one package or something weird like that. I would avoid that. I don't think that that typically rises to the reuse across the board situation. And finally, most UI components. Most UI components probably should not be in a shared library. I know it's tempting. You say, well, you know what? We're going to create this login form. We're going to use this login form everywhere. Let's put that in a shared component. Maybe. But here's the problem. You're going to have one place where it's not going to fit real well, where you're like, ooh, I kind of want to do something else. Or, you know what, we don't have the same amount of space we do everywhere else. You have to make this a little bit smaller. Now what you're trying to do is change a component for one spot that's not changed the other spots. And then you start creating that, like modifying that component to maybe be a little bit more flexible so it handles that one situation versus all the others. Before you know it, you have a situation where your shared component is just a whole bunch of configuration you're passing in. And you really should have just created that component more than one time and maybe put a copy somewhere that you just copy and paste from. Copy and paste is not the end of the world. There are times when it makes sense as opposed to a shared library that handcuffs you between projects. So those are some places to avoid creating shared content in a shared library. Shared libraries can be powerful tools to make your development faster. However, don't get so over-eager to centralize things that you actually make life harder for yourself in the process. All right. Thanks for listening. And as always, I am Tim Corey.
SPEAKER_00Thank you for joining us for this episode of Dev Questions. When you're ready to learn to think and code like a professional developer, head over to Iamtimcorey.com and enroll in a course.