Refactoring Based on Code Responsibility
Published 4/20/2016
In today's episode, I give you some tips for refactoring based on the responsibility of your code.
Mentioned or relevant to today's episode
- Single Responsibility Principle
- Developer Tea Episode on SRP
- Sorting algorithms (note the single-letter variable names)
- "Anti-pattern: The God Object"
- And the long awaited announcement: Developer Tea is available on Google Play!
Today's episode is sponsored by FreshBooks! Get paid faster, and get control of your business cash flow. Head over to FreshBooks.com/DeveloperTea today to get started with a free month. Don't forget to enter “Developer Tea” in the “how Did you Hear About Us?” Section when you sign up. Thanks again to FreshBooks for sponsoring the show!
Transcript (Generated by OpenAI Whisper)
Welcome to Developer Tea, my name is Jonathan Cutrell, and in today's episode I'm going to be talking to you about refactoring tips. Specifically, we're going to be talking about refactoring tips for determining responsibility. Today's episode is sponsored by FreshBooks. FreshBooks is the ridiculously easy to use online accounting software designed to help creative entrepreneurs get organized, save time, and get paid faster. We will talk more about what FreshBooks has to offer to Developer Tea listeners later on in today's episode. But first, I want to talk about these refactoring tips. And we're going to frame this through the lens of different places where responsibility of code can go haywire. Code should call out what it is responsible for. And I have four times when the responsibility of code can be basically impossible to determine or incredibly difficult to determine. And really, here's why it's important. If a new developer comes to the project, let's say the developer has never seen the code for that particular project, and they come to the project and they realize that they need to work on this one particular piece of code. But it's impossible to determine what pieces are doing what. Now this episode is certainly inspired by a recent example at my work. I was working on some code and it was difficult to determine exactly what that code was doing. And I couldn't determine what was responsible for what was happening in the interface. I was working on some JavaScript. And so I want to point out a few things that can fix this problem before it occurs in your code. This is a huge maintainability issue because if I come to the code as a new developer and I don't know what that code is responsible for, I could introduce brand new bugs that you never necessarily saw coming because that code may be responsible for something that's totally outside of the context of where that code shows up. So let's talk about these four times when code can go haywire, when the responsibility of your code can go haywire. And the first one, this shouldn't surprise you, is poor variable naming. And when I say variable, I mean poor function naming, poor class naming, poor variable naming, of course, poor consonant, anything that you name as the developer, your responsibility is incredibly high here, incredibly high. There is a huge amount of talent that is necessary to be able to name things properly. Now, we've talked about naming being a difficult problem on the show and it can't be stated enough. Learning how to label things well is absolutely essential to writing good, maintainable code. You can't write good code without proper labels. Code is really doing two things, right? This is why labels are important. Code is really doing two things. It's processing something, whether that is input, maybe it's some data that exists elsewhere, maybe it is just time, for example. There's something that it's processing, right? It's doing something. And it's explaining to the developer how that processing is occurring, okay? So it's a two-way communication. It's communicating with the computer and it's communicating with a user, in this case, the developer. Of course, code can do more than that. For example, it can model behavior and characteristics. But ultimately, what code does is it creates some set of pathways or actions and it provides a way for humans to read and manipulate those pathways. So when we talk about responsibility, it's incredibly important to name things well so that the responsibility of that particular reference, that particular object, is determined easily by someone new coming into the project. If you labeled something one letter variable name, for example, this is a really common thing that you'll see in a practical tip. For those of you who are writing code today and are looking for a very simple practical tip, stop using one letter variable names. The reason is it's kind of difficult to know what that one letter is actually implying, what it refers to. We see these all the time when we look at generic algorithms. And that's probably the reason why we use them in our code. We may also think that it makes our code a little bit smaller and therefore a little bit more efficient. And this is true to a certain extent, but not to a reasonable extent. In other words, you're going to get much more out of naming your variable a few letters longer, just a few bytes more. You're going to get so much more value out of that than you gain by keeping your variable names short. So go ahead and name it something significantly more descriptive. Most of the time, any of the issues that you would have experienced as a result of that name being too long will be resolved by either a compiler or a compressor or a minifier. It typically is going to be resolved. And most of the time, it's not even that much of an overhead anyway. So go ahead and name your things much more descriptively. But imagine that you come into a project and you have something that is named really broadly or really nondescriptively. Well how do you find out what that thing is doing? You have to go in and read every single line of code and determine, okay, what is this particular function, this FU function that I'm seeing? What is it actually performing? What are the effects? What are the side effects? What are the inputs and outputs? What should I expect from this code? From you as the programmer, you have the opportunity to point programmers in the future in the right direction if you name things well, okay, don't miss this. You have the opportunity as a developer to point future developers. That includes you by the way. If you come back to the project after a few years, I promise you you're not going to remember what that abstractly named function is supposedly doing. You have the opportunity to point those developers in the right direction if you simply name things well, okay, so that's number one. Number one, poor variable naming can cause a massive problem in your code. Name things well and you can help your future self, other developers, you can help them out significantly. Number two is what I like to call tag-along code. Tag-along code. Go open a piece of code you wrote in a hurry and you're likely to see this poor practice. You have a really well written function that runs and it does a few things. Maybe let's say you're writing JavaScript and it creates some HTML to inject into your page. Everything is looking great, but then you need to add one little unrelated thing to the page. You could go and create another function and create tests for that function and you could rename everything and go through that whole process or in your hurry, what is more likely, is that you added that little bit of code to the end of that function. You've tagged it on as if it was a part of that function. Now the problem with this is related to the first issue and that is even if your function is well-named, in this scenario you're doing something that doesn't necessarily fit inside of whatever that function was initially intended to do. Now you've created basically the same problem that you had in the first point, which is the name suddenly is invalid. You could possibly rename that function, Foo and Bar. Whatever Foo is and whatever Bar is, hopefully you're not using those names specifically, but maybe you're creating HTML. You could name it Create HTML if it was named that initially and then you added something else like Add a Class to the Header, Create HTML and Add Class to Header, but now you run into issues of that code just being terrible to look at and your responsibilities are getting mixed up with each other. This is the problem that Tag Along code can create. You're much better off if you go ahead and take that code out of that function and create a new function or a method. Whatever you need to do, create a new container with a new name for that particular job. Okay, so tips one and two or times when your code will go, hey, wire number one was when you name things poorly and number two is when you add Tag Along code. Now I have two more for you, but first I want to talk about today's sponsor, FreshBooks. This is FreshBooks first episode of Developer Tea. So thank you so much, FreshBooks, for joining it, spec and sponsoring Developer Tea. FreshBooks is the ridiculously easy to use online accounting software designed to help creative entrepreneurs get organized, save time and get paid faster. How do you get paid faster? Well, your clients can pay you online. You create your invoice. It literally takes about 30 seconds and your clients can pay you online. This is so much faster than them trying to send you a check or whatever it is that your clients normally do, paying online is just a few clicks away, right? If a client forgets to pay you on time, FreshBooks handles the awkwardness of reminding them. You can send customizable late payment reminders automatically through FreshBooks so you don't have to give them a call and ask them to pay you since they're late. On top of this, FreshBooks has a mobile app which will let you take pictures of your receipts and then it organizes them for you to go through later. Beyond that, FreshBooks can also automatically import expenses directly from your bank account. So you have no excuse when you're tracking expenses anymore. It makes it so easy to just go and get those expenses directly either from the pictures you take of your receipts or directly from your bank account. FreshBooks will also handle your time tracking. So when it comes time to create that invoice, you'll know what you did and when you did it. All the little details about cash flow are kept in one place. So FreshBooks knows exactly what invoices you sent. When you sent them, who's paid you and perhaps most importantly, who owes you what? So to claim your free month, this is what FreshBooks is giving developers you listeners. A free month on FreshBooks, go to FreshBooks.com slash Developer Tea and enter Developer Teain the how did you hear about us section when you sign up. If you've been putting off your bookkeeping, this is tax season. If you've been putting it off, then today is the day to start getting that stuff cleaned up. Go to FreshBooks.com slash Developer Tea. Of course, that link can be found in the shownotes at spec.fm. Thanks again to FreshBooks for sponsoring Developer Tea. Okay, so we've been talking about refactoring and we're talking about determining the responsibility of different parts of your code and I'm outlining a few times when your code may go haywire where it becomes incredibly difficult to determine the responsibility of a particular piece of code. Now, hopefully this will help you avoid that situation in the future because the refactoring process when things are poorly named, for example, is very difficult. You have to go back and forth with whatever the product is, that front end and the back end to determine whatever it is that this particular method or function is trying to do. It's really hard, it's really expensive and it's much better to just go ahead and avoid it on the front end. So we talked about number one, poor variable naming. This is an incredibly huge issue, especially when you're first starting out and you're wanting to move through things very quickly and there's not a lot of code. It's easy to name things very short variable names when there's not a lot of code. When the code gets longer, those short variable names that you have, you kind of go to that same variable name over and over, that's going to come up over and over in your code and then it's going to be hard to discern one function from another, one method from another. So make sure you're naming your stuff properly. This takes just a few extra brain cells, a few more seconds to make sure that those names are correct, but it saves you so much time in the long run. Number two was tag along code. Even if you get the naming of that particular method or function correct, if you add code that doesn't belong in that method, well now that name is no longer correct and it's going to be hard to track down where that code was added if you're looking for it in the future. So make sure you don't add tag along code into methods where it doesn't belong. A good rule of thumb here is just create a method, create a new function if you are in doubt. Go ahead and create a new function. This is so much easier than actually trying to discern where these things came from. Why does this method have this thing that's totally unrelated in it? Just avoid that altogether, create an extra method. That gives it a name. That gives it something for you to describe it by. So that's number one and two. Number three, container functions. You might hear this term, God objects or God classes, God models. The idea here is the model is too powerful. It has too much stuff in it. It has too many methods attached to it. Or for example, an initialization method. This happens most often in initialization or cleanup methods, these hooks that we hook into these things that are happening maybe in the back end. We're hooking into them into the front end or maybe there's an API for this. This happens all the time, for example, in WordPress programming. You have initialization functions and cleanup methods. And they collect a bunch of actions into one big string of stuff. It's one big method that just holds all of these actions that are one after the other, one operation after the other. And really, this isn't much different than putting those methods or those declarations or those actions at the top level, like you might see in a utility script. The reason this is similar is because if you were to just take out the function name, the initialization function, which really doesn't tell you a lot about what's happening, then all you have is a bunch of unorganized imperative code. And this is what we're trying to avoid when we create these functions or when we containerize these ideas into their own methods. We're trying to avoid this top level kind of global, dirty space where everything is happening in parallel to each other. And there's no particular organization. And that's what we get when we create these initialization functions or cleanup functions or whatever you want to call those things, those things that collect a bunch of different actions into an event. The problem is not only that they have multiple responsibilities, which is already a problem, but they also are very unpredictable. They're very unpredictable and often end up having a bunch of really poor patterns, like nested logical branching, multiple return statements, side effects, all over the code. On top of this, container functions can be incredibly difficult to test and just as difficult to document. An acceptable alternative is to create functions outside of those container functions that perform the individual routines or actions or the individual pieces of imperative code that end up showing up in the initialization. This allows initialization functions to exist because it's important to have these event-driven type functions. And you read them more like a checklist rather than a long detailed imperative routine. So what you want to do is avoid these container functions, avoid these God objects. These things that have far too many methods that are contained in one big object because you didn't know where else to put it. So we want to avoid these massive functions that are just filled with imperative code. And finally, number four, so number one, poor variable naming, number two, tag along code, number three, container functions or God functions, God objects, and number four, mixing presentation with your logic. Hopefully you're not surprised by this. Hopefully you've heard this before. This has been around for a very long time. Separating your presentation from your logic is not a new idea. Of course, there's a whole set of refactoring patterns and software design patterns that recommend separating your presentation from your logic. But this particular space is even more important than it has been in recent years as we have a lot of powerful and interesting libraries and tools that allow and sometimes even encourage these two things to be mixed. React is a great example of this as you could return markup inside of the same function where your logic is and in fact, you can interspers some logic in that markup. They've gone a little bit to the point where it's a little bit more difficult to do that, but it is possible. And we want to avoid this issue by simply separating those methods from one another and using templates for presentation when possible. This doesn't necessarily mean that you have to separate these responsibilities into different files. This is the common misconception. You don't have to separate them into different files, but rather it means that the logical method or function you are writing should be agnostic to how the presentation will use the output. Let me say that again. You want to create two methods, one that does your presentation and one that does your logic and your logic method should not care about how you wrote your presentation method. Your logic method should not care about how you wrote your presentation method. Now if you can grasp this, it's going to change the way you write code forever. It's an incredibly powerful concept and I highly recommend that you at least try to write code this way and see what it does to your workflow, see what it does to the maintainability of that code. I think you're going to be surprised. Thank you so much for listening to Developer Tea today and I hope you enjoyed today's episode. By the way, Developer Tea was just launched. I'm recording on the day that it was launched in Google Play. If you are an Android user and you've been waiting on a good Android app or a good place to pick up the podcast on Android, this is your perfect opportunity. Google Play, they have the podcast directly available. Of course, that will be in the show notes at spec dot up them. Thank you again to FreshBooks for sponsoring today's episode of Developer Tea. If you want to get your invoices paid faster, if you want them to be paid online, if you want someone to handle the awkwardness of sending late payment notices for you, go and check it out. There's a free month at freshbooks.com slash Developer Tea. Make sure when you sign up that you put Developer Teain the how did you hear about a section. And of course, all of those details will be found in the show notes at spec dot fm. If you're enjoying the show, make sure you leave a review in iTunes. This is the best way to help other developers find Developer Tea. Once I know exactly how Google Play system works, I will tell you how to leave a review or how to help us out on Google Play as well. Thank you so much for listening and until next time, enjoy your tea.