Clean Code
Uncle Bob brings us the necessary principles for clean code and encourages the practice of these principles to develop the skill of being a clean code craftsman.
5S Principles:
- Seiri - Organization (think "Sort") - Knowing where things are.
- Seiton - Arrangement (think "Systematize") - Code should be where you expect to find it.
- Seiso - Cleaning (think "Polish") - Refactoring and eliminating comments.
- Seiketsu - Standardization (think "Standards") - Maintain standards among the development team.
- Shutsuke - Discipline (think "Self-discipline") - Maintain discipline in adhering to clean code practices.
We always need to maintain and improve our code, making it cleaner. Often, we make the code work and consider it done.
Part I - Principles, Patterns, and Practices for Creating Clean Code
1 - Clean Code
- Codes represent the details of requirements. Specifying them in detail so that the machine can execute them is programming.
- Bad code can ruin companies. Maintaining bad code is costly.
- Don't let yourself be corrupted by managers' deadlines. They defend the deadlines, and you have to defend clean code as a deliverable. Disorganized code will only delay deliveries.
- Writing clean code is like painting a picture. Most of us can distinguish a well-painted one from a poorly painted one. But not everyone can paint well.
- Clean code does one thing well, requires attention to detail, and is simple and straightforward. It is readable, understandable, and all information is expressed within the code itself. Code without tests is not clean.
- Scout Rule: "Leave the campsite cleaner than you found it." Always refactor.
2 - Meaningful Names
Use meaningful names that reveal the purpose of the code. Use searchable names.
3 - Functions
Functions should not have too many parameters. Functions should not have logical parameters that require entering the function and understanding their use.
4 - Comments
"Don't comment bad code, rewrite it." Old comments are harmful as they spread lies.
5 - Formatting
Follow alignment and spacing standards. Avoid excessive indentation layers. Follow a logical order of declarations and function calls.
6 - Objects and Data Structures
"Objects should hide their data and expose operations" - Law of Demeter
7 - Error Handling
Use exceptions instead of returning error codes. Create informative error messages and pass them along with exceptions.
8 - Boundaries
When using code that is out of your control, pay special attention to your investment and ensure that future changes are not too costly. Using a single point of contact with external libraries is a good approach.
9 - Unit Tests
Tests are as important to project health as production code. TDD
- First Law - Don't write production code until you have a failing unit test.
- Second Law - Don't write more of a unit test than is sufficient to fail, and not compiling is failing.
- Third Law - Don't write more production code than is necessary to pass the current failing test. FIRST Tests need to be clean and follow the F.I.R.S.T rules:
- Fast - Tests should be fast.
- Independent - Tests should not depend on each other.
- Repeatable - Tests should be repeatable in any environment.
- Self-Validating - Tests should have a boolean output.
- Timely - Tests need to be written in a timely manner.
10 - Classes
Classes should be small and follow the following order:
- List of variables (Public, Static, Constants, Private).
- Methods. Apply the Dependency Injection Principle so that classes depend on abstractions rather than concrete details.
11 - Systems
"Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build, and test." - Ray Ozzie, CTO, Microsoft Corporation
Systems are like cities that need to progress through appropriate levels of abstraction and modularity, allowing individuals and "components" to work efficiently, even without understanding the whole.
Software systems should separate the initialization process - creating application objects and "connecting" dependencies - from the runtime logic that comes after initialization.
An architecture can grow gradually if we maintain proper separation of concerns.
An invasive architecture affects agility and overrides domain logic, which, when obfuscated, loses quality as bugs hide more easily and implementation becomes difficult.
12 - Emergence
Four rules of Simple Design by Kent Beck:
- Pass all tests.
- Don't repeat yourself.
- Express the programmer's intent.
- Minimize the number of classes and methods.
Most of the costs in a software project come from long-term maintenance. The clearer the author makes their code, the less time others will spend understanding it.
13 - Concurrency
Decoupling "what" and "how much" often has a significant impact on the system's structure.
Keep your concurrency-oriented code separate from the rest of the code.
Take data encapsulation seriously; severely limit access to any shared data.
Try to divide data into independent subsystems that can be manipulated by independent threads, possibly on different processors.
Part II - Case Studies with Increasing Complexity
14 - The Grand Redesign in Small Steps
The author presents a case study where he took an application and refactored it to achieve cleaner code. It is not enough to make the program work professionally. We need to refine and clean our code continuously.
15 - Internal Characteristics of the JUnit Framework
The author examines parts of the JUnit framework code and proposes improvements. Scout Rule: Leave the code a little cleaner than you found it.
16 - Refactoring SerialDate
The author presents a class called SerialDate in the JCommon library and proposes improvements.
Part III - Heuristics and Code Smells
17 - Code Smells and Heuristics
- Putting code in comments is an abomination. Maybe the code could be clearer so it doesn't need the comment;
- Bad names (variable, method, class);
- Building a project should be a simple and single operation (EG: yarn build);
- Dead code ( "turned off" code) or unnecessary;
- Distance from the margin (increased complexity);
- Functions should have a small number of parameters;
- Output parameters are unexpected;
- Boolean parameters are confusing;
- Don't be afraid to delete functions that are not used;
- DRY (Don't repeat yourself) - Don't duplicate code. Use Template Method pattern and Strategy. Object orientation and structured programming are tactics to organize modules and eliminate duplication;
- Base classes should not see anything in their derivatives;
- Limit information to help maintain low coupling;
- Keep your source files clean, well organized and free of clutter;
- In general, it is better to have many functions than to pass code as a parameter to select behavior;
- We want the code to be as expressive as possible;
- Use descriptive variables, if you can't understand what a function does and need to look at the documentation it's better to refactor;
- There is a difference between understanding how the code works and knowing if the algorithm will do the job properly. Not being sure if the algorithm is appropriate is normal. Not being sure what your code does is pure laziness;
- The team should not need a document describing their conventions because their codes provide the examples;
- Different responsibilities (SRP breach);
- Blank line (no pattern);
- Magic numbers (that could be constants);
- Inadequate exception handling (return -1, unnecessary try/catch);
- Unnecessary else (invert the if);
- Untyped variable (in languages where typing is mandatory);
- Variables declared together;
- Variables declared long before their use (eventually groups of variables);
PS: Remember that performance can be affected by clean code.