The Clean Programmer Manifesto or a brief synopsis of Robert Martin's Clean Code book
This article is a synopsis of Robert Martin’s Clean Code book and my understanding of how Clean Code should be. There are no sections on testing, TDD, about what should be the architecture, etc. Here everything is only about what the Pure Code should be.
Yes, it is possible that the Clean Code theme has already been banned, but nevertheless, not everyone is familiar with it and, all the more, I have not met analogs of the content contained in my article.
There is no true way and decision. There is one that is best suited for solving a particular problem.
When solving a task, try to reproduce absolutely all cases that can affect this task and implement the task with regard to absolutely all cases.
Also, when solving a problem, try to go from the opposite. Understand what results in the end you want to get and build on this basis the algorithm for which the task will be performed.
Before you send a task to release - check if it works correctly. Are there any mistakes in it? This applies even to those commits that are sent to your thread. The most ideal scenario is the one in which no one could find errors in the functionality that you developed.
Always think about how to make your code simpler, cleaner and more readable.
- What cases can a task have?
- Did I learn everything?
- What can go wrong?
- What can be combined?
- Is there a similar functionality?
- What is there too much?
- How to make it easier?
- How to make more readable?
- How to make it clearer?
How to write clean and good code? It is like writing a book. First, you make a draft and then comb it to the state in which you would be pleased to read it. Always remember that your code must tell the story of what is happening so that the reader can understand it.
An entity is understood as an interface, class, method, variable, object, etc.
- Clean code is simple, expressive and focused on a specific task.
- Clean code is easy to read, like prose. If this is not the case, then it is worth refactoring.
- Clean code is easy to change. It should not be tightly tied on a pile of entities. Any entity can be easily changed.
- Clean code is much better to review. If the review passes with a huge number of comments, it is not clean and it should be refactored.
- Clean code always looks like it was worked on for a very long time. Whatever ways you’re looking to improve it, you’ll still come to the conclusion that this code is the best. Accordingly, the clean code - thought out to every detail.
- Boy Scout Rule: Leave a parking space cleaner than it was before you. This is easily shifted to programming. See the dirty code? Make it cleaner while solving your task. You should not get involved in this and if the dirty code is very dirty, then it is necessary to allocate a separate task and time to clean it.
- Do not be afraid to make a change. If you want to make them, then you have a reason for it, which means you will make the code better and cleaner. Moreover, the tests will show if there are any errors in your code (provided that they exist at all).
- Any entity should be responsible for one functional and only for it. And she should perform it well. Single Responsibility.
- If an entity is responsible for two or more actions at once, then its functionality should be separated.
- The code should be read from top to bottom.
- In a good and competent architecture, making changes does not require significant costs and effort.
- Delete dead code. Dead code is a code that will not be called under any circumstances or a code that is not used anywhere.
Names and divisions
- Use clear and easy-to-pronounceable names for any entities. They should describe why this entity exists, what it does and how it is used.
- Do not be afraid to spend time choosing the best and most understandable name. You will win in the future when working or reading this code.
- If the name of the entity does not correspond to its functionality or it is not clear by its name what the entity does, then it must be renamed to the most understandable name. If this is not possible, then something is wrong with its functionality and it must be refactored.
- The entity, which has the title "And", "With" - violates Single Responsibility. The functionality of such an entity is worth sharing. But this rule is sometimes neglected.
- Incomprehensible texts, strings should be put into variables and give them clear names.
- Method names must contain a verb that describes what this method does and the keyword the method works with. If there is no verb in the name of the method, then this entity should not be a method or it should be given the correct name.
- It is necessary to avoid the same names for two different purposes.
- If an entity has a name similar to another entity, then most likely their functionality is very similar and should be combined? If not, their names should be changed so that they are not similar.
- If you mentally rename an entity when you read the code so that you understand its functionality more clearly, then rename it to this mental name.
- Choose one word for one concept. It will be difficult to understand the functionality when you have fetch, retrieve and get in the names. Let it be better to get everywhere.
- A long and clear name is better than a short but incomprehensible.
- Functions should be short and compact.
- Functions must be very short and very compact.
- Approximate maximum of 20 lines and 150 characters in one line, if not fit, then you need to separate.
- A function must perform only one operation.
- She should do it well and she should not do anything else.
- If the function performs only those actions that are on the same level of abstraction, then the function performs one operation.
- To determine if a function performs more than one operation, try extracting from it another function that will not be a simple reformulation of the implementation.
- Any conditional operators with long choices through a switch-case, if-else should be divided or combined without duplication, possibly into classes with implementations, and the choice of implementation should be transferred to the base class, factory, or someone else.
- If, else, while, etc. must contain a single function call. So it will be more readable, clearer and easier.
- The ideal number of input arguments for the function = 0. If the input arguments are more than three, then it is worth considering how best to get rid of them, for example, create a class for these arguments.
- The more input arguments, the harder the function is understood.
- The function to which the argument flag is passed, on which the function depends, indicates that the function performs more than one operation. Such functions should be divided into two and called their level above.
- A function that changes the input argument should give a reference to the modified object, and not just change without return.
String transform(String text)
- If the function should change the input argument, then let it change the state of its own object.
- If the input argument of the function should not change (and is used later in the code), then copy the value of the argument and work with the copy inside the function.
- Instead of return null it is better to use an empty object -
Collection.empty()or a null-object -
- Always try to use non-static functions. If this is not possible, use static.
- If there is a code that should follow one after the other, then transfer the results of the first function to the second one so that someone does not change the sequence of calls.
- Use polymorphism instead of if / else or switch / case or when.
- Avoid negative conditions.
- Do not use comments if you can use a function or variable instead.
- Do not comment on the bad code - rewrite it. It is not necessary to explain what happens in bad code, it is better to make it explicit and understandable.
- Comments can be used to transmit some information, warnings about the consequences, but not to explain how the code works.
- Use TODO and FIXME in cases where you need to mark that the code needs to be improved, but now there are no resources for this.
//region REGIONNAME //endregion REGIONNAME, and if you use, then think whether it is possible to divide the region into entities.
- Document the code that is complex but clean.
- Do not leave the old commented out code. You can find it in commit history, if necessary.
- Comments should be brief and understandable. In the comments with the information should not be a lot of information. Everything should be brief and to the point.
Formatting and rules
- Follow the codestyle adopted on the project.
- Follow the rules adopted in the team.
- Subject to the formatting and codestyle code will be easier to read and better. It's no wonder that they give the book to the editorial board before it is published.
- You need to have automatic tools that will format the code for you.
- The source code file should be like a newspaper article. There is a title, a brief description in the form of parameters and content in the form of functions. If this is not the case, then you should change the formatting.
- Entities associated with each other should be located nearby, for example, in the same package in order to make it easier to navigate through the code.
- Class variables (fields) must be at the top of the class.
- Method variables should be closer to their place of use.
- Functions must be in the order of the call. If one calls the other, then the calling function must be above the called one. On the other hand, lower-level private functions may be located at the bottom of the file and do not interfere with the understanding of the high-level code. But I prefer the first method.
Objects and data structures
- You have to work with abstractions so that the implementation can be easily changed.
- You have to work with abstractions, because a client that uses the functionality does not need to know about the implementation details, he must know which implementation to use in which case.
- You must provide an API with which to work and hide implementation details, structure. So it will be easier to work with such entities and add new kinds of behaviors, functional and implementations.
- DTO - Data Transfer Object. A class that contains only data and no functionality. Needed in order to transmit any data. An object of this class must be immutable.
- Classes should be compact.
- Classes should be even smaller.
- The class name must describe its responsibilities. From here you can calculate the size of the class.
- The class functional must clearly fit and fit the class name.
- Divide connectivity into small classes. Rigid and abundant connectivity should not be - this complicates the support and development of the project.
- Remember Single Responsibility. An entity must have one and only one reason for the change.
- Observe encapsulation. Weakening encapsulation should always be the last measure.
- Usually we declare variables and auxiliary functions to be private, but sometimes they need to be declared protected and be able to access it from the test.
- If a group of functions belongs to a specific functional, then this group of functions can and should be separated into a separate class and its instance can be used.
- Use Exceptions instead of returning error codes.
- Error handling is one operation. If the function has a keyword
try, then after the blocks
catch/finallythere should be nothing else in the function.
- If you have an enum that lists errors, then it is better to get rid of it and use exceptions instead.
- Use unchecked exceptions to explicitly point out where the problem is. Such errors do not need to be caught; instead, you need to write code so that this error never occurs.
- Pass enough information along with an exception to be thrown so that the users of your code can then understand what really happened.
- Instead of conditional operators with error handling, it is better to throw exceptions and handle them.
- Do not pass null anywhere. Try to avoid this as much as possible.
- Error handling is a separate task and does not relate to the main logic of the program.
- We always use any libraries that most often give us too wide, too little functionality or conflict with the expected functionality, which makes the code dirtier in its final use. You can avoid this by simply applying patterns like Decorator, Adapter, Facade or others.
- There are situations when you need to work with functionality that is in development or has not yet been adapted for use in production code. In this case, you should imagine what you expect from the library / this functionality and write your own interface or create an entity with which you will work in your project as you need. When the library is completed and becomes stable, you will adapt it to your ready-made structures and use the ready-made functionality.
This article provides only recommendations for writing Clean Code. Of course, they can be neglected. You just need to understand that any of your decisions should have arguments in favor of it.