Categories
Coding Topics

Getting Rid of Stinky Code

If you are a programmer, and have been for even a year, chances are you have seen what you might consider to be messy code; I for sure have. It’s the code that looks like someone’s mind threw up what they were trying to do, but yet it doesn’t quite do it; or if it does, the methods it uses are so ridiculous you just close the page because you can’t stand the sight of it. Luckily, from early on in my learning, I worked in a group with someone who embodies clean code. What he wrote was beautiful. I learned so much from him and, over the last year, have tried to adapt it to my own development workflow.

What to Begin

With that being said, one major takeaway from what I’ve learned from him and from the article about clean code is that writing clean code starts at the very beginning of developing any piece of software [1]. Bjarne Stroustrup put it best: “Clean code does one thing well.” [1]. While a piece of software must accomplish multiple tasks, each component of that software, such as a function, should do one thing—and do it exceptionally well. For example, in web development, a React component should focus on a single responsibility. If it’s handling form input, it shouldn’t also concern itself with data fetching or state management.

Here is an example of a React component that could be considered “Messy Code”:

RecipeSearch.js

Here is an example of the “Messy Code” above becoming “Clean Code”:

SearchForm.js

SearchResults.js

RecipeSearch.js

In the “Messy Code” example, the RecipeSearch React component is doing multiple things at once. It handles state management, user input, API fetching, and rendering the results—all within a single component. While it might perform what it needs to do, reading and understanding it can be challenging, and maintaining or extending it could lead to errors in the future.

The “Clean Code” example separates all the functions of the RecipeSearch into three distinct components, each with a single responsibility:

  • SearchForm: Responsible solely for rendering the search form and handling user input. This makes it easy to understand and reuse if the same form structure is needed elsewhere in the app.
  • SearchResults: Handles the display of search results, ensuring the rendering logic for the results is isolated from the state management and data fetching.
  • RecipeSearch: Manages the overall state, orchestrates the API calls, and connects the SearchForm and SearchResults components. It serves as a higher-level controller, keeping the logic centralized but the presentation decoupled.

By following this approach, the code becomes easier to read, test, and maintain. Each component is focused, making it less likely that changes in one area will accidentally introduce bugs elsewhere. This is something I already do, but need to do from the beginning of writing code, instead of just going back through the “messy” code and refactoring it to become “clean” code.

What to Avoid

On the other hand, from the article about bad code smells, one habit I need to work on avoiding is the Long Method. Long methods are difficult to understand, maintain, and extend. They kind of just happen if you’re not careful. They often try to do too much at once, leading to clutter and confusion [2]. The idea is simple: shorter methods are easier to read and manage because they focus on a single task. Modern object-oriented languages such as TypeScript eliminate much of the overhead of subroutine calls, making it practical to use many small methods instead of one long one.

This thing to avoid ties in very well to the concept of clean code. Long methods often indicate that a function is trying to handle multiple responsibilities, which goes against the principle of “doing one thing well.” Breaking down a long method into smaller, more focused methods not only makes the code easier to read but also improves reusability and testing.

For example, let’s revisit the handleSearch function from the messy RecipeSearch component. In its current form, it does everything: preventing the default form submission behavior, updating the loading state, fetching data from the API, and handling both success and error states. This approach makes the method long and harder to maintain.

Instead, by applying the principle of breaking down long methods, we could refactor it into smaller, more focused functions:

Here, handleSearch delegates specific tasks to smaller, dedicated functions like fetchRecipes, updateResults, and logError. Each function has a single responsibility, making the code easier to follow and less error-prone.

Final Thoughts

The road to writing clean, maintainable code starts with awareness and deliberate practice. By focusing on principles like separation of concerns, avoiding long methods, and prioritizing readability, we can significantly improve the quality of our codebases. While refactoring messy code into clean code is an essential skill, the ultimate goal should be to write clean code from the very beginning. This not only saves time and effort but also creates a foundation for scalable and robust applications.

As developers, it’s our responsibility to strive for clarity and simplicity in our work. After all, clean code doesn’t just make our lives easier—it makes our codebases a joy to work with for everyone.

Sources

[1] R. C. Martin, Clean code a handbook of agile software craftmanship. Upper Saddle River [Etc.] Prentice Hall, 2010.

[2] M. Fowler, Refactoring: Improving the design of existing code. Boston: Addison-Wesley, 2019.