4 Code Smells (And How to Fix Them)
Published 4/16/2018
This week we're going to get very practical. Today we're talking about identifying code smells (very common ones) and a few ways to fix them.
Today's episode is sponsored by Linode.
In 2018, Linode is joining forces with Developer Tea listeners by offering you $20 of credit - that's 4 months of FREE service on the 1GB tier - for free! Head over to https://spec.fm/linode and use the code DEVELOPERTEA2018 at checkout.
####Get in touch
If you have questions about today's episode, want to start a conversation about today's topic or just want to let us know if you found this episode valuable I encourage you to join the conversation or start your own on our community platform Spectrum.chat/specfm/developer-tea
Transcript (Generated by OpenAI Whisper)
We're going to get very practical this week on the show. My name is Jonathan Cutrell and you're listening to Developer Tea and my goal in this show is to help driven developers connect to their career purpose so they can do better work and have a positive influence on the people around them. And in this week's episodes, we're talking about very practical things. For example, today we're talking about identifying code smells. We're not going to talk about every code smell on the planet but we're going to talk about some very common ones and then we're going to give you some very simple ways of fixing these. Of course, we aren't going to give you any code examples on this show. That's not the goal of this podcast. Instead, we're going to give you some heuristics and ways of applying these things and some terms that maybe you can Google in your own time to learn a little bit deeper if they resound specifically for your case. So I'm really excited about this because code smells are such a common way to become a better programmer. Being able to identify them is such a key skill that programmers have to develop. And the reason we call them code smells is because there's nothing necessarily wrong with the code. In other words, there's syntax that is all correct. And even algorithmically speaking or accuracy wise, things look fine. There's nothing particularly bad about the outcome of the code. And so for all intents and purposes, if you left the code the way that it is today, it will work indefinitely. It will continue to work as you expect it to work. The problem is you're very unlikely to leave the code as it is today. In fact, most code is written in order to be changed. If things go as planned is very unlikely that you're going to write a piece of code and then leave it. So we're going to ignore cases where that might be true. For example, you may be writing scripts or macros or something like that. And you don't necessarily need to refactor those. But there's even a good case to be made for writing maintainable code, even in those scenarios. For example, if you want to publish that script or that macro as an open source tool for other people to use or perhaps if you've found it extremely useful to you and you want to share it with your colleagues, with your coworkers, then writing code, even from the very beginning, with the idea of change in mind and flexibility and maintainability, we've talked about maintainability relatively recently on the show. The idea that code is written in order to be changeable, in order to be able to flex to new requirements, flex to new situations. And even the smallest of jobs, you can benefit from writing that code in a maintainable way. So we're not talking about correctness in this episode because this episode assumes that your code is correct. Now how do you determine if code is correct? Well, it depends on the use case. But what we are talking about in today's episode is taking correct code and making it maintainable code, identifying things that kind of point to what made down the road, make that code hard to maintain, and then using those kind of smells to give you a pointer to how to fix those smells, how to make that code more maintainable for future changes. So it's called a smell because it's not going to be a black and white thing. It's very unlikely that you're going to be able to look at code and immediately recognize a code smell. It may be something that as you're working with it, you start to recognize what made down the road cause issues. And that's why it's very difficult to grab a hold of. It's not something that you can write a test for necessarily, although there are ways of making this more black and white. A code smell typically comes on in terms of a gradient. In other words, it gets worse and worse, and the smell gets worse and worse. So you may get a whiff at first, right? The sense that something might be able to be changed for the better. And then as you continue writing code and the smell gets worse, the code continues in the same bad direction, then you have a very clear picture of what has gone wrong. We've all worked on projects most likely where that smell has been ignored and eventually it became an overwhelming stench rather than just a whiff. So the hope of this episode is to give you some heuristic tools to identify things in the early stages of that smell so you can go ahead and refactor, go ahead and start pushing that code in the better direction so that you don't end up in that really bad smelling code scenario. We're going to actually cover four different code smells in today's episode. It's a lot of content, so I want to jump straight in. The first one is something that I actually have identified as a code smell for myself and others have called it something different, but I'm going to call it the 13-fingered monster, the 13-fingered monster. If you look at your code, especially if you are developing in some kind of interface language, for example, an XML-based like HTML or a JSX. If you look at your code, and you see indentient patterns that look like fingers. In other words, it goes deeply indented and then comes back out to more shallow and deeply indented again and then back out more shallow. This happens over and over and over in the same file. This is a code smell. Why is this? Why is it that deep indition and multiples of deep inditions can be a code smell? Well, most likely what is happening when you have these deep inditions is that you've created a collaborator, a collaborator takes multiple responsibilities and puts them into the same file. In a class-oriented language, you might call this a guide class, but the same thing could be true for interface languages. You very often see this in HTML where you put all of your HTML code into a single file like an index.html. There's nothing necessarily wrong with this until it becomes un-maintainable. Most of us are not writing static HTML files. We're either generating them with some kind of static site generator or we're using a template language or we're using backing code to generate them using partials, using other templates that we compile into a single template, for example, PHP. What we can do instead of having this, like we said, this kind of guide class or collaborator template, we can flatten those indentations out. So how do we do this? Well, we take the various fingers, the 13-finger monster, remember, we take each of those, those deep inditions and we try to recognize what is the job that this indition, this kind of spike in the code, what is it doing? In JavaScript, you can also view this through the lens of callback functions. If you have multiple nested callback functions, then very often you could run into scenarios where you haven't covered that particular state that callback isn't running properly, you aren't handling errors properly. Something is making it difficult to maintain that code and the callbacks are becoming nearly impossible to reason about. So that's a deep indentation in JavaScript. So what we need to do is find ways of flattening the code out. Again, in HTML or in a templating language, you can abstract these various indentations into partials that do the job that that particular code was performing. In JavaScript, you may use promises, right? You can use promises and flatten the code out so you have more reasonable functions to pass to those promises. Now here's the interesting reality. The end point, the outcome of this, may even be logically exactly the same, but the code has changed significantly. More specifically, as it relates to indentation, most of the time, indentation means new context. Continuing inside of another div or something inside of a callback function, maintaining an understanding of what state this particular indentation is referencing can be very difficult for your brain to do. And so what you're doing by switching this out and offloading it into functions or into partials or whatever it is that you use to wrap this code up, this allows you to reason much more clearly about that particular piece of code. And you can do it in one piece at a time. This is exactly the concept that we follow when we're taking code and abstracting it to components, for example, using React. We're encapsulating some kind of job, some kind of responsibility, some kind of data or information, even the semantics around that particular item. We're encapsulating that into its own thing, its own thing that has its own responsibility. So the heuristic here is if you have multiple indentations, if you have that 13-fingered monster, then it's very likely that you're going to benefit from flattening that code out. Now we're going to take a quick break and talk about today's awesome sponsor. Then we're going to come back and talk about the other three code smells very quickly. Today's episode is sponsored by Linode. With Linode, you can get up and running in just a few minutes. You can have a Linux server running in the cloud, in Linode's cloud, in just a few clicks. You pick your node location, your resources, and of course your Linux distribution. Now, there's tons of shared starting distributions that other Linode users have created that you can use as well. This is one of the benefits of Linode, that other developers, just like you, are the builders and the users of this platform. On top of that, Linode provides 24-7 customer support, and you can get started with just $5 a month. Not only that, Linode's going to provide you with $20 worth of credit, and that's four months of free service on their introduction plan. That is a one gigabyte of RAM server. By the way, all of their servers run on SSD storage and on Intel E5 processors with a 40-gig bit internal network. Go and check out what Linode has to offer head over to spec.fm slash Linode to get started today. Use the code Developer Tea2018. That's all I'm word Developer Tea2018 at checkout. Thank you again to Linode for sponsoring today's episode of Developer Tea. So, we're talking about code smells. We talked about this one that I've dubbed the 13-fingered monster. Other versions of this are the God class, but it doesn't just apply to object oriented programming pretty much any time you have a single file that is trying to describe multiple things, multiple responsibilities, articulately. There's probably a better way to handle it. There's probably a way that you can take those responsibilities, wrap them up so that you can interface with them more reasonably. You don't have to keep all of that in your brain, and you don't have to have that file know everything about that responsibility. But an extension to the 13-fingered monster is the long method code smell. This is what happens when even when you have one of those things that has its own responsibility, when it is caring too much of that responsibility. Now, here we're talking mostly about some kind of object oriented or method driven programming in JavaScript to may consider these functions. But ultimately, if you're doing too much inside of a single function, then that function number one becomes less useful, less reusable, and number two, it becomes more difficult to reason about. A decent heuristic to use is somewhere between 10 and 20 lines of code. Everyone has their own threshold, and in fact, different languages have different thresholds. For example, you may set your threshold at a much lower number. Two or three or four lines of code may be the most that you want to put into a single method. But certainly, at the top of that range is probably around 20 lines of code, especially when you start taking up more than half of your editing space or something like that, then it's probably time to start breaking this up too. Now you'll notice these first two anti-patterns, these first two code smells that we're identifying. Both of them have to do with trying to do too much within a single container, within a single component, within a single method. The antidote to both of these is to break things down into smaller pieces. You're going to see this over and over and over, and it's why a lot of refactoring ultimately ends up adding more lines of code than it does taking away. Because a lot of the time what this means is adding new methods, adding new functions to your code so that you can abstract away some of the logic that you're doing within that single long method or that single long function, that single long component. A common habit that people have when they're writing these long functions or these long components is to break them up using white space or comments. You've probably done this before. You have different sections of a function. You have different sections of a method or of a component, and you put a line break before each of these sections. And what you've done is you've created this mental bucket. You've collected some of your lines of code together that are doing something that is conceptually related. Now, when you find yourself doing this, that is a perfect signal of when to wrap that code up into its own function, into its own method, into its own component. All right, so let's get moving on. We've got two more code smells to cover. The next code smell is called the semantic battle. This is another one that I've named, unfortunately you're not going to be able to Google this. It is similar to another code smell called alternative classes with different interfaces. In that code smell, you have two classes that perform essentially the same exact thing, but you've created semantic differences between the two. But in the semantic battle code smell, you find yourself changing your code only so that you get the naming right. This often happens, and this is really the code smell that you're looking for. This happens when the naming of the business side changes, and you want your end on the code side to reflect it. In other words, there is a semantic label in the business logic or in the business language that you need to apply in your code for some reason. For example, you may have a header, and the business language wants to change that to a hero, or maybe the business logic changed the language from payment to transaction, and they want all of the language in your code changed to reflect this. Here's the problem. One of the hardest things that you'll ever do as a programmer is come up with a right name for something. There are times when this particular code smell may unfortunately be necessary, but most of the time when you actually run into this code smell, it's because you've named something that is more jargon oriented than it is descriptive. Another example of this might be naming something mission statement rather than naming it paragraph. How can you capture this information, this mission statement label while pertaining the underlying reality that this is ultimately text, that it's ultimately a paragraph? What you can do is add this concept of a labeling a visual label to your code so that no matter what that label is, the underlying data structure, the underlying reality of that code is that it's dealing with text. In fact, this is a very good refactoring solution because you're also going to deal with a very similar problem when it comes to internationalization. When you have a display to the end user that needs to be translated, right? This certainly becomes a problem. If your code is reflecting only one language, and now you have to translate that information to the end user. Adding this dynamic labeling on top of your code avoids this issue of the semantic battle. If your code continuously is having to be updated because of the business language, then it's very likely that it's time to make that update more sequestered to its own area of the code, more specifically a dynamic field. For example, if you're working with a CMS, add that label field so that can be configured on its own. Another flavor of the semantic battle happens when you try to name code based on where it shows up. For example, you may name a field in your CMS that's going to show up in the interface in this particular iteration towards the top of the page. You may name that field with the word top. A few weeks later, a redesign pushes that field towards the bottom of the page. Now you have to deal with this mismatch in naming versus the outcome versus the design. Again, another way to avoid this is to separate that labeling from the underlying reality, the underlying structure of that information, that data. Another code smell that is perhaps more relevant today than it has been in a long time is the message chain's code smell. This is when you have multiple objects that have to be jumped through in order to pass a message to another object. Another example or another version of this, maybe if you're passing data through multiple objects just to get to that final object. This happens quite a bit in React, for example, when you're passing down data through props and you're only passing it between objects until it gets to the one that you needed in. And while most of the time this kind of passing of information down to the next component is not necessarily an issue, especially if you're only passing through one or two components, sometimes it does become an issue because managing that dependency chain means that all of those objects have to stay intact in order for that final object to receive the information that it needs to render or behave properly. That's mainly for React developers. This is the point of things like flux or redux. It helps to separate the information that you would pass to a given component that you could fetch for a given component. It separates that from the structure, from the hierarchical structure of your components. If you found this code smell in an object-oriented language, for example, like Ruby, then there's a few things you can do to circumvent it. The first one is to identify why you are using the class in the first place. In other words, if class A is calling a method on an object that is an instance of class B, then why is it that that class is calling that method? Perhaps there is a way to delegate that method. So basically what you're doing is you're hiding the middleman. You're hiding the facts that you're calling that class. And instead you're delegating it to a different class. One way to handle this is to inject a dependency into the initialization of class A. And then delegating that particular method call onto that injected dependency. Now you don't necessarily have to call that dependency by name. Instead, you can just call the method. And then the receiver of the method is responsible for the implementation. What is perhaps more common is that the first class, class A, calling the method on class B, shouldn't be happening in the first place. In other words, whatever is making class A, call that method on class B. Perhaps that thing should call the method directly through class B. Now that's a little bit confusing and a little bit more in depth than we usually go on the show in terms of technical discussion. But hopefully this makes sense. The ideas here are to find ways that your code seems a little bit weird, a little bit off. And to understand why and find ways of fixing it. We've gone through four code smells in today's episode. The first one is the 13-fingered monster. The second one is the long method code smell. The third one is the semantic battle. And finally, the fourth one is the message chain code smell. I hope you've enjoyed today's episode of Developer Tea. It's a little bit more practical than we usually do. But hopefully this has been a breath of fresh air and has kicked your week off to a good start. Thank you again to today's excellent sponsor, Lynneud. It started with $20 worth of free credit on Lynneud.com. Head of respect.fm slash Lynneud to get started today. Use the code Developer Tea. 2018 and check out for the $20 worth of credit. Thank you again for listening. If this show is helping you out, and if you're feeling a little bit generous today and you'd like to give back, I would love to request that you go and leave a rating and review for this show in iTunes. This happens to be kind of the driver for discovery of pretty much every podcast platform out there. iTunes and Google Play make a huge difference if you go and leave ratings and reviews on these types of platforms. Then it helps us reach more developers. It helps other developers just like you find Developer Tea and get the same kind of value that you're getting out of it. Thank you so much for listening to today's episode. And until next time, enjoy your tea.