Developer’s Journal #002 — Clean Code

Marcos Costa
9 min readAug 18, 2023

--

Clean Code Cartoon

It’s no secret that one of the most recommend books for new programmers is Clean Code: A Handbook of Agile Software Craftsmanship, by Robert C. Martin. So, I’ve decided to take on this must read and bring to my little journal some of its main concepts and ideas.

This article is divided between seven sections: (i) Main Concepts; (ii) Naming; (iii) Code Structure; (iv) Functions; (v) Control Structures; (vi) Objects, Classes & Data Containers; and (vii) Conclusion and Guidelines.

I — Main Concepts

First, what is “Clean Code”? It can be defined as a term used to describe well-written, easy-to-read, maintainable and extensible code. It is a set of principles and best practices that developers can follow to produce high-quality code that is easy to understand and modify.

Some of the key principles are writing code that is self-explanatory, using meaningful names for variables, functions, and classes, and keeping functions and classes small and focused. It also involves avoiding duplication, writing tests to ensure code quality, and ensuring that code is easy to modify and extend.

Following those principles can help improve productivity, reduce errors, and make it easier to collaborate with other developers on a project.

Overall, Clean Code is an important concept in software development that emphasizes the importance of writing code that is not only functional, but also easy to read, maintain, and extend over time.

Here we highlight the power of refactoring. Clean code principles can come from the start, but it’ll certainly come from refactoring and implementing new features and fixes. A good rule of thumb is to always try to improve something when adding or changing your code.

II — Naming

The main rule for naming is that names should be meaningful and allow readers to understand what’s happening without having to dive into details.

  • Variables and Constants are data containers, therefore, is good practice to use nouns of short phrases with adjectives in a meaningful way to describe what’s being stored in them.
  • Functions and methods are commands, thus, typically, it’s better to use verbs for them.
  • Classes are uses to create “things”, such as objects, thus, nouns are usually preferred.

If the variable stores an object, a string or a number, the rule of thumb is that the name should describe the value. For Booleans, it should answer a true/false question. You can always provide more details, but without being redundant. It’s not about a big or small name, but one that clearly represents the data being stored.

Functions perform an operation; therefore, its naming should describe that operation. For functions that computes a boolean, the naming should also answer a true/false question. Again, you can always provide more details, but without being redundant. Think about what that function is performing.

Classes names should describe the object being instantiated, it’s always possible to provide more details, but redundant suffixes should be avoided. Here is very common to add too much unnecessary information that end up polluting your code.

Distinctive names are crucial. In some situations, it’s possible to have very similar methods, resulting in confusing as to knowing which method should be used. Naming should help the programmer to know which method should be cast.

In the subject of naming, consistency is also important, for example getUser, fetchUsersandretrieveUsers can all be good names, but whatever one you choose, is important to stick with it throughout your code.

III — Code Structure

Comments should be used only in rare cases. Typically, you see comments to fix problems that should have be handled with Clean Code principles. Extra text can be more harmful than helpful.

For example, imagine a comment that exists only to explain the naming of some structure, that comment is just pollution to your code and the naming should be adjusted. There are also cases of comments to separate sections of the code, in those cases perhaps it’s better to divide them into different files instead.

Nonetheless, it can be important to include some comments for explanations which can’t be replaced with good naming (e.g., Regex), some warnings or even to-do notes.

In the topic of formatting, you can think about vertical and horizontal formatting.

  • For vertical formatting, it’s important to think about the reading flow of the code. There shouldn’t be many jumps and related code should together. Ordering is important.
  • For horizontal formatting, it’s important to not keep lines to long. Use indentation, define multiple variables and break lines can help you here.

IV — Functions

Functions represent a good part of the code in a project. Not surprisingly, there are many Clean Code principles to follow when coding them.

The first would be to minimize the numbers of parameters. An optimal scenario would be to pass no parameters, but that’s not always possible. Another more general aspect to keep in mind the readability and how obvious the function is, especially when its being called. Ideally, the reader will understand what that function is doing just by reading its name, not having to go through the code to discover.

For functions that require lots of data as parameters, a go-to solution can be to structure it in a way it receives only one argument, but that argument is a data container, such as a hash, map or JSON object. When passing a map/object the order or the arguments does not matter, therefore you don’t have to guess or remember what goes where.

As usual, functions should be small. Split bigger functions into the smaller ones, each serving only one purpose, that is, doing exactly one thing/achieving one result. For defining the what’s “one thing” is a good practice to look of the level of abstraction of each step. It can’t have a too big a gap of the level of abstraction implied by the name and what the body is doing.

Don’t Repeat Yourself — DRY and Reusability are concepts that must be kept in mind when thinking about splitting functions. Whenever you find yourself copying and pasting code or managing the same concept in multiple places, that’s a hint you should probably create a function to handle the situation. Nonetheless, it’s important to use common sense, otherwise you can have to granular code.

Furthermore, Unit Testing can help in writing clean functions, because small and slim functions are easier to test. For instance, when testing for validations or if a function is behaving correctly, you can notice the same function is requiring tests for multiple actions. That can be a sign that you have too many actions encapsulated in a single function and therefore that function should be split.

V — Control Structures

The main concepts of clean code regarding structures are to (i) avoid deep nesting; (ii) use factory functions & polymorphism; and (iii) prefer positive checks.

The use of “guards” can avoid running unnecessary code and nested if statements. A guard can be perceived a way to structure of code so that the “error” scenario comes first. For instance, instead of checking if something is present, check if it isn’t first, so that it will fail first and, if there is a second validation required, the nested if statement won’t be necessary. It’s common to see coded where you would check if two properties were true (nesting each other), but with the guard, you can separate them while still achieving the same result and handling the failing scenario faster.

In control structures, its usual to run validations and check for certain properties to know how to handle different scenarios. Here an extraction can do wonders. For example, in the begging of the control structure you want to make sure that the argument is not nil or empty, so instead of running the if/else statement its best to extract the validation and create a function called isEmpty?(parameter) and use it as a guard. Keep in mind that extraction and clean code isn’t about shorter code, but rather easier code to read.

Another possibility of extraction is of a function itself. If inside a function you have a big block that handles a specific action, it may be the case to extract it into a separate function and just call it inside the “bigger” function. An example of this can be a situation where you have an object that, depending on the property X being A, B or C you need to call different functions. Instead of inserting the three different ways of handling the object inside the same function, you can extract each scenario into a separate function of its own. With proper naming and shorter code, this will certainly be cleaner code.

One more idea to achieve Clean Code while handling functions can be to invert the conditional logic. This will require a moment of consideration by the programmer to establish what’s being checked, when and if there are duplicates. But it’s important to keep in mind that sometimes just inverting the logic can be a simpler and more elegant way to arrive at the intended solution.

VI — Objects, Classes & Data Containers

Initially, it’s important to differentiate that there are some objects that function only as a data container, used only to pass values around, and there are objects where you create their own methods using the object properties.

Another important concept here is Polymorphism, the ability of an object to take many forms, that is, the structure is the same, but it can operate or handle operations in various forms, depending on the arguments passed or the way it was instantiated.

In this sense, whilst utilizing inheritance properties, you can create “special” subclasses of a base class. Thus, it’s no longer necessary to implement a series of if checks inside a class method to handle different situations, since the various special methods are now defined inside their own special class.

Typically, you should prefer many small classes over a few large ones. The concept of small here is the “single responsibility principle”. The classes must be focused on one responsibility only.

The concept of cohesion can be a good parameter to define if a class should split or not: ideally the class methods should use all the properties of the class or at least most of them. If you have many methods using only one property, perhaps it’s the case of splitting the class and using inheritance if necessary.

The Law of Demeter establishes that code in a class method may only access direct internals (properties and methods) of: (i) the object it belongs to; (ii) objects that are stored in properties of that object; (iii) objects which are received as method parameters; (iv) objects which are created in the method.

Following the SOLID (Single Responsability Principle, Open-Closed Principle, Liskov Substitution Principle, Interface Segregation Principle and Dependency Inversion Principle) principles will certainly guarantee a Clean Code when dealing with Classes, but the most import ones here are the Single Responsability Principle and the Open-Closed Principle.

The Single Responsability Principle defines that a class shouldn’t change for more than one reason. That is, if a class has multiple responsibilities, it will change for multiple reasons. Single responsibility can be perceived as referring to a single “business area/feature”.

The Open-Closed Principle established that a class should be open for extension but closed for modification. Meaning that if each time you want to add a new method/feature for that class, you must need to hard code it and create a new method, it’s best to create a subclass/special class and extend the base class for it. Therefore, that base class should be “closed” for modifications.

VII — Conclusion and Guidelines

“Clean Code” is code which is easy to read and understand.

Naming

  • Descriptive naming.
  • Nouns for variables and properties, nouns for classes and verbs for methods.
  • Be specific but not redundant.
  • Consistency in naming.

Comments & Formatting

  • Most comments are bad, except warnings, required explanations and to-dos
  • Keep related concepts together.
  • Keep lines short.

Functions

  • Limit the number of functions parameters. Consider objects as “value container.”
  • Small functions that do only thing.
  • Do not mix levels of abstraction.
  • Write DRY code.

Control Structures

  • Prefer positive wording.
  • Avoid deep nesting — use “guards” and extract control structures into separate functions.
  • Use polymorphism and factory functions.

Classes & Objects

  • One responsibility per class
  • Follow Law of Demeter
  • SOLID principles, especially SRP and OCP

--

--