What is clean code and why do we need it?

What is clean code and why do we need it?

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler video.gif

A conditionally "clean" code can be classified as having the following properties:

  • easy to read and helps new developers quickly understand.

  • designed by community standards.

  • does not create problems when expanding or changing.

  • has a predictable behavior.

Why write code according to some rules at all, after all, "it works like that"? Imagine a development team starting from scratch on a simple project. In the early months, everything is fine, but business objectives change periodically, and it becomes more difficult for the team to make changes. Each new change breaks the code in two or three other places. No change can be trivial; for each change or modification of the code, you need to understand the entire structure of the system. Instead of refactoring the code or changing the architecture, we carry it over to later, in the hope that we will rewrite it someday. Unfortunately, we do not know what according to Leblanc's law: then it is equivalent to never.

In general, the concept of "clean code" is very subjective and difficult to measure, but there are still some simple rules that will help make your code more readable, flexible, and maintainable.

"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." - Martin Golding

All examples described below are simplified as much as possible and many details have been omitted for better understanding.

Meaningful names

Choose names for variables, functions, classes so that the name explains exactly what this code does and what it was created for. Picture1.png

Functions / Methods

Functions and methods should only perform one operation and be as short as possible. Functions should not contain nested structures, as this leads to their growth. Picture3.png

Blocks

Blocks in commands if, else should consist of one line, which usually contains a function call. The maximum indentation level in a function should not exceed one or two. This makes it easier to read and understand. Too many if-else statements can make the code hard to follow. Explicit is better than implicit. Picture4.png

One level of abstraction per function

Try to hide minor details in your functions/methods. You shouldn't mix levels of abstraction in functions - it always makes the code more confusing. Picture5.png

Reading code from top to bottom

Each next function must be followed by the functions called above. This way we can read our code sequentially. Uncle Bob (Robert Martin - engineer, author of the book "Clean Code") calls this approach "the downgrade rule." Picture6.png

Switch commands

There are many controversial decisions associated with the switch statement. every switch statement should be suspicious, but people use switch statements because they are an obvious brute force solution, not because they are the right solution for the situation. Picture7.png

Function arguments

Functions and methods must contain the minimum number of arguments. There must be a very good reason for using a function with many arguments. Zero arguments are the ideal case. One or two arguments are ok, and three should be avoided, and consider bundling some of the arguments into a separate abstraction or class. Picture8.png

Using flag arguments

Flag arguments can lead to some confusion in your code, so it's best not to use them at all. Such arguments complicate the method signature and indicate that the function is performing more than one operation. Remember, a true flag does one operation, and a false one does another. Picture9.png

Get rid of side effects

Don't fool yourself and other developers working with your code. Functions/methods should not do what they are not intended to do as their name suggests. The main point is to avoid common pitfalls like sharing states between objects without any structure Picture10.png

Isolate try/catch blocks

Whenever possible, try to isolate try/catch in a separate function/method. Otherwise, you create confusion in your code by mixing normal handling with error handling.

Bad comments

Well-written code doesn't need any additional comments. If you have comments that describe how your method/function works, then it is worth considering.

Mandatory comments

Despite the existing rules, you should not write a comment for every function or variable. They are worth writing only when you are developing an API.

Demeter's Law

If module "A" knows about module "B", and module "B" knows about module "C", then module "A" should not know about module "C". Also, objects/modules should not reveal their internal structure in any way. Picture11.png

Don't mess with null

Avoid using null in your business logic whenever possible. This creates unnecessary work and problems for the caller. Instead of a bunch of checks for null, it's better to throw an exception. As a last resort, omit this variable to a low level of abstraction. If you are tempted to return null from a method, consider throwing an exception or returning a special case object. A special case is a subclass that provides special behavior for specific cases known as Null Object. Remember, returning null from a method is bad, but passing null to a method is even worse. Picture12.png

The described principles are a small part of those approaches that every developer should master. But let them help you write a stable, expansive, and comprehensive code. It is very desirable to follow them, but this is more a recommendation than an ultimatum. You can also find other tips that Robert C. Martin gives us in his books.

asdasd.drawio (3).png

Useful literature

Clean Code: A Handbook of Agile Software Craftsmanship