Each experienced programmer has his/her own definition of clean code, but something is clear, a clean code is a code that you can read easily. The clean code is code that has been taken care of.
It’s not enough to write the code well. The code has to be kept clean over time. We have all seen code rot and degrade as time passes. So we must take an active role in preventing this degradation.
It’s a good practice apply the Boy Scout Rule. Always leave the campground cleaner than you found it.
Meaningful Names
The name of a variable, function or class, should answer all the big questions.
It should tell you why it exists, what it does, and how is used.
If a name requires a comment, then the name does not reveals its intent.
Programmers must avoid leaving false clues that obscure the meaning of code. We should avoid words whose entrenched meaning vary from our intended meaning.
Noise words are meaningless distinction.
Imagine that you have a Product class.
If you have another called ProductInfo or ProductData, you have made the names different without making them mean anything different.
Info and Data are indistinct noise words like a, an, and the.
Noise words are redundant.
The word variable should never appear in a variable name.
Imagine you have the variable genymdhms (Generation date, year, month, day, hour, minute and second) and imagine a conversation where you need talk about this variable calling it “gen why emm dee aich emm ess”.
Single-letter names and numeric constants have a particular problem in that they are not easy to locate across a body of text.
Encoding type or scope information into names simply adds an extra burden of deciphering.
Encoded names are seldom pronounceable and are easy to mis-type.
Readers shouldn’t have to mentally translate your names into other names they already know.
One difference between a smart programmer and a professional programmer is that the professional understands that clarity is king.
Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager,Processor, Data, or Info in the name of a class.
Methods should have verb or verb phrase names like postPayment, deletePage or save. Accessors, mutators, and predicates should be named for their value and prefixed with get, set, and is according to the javabean standard.
Pick one word for one abstract concept and stick with it. For instance, it’s confusing to have fetch, retrieve, and get as equivalent methods of different classes.
Avoid using the same word for two purposes. Using the same term for two different ideas is essentially a pun.
Remember that the people who read your code will be programmers.
So go ahead and use computer science (CS) terms, algorithm names, pattern names, math terms, and so forth.
When there is no “programmer-eese” for what you’re doing, use the name from the problem domain. At least the programmer who maintains your code can ask a domain expert what it means.
There are a few names which are meaningful in and of themselves—most are not. Instead, you need to place names in context for your reader by enclosing them in well-named classes, functions, or namespaces.
Functions
The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.
If you have a function divided in sections like declarations, initialization etc, it’s a obvious symptom of the function is doing more than one thing. Functions that do one thing cannot be reasonably divided into sections.
In order to make sure our functions are doing “one thing”, we need to make sure that the statements within our function are all at the same level of abstraction.
It’s hard to make a small switch statement.
Even a switch statement with only two cases is larger than I’d like a single block or function to be.
It’s also hard to make a switch statement that does one thing. By their nature, switch statements always do N things.
Unfortunately we can’t always avoid switch statements, but we can make sure that each switch statement is buried in a low-level class and is never repeated.
Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.
The ideal number of arguments for a function is zero (niladic).
Next comes one (monadic), followed closely by two (dyadic).
Three arguments (triadic) should be avoided where possible.
More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.
In general output arguments should be avoided. If your function must change the state of something, have it change the state of its owning object.
Functions should either do something or answer something, but not both. Either your function should change the state of an object, or it should return some information about that object. Doing both often leads to confusion.
Returning error codes from command functions is a subtle violation of command query separation.
Duplication may be the root of all evil in software. Many principles and practices have been created for the purpose of controlling or eliminating it.
Some programmers follow Edsger Dijkstra’s rules of structured programming. Dijkstra said that every function, and every block within a function, should have one entry and one exit.
Comments
Nothing can be quite so helpful as a well-placed comment. Nothing can clutter up a module more than frivolous dogmatic comments.
Clear and expressive code with few comments is far superior to cluttered and complex code with lots of comments.
TODOs are jobs that the programmer thinks should be done, but for some reason can’t do at the moment. It might be a reminder to delete a deprecated feature or a plea for someone else to look at a problem.
If you must write a comment, then make sure it describes the code it appears near. Don’t offer systemwide information in the context of a local comment.
The connection between a comment and the code it describes should be obvious. If you are going to the trouble to write a comment, then at least you’d like the reader to be able to look at the comment and the code and understand what the comment is talking about
Formatting
Code formatting is important. It is too important to ignore and it is too important to treat religiously.
Code formatting is about communication, and communication is the professional developer’s first order of business.
The vertical density implies close association. So lines of code that are tightly related should appear vertically dense.
Variables should be declared as close to their usage as possible. Because our functions are very short, local variables should appear at the top of each function,
We use horizontal white space to associate things that are strongly related and disassociate things that are more weakly related.
The indentation it’s important because help us to have a visible hierarchy and well defined blocks.
A team of developers should agree upon a single formatting style, and then every member of that team should use that style.
Objects and Data Structures
Hiding implementation is not just a matter of putting a layer of functions between the variables.
Hiding implementation is about abstractions!
A class does not simply push its variables out through getters and setters.
Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation.
Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures.
OO code, on the other hand, makes it easy to add new classes without changing existing functions.
Mature programmers know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them.
The quintessential form of a data structure is a class with public variables and no functions. This is sometimes called a data transfer object, or DTO.
DTOs are very useful structures, especially when communicating with databases or parsing messages from sockets, and so on.
Error Handling
Use Exceptions Rather Than Return Code.
Each exception that you throw should provide enough context to determine the source and location of an error.
If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead.
Boundaries
We seldom control all the software in our systems. Sometimes we buy third-party pack- ages or use open source. Other times we depend on teams in our own company to produce components or subsystems for us.
Third-party code helps us get more functionality delivered in less time. Where do we start when we want to utilize some third-party package?
It’s not our job to test the third-party code, but it may be in our best interest to write tests for the third-party code we use.
Some times it’s necessary work in a module that will be connected to another module under develop, and we have no idea about how to send the information, because the API had not been designed yet. In this cases it’s recommendable create an interface for encapsulate the communication with the pending module.
Unit Tests
**The Three Laws of TDD
First Law: You may not write production code until you have written a failing unit test.
Second Law: You may not write more of a unit tests than is sufficient to fail, and not comipling is failing.
Third Law: You may not write more production code than is sufficient to pass the currently failing tests.
Write one test per each concept that you need to verify.
FIRST principle for tests.
Fast: Test should be fast.
Independent: Test should not depend on each other.
Repeatable: Test Should be repeatable in any environment.
Self-Validating: Test should have a boolean output. either they pass or fail.
Timely: Tests should be written just before the code that makes them pass.
If you write tests after the production code, then you may find the production code to be hard to test.
Classes
Classes Should have a small number of instance variables. Each of the methods of a class should manipulate one or more of those variables. In general the more variables a method manipulates the more cohesive that method is to its class.
For most systems, change is continual. Every change subjects us to the risk that the remainder of the system no longer works as intended. In a clean system we organize our classes so as to reduce the risk of change.
Systems
Software Systems should separate the startup process, when the application objects are constructed and the dependencies are “wired” together, from the runtime logic that takes over after startup.
One way to separate construction from use is simply to move all aspects of construction to main, or modules called by main, and to design the rest of the system assuming that all objects have been created constructed and wired up appropriately.
A powerful mechanism for separating construction from use is Dependency Injection (DI), the application of Inversion of control (IoC) to dependency management. Inversion of control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle.
Emergence
According to Kent Beck, a design is “simple” if it follows these rules:
Run all tests + Contains no duplication + Expresses the intent of programmers + Minimize the number of classes and methods
Concurrency
Concurrence is a decoupling strategy. It helps us decouple what gets fone from when it gets done. In single-threaded applications what and when are so strongly coupled that the state of the entire application can often be determined by looking at the stack backtrace.
Concurrency always improves performance. Concurrency can sometimes improve performance, but only when there is a lot of wait time that can be shared between multiple threads or multiple processors.
Concurrency incurs some overhead, both in performance as well as writing additional code.
Concurrency bugs aren’t usually repeatable, so they are often ignored as one-offs instead of the true defects they are.