Clean Code Book | Full Summary

Clean Code Book | Full Summary

Clean Code Book

Here I’m sharing all the points I have gathered from the Clean Code Book. It is a must-read book for all the programmers. If you don’t have time to read the full book then read this summary. Alert: It is going to a long summary. All these are gathered from the Clean Code Book.

The post contains 5 sections,

  1. Comments
  2. Functions
  3. Names
  4. Tests
  5. Miscellaneous

COMMENTS

Inappropriate Information:

In general, meta-data such as authors, lastmodified-date, SPR number, and so on should not appear in comments. Comments should be reserved for technical notes about the code and design.

Obsolete Comment:

A comment that has gotten old, irrelevant, and incorrect is obsolete. Comments get old quickly. It is best not to write a comment that will become obsolete. If you find an obsolete comment, it is best to update it or get rid of it as quickly as possible.

Commented-Out Code:

Delete commented out code. Who knows how old it is? Who knows whether or not it’s meaningful? Others who see that commented-out code won’t have the courage to delete it. They’ll think it is there for a reason and is too important to delete.

Warning Comments:

Sometimes it is useful to warn other programmers about certain consequences.

public static SimpleDateFormat makeStandardHttpDateFormat()
{
  //SimpleDateFormat is not thread safe,
  //so we need to create each instance independently.
  SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
  df.setTimeZone(TimeZone.getTimeZone("GMT"));
  return df;
}
TODO Comments:

It is sometimes reasonable to leave “To do” notes in the form of //TODO comments. In the following case, the TODO comment explains what that function’s future should be.

//TODO-MdM these are not needed
// We expect this to go away when we do the checkout model
protected VersionInfo makeVersion() throws Exception
{
return null;
}

Nowadays, most good IDEs provide special gestures and features to locate all the TODO comments, so it’s not likely that they will get lost. Still, you don’t want your code to be littered with TODOs. So scan through them regularly and eliminate the ones you can.

 

If you decide to write a comment, then spend the time necessary to make sure it
is the best comment you can write.

FUNCTIONS

Too Many Arguments:

Functions should have a small number of arguments. No argument is best, followed by one, two, and three. More than three is very questionable and should be avoided with prejudice. When no.of arguments increase, it becomes difficult to memories the order of arguments.
For Example: we may mistakenly call calculatePay(employee,month) but actual function definition is calculatePay(Date Month,Employee employee);

Flag Arguments:

Avoid passing flag to function Ex: calculatePay(employee,isManager) From the example you can clearly see that function does more than one thing. Because either it calculate pay for manager or for employee. They are confusing and should be eliminated.

Dead Function:

Methods that are never called should be discarded. Keeping dead code around is wasteful. Don’t be afraid to delete the function. Remember, your source code control system still remembers it.

Incorrect Behavior at the Boundaries:

Developers often write functions that they think will work, and then trust their intuition rather than going to the effort to prove that their code works in all the corner and boundary cases. There is no replacement for due diligence. Every boundary condition, every corner case, every quirk and exception represents something that can confound an elegant and intuitive algorithm. Don’t rely on your intuition. Look for every boundary condition and write a test for it.

Overridden Safeties:

Turning off certain compiler warnings (or all warnings!) may help you get the build to succeed, but at the risk of endless debugging sessions. Turning off failing tests and telling yourself you’ll get them to pass later is as bad as pretending your credit cards are free money

Duplication:

Every time you see duplication in the code, it represents a missed opportunity for abstraction. That duplication could probably become a subroutine or perhaps another class outright. Other programmers can use the abstract facilities you create. Coding becomes faster and less error prone because you have raised the abstraction level.

Code at right Abstraction

We want all the lower level concepts to be in the derivatives and all the higher level concepts to be in the base class.
For example, constants, variables, or utility functions that pertain only to the detailed implementation should not be present in the base class. The base class should know nothing about them.

Vertical Separation

Variables and function should be defined close to where they are used. Local variables should be declared just above their first usage and should have a small vertical scope. We don’t want local variables declared hundreds of lines distant from their usages. Private functions should be defined just below their first usage. Finding a private function should just be a matter of scanning downward from the first usage.

Remove Clutters

what is use of a default constructor with no implementation? All it serves to do is clutter up the code with meaningless artifacts. Variables that aren’t used, functions that are never called, comments that add no information, and so forth. All these things are clutter and should be removed. Keep your source files clean, well organized, and free of clutter.

Function Names Should Say What They Do:

If the function adds five days to the date and changes the date, then it should be called addDaysTo or increaseByDays. If you have to look at the implementation (or documentation) of the function to know what it does, then you should work to find a better name or rearrange the functionality  so that it can be placed in functions with better names.

Encapsulate Conditionals:

Extract functions that explain the intent of the conditional.
For example:
if (shouldBeDeleted(timer))
is preferable to
if (timer.hasExpired() && !timer.isRecurrent())

Avoid Negative Conditionals:

Negatives are just a bit harder to understand than positives. So, when possible, conditionals should be expressed as positives. For example:
if (buffer.shouldCompact())
is preferable to
if (!buffer.shouldNotCompact())

Functions Should Do One Thing:

It is often tempting to create functions that have multiple sections that perform a series of operations. Functions of this kind do more than one thing, and should be converted into many smaller functions, each of which does one thing.

Encapsulate Boundary Conditions

Boundary conditions are hard to keep track of. Put the processing for them in one place. Don’t let them leak all over the code. We don’t want swarms of +1s and -1s scattered here and there.
Consider this simple example:
if(level + 1 < tags.length)
{
parts = new Parse(body, tags, level + 1, offset + endTag);
body = null;
}
Notice that level+1 appears twice. This is a boundary condition that should be encapsulated within a variable named something like nextLevel.
int nextLevel = level + 1;
if(nextLevel < tags.length)
{
parts = new Parse(body, tags, nextLevel, offset + endTag);
body = null;
}

 

NAMES

Choose Descriptive Names

Don’t be too quick to choose a name. Make sure the name is descriptive. Remember that meanings tend to drift as software evolves, so frequently reevaluate the appropriateness of the names you choose. This is not just a “feel-good” recommendation. Names in software are 90 percent of what make software readable. You need to take the time to choose them wisely and keep them relevant. Names are too important to treat carelessly

Use Long Names for Long Scopes

The length of a name should be related to the length of the scope. You can use very short variable names for tiny scopes, but for big scopes you should use longer names. Variable names like i and j are just fine if their scope is five lines long. If we use a variable all over the function we should definatly give a long. So the longer the scope of the name, the longer and more precise the name should be.

Names Should Describe Side-Effects

Names should describe everything that a function, variable, or class is or does. Don’t hide side effects with a name. Don’t use a simple verb to describe a function that does more than just that simple action. For example, consider this code:

public ObjectOutputStream getOos() throws IOException {
   if (m_oos == null) {
      m_oos = new ObjectOutputStream(m_socket.getOutputStream());
   }
   return m_oos;
}

This function does a bit more than get an “oos”; it creates the “oos” if it hasn’t been created already. Thus, a better name might be createOrReturnOos.

TESTS

Insufficient Tests

How many tests should be in a test suite? Unfortunately, the metric many programmers use is “That seems like enough.” A test suite should test everything that could possibly break. The tests are insufficient so long as there are conditions that have not been explored by the tests or calculations that have not been validated.

Test Boundary Conditions

Take special care to test boundary conditions. We often get the middle of an algorithm
right but misjudge the boundaries.

 

MISCELLANEOUS

Command Query Separation:

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. Consider, for example, the following
function:
public boolean set(String attribute, String value);
This function sets the value of a named attribute and returns true if it is successful and
false if no such attribute exists. This leads to odd statements like this:
if (set(“username”, “unclebob”))…
Imagine this from the point of view of the reader. What does it mean? Is it asking whether
the “username” attribute was previously set to “unclebob”? Or is it asking whether the
“username” attribute was successfully set to “unclebob”?
. The real solution is to separate the
command from the query so that the ambiguity cannot occur.
if (attributeExists(“username”)) {
setAttribute(“username”, “unclebob”);

}

Code drafts:

Writing software is like any other kind of writing. When you write a paper or an article, you get your thoughts down first, then you massage it until it reads well. The first draft might be clumsy and disorganized, so you wordsmith it and restructure it and refine it until it reads the way you want it to read.
When I write functions, they come out long and complicated. They have lots of
indenting and nested loops. They have long argument lists. The names are arbitrary, and
there is duplicated code.
So then I massage and refine that code, splitting out functions, changing names, eliminating duplication. I shrink the methods and reorder them. Sometimes I break out whole classes, all the while keeping the tests passing.
I don’t write them that way to start. I don’t think anyone could.

Team Rules:

A team of developers should agree upon a single formatting style, and then every member of that team should use
that style. We want the software to have a consistent style. We don’t want it to appear to have been written by a bunch of disagreeing individuals.

Single Responsibility Principle:

Getting software to work and making software clean are two very different activities. Most of us have limited room in our heads, so we focus on getting our code to work more than organization and cleanliness. Maintaining separation of concerns is just as important in our programming activities as it is in our programs.
The problem is that too many of us think that we are done once the program works. We fail to switch to the other concern of organization and cleanliness. We move on to the next problem rather than going back and breaking the overstuffed classes into decoupled units with single responsibilities.

At the same time, many developers fear that a large number of small, single-purpose
classes make it more difficult to understand the bigger picture. They are concerned thatthey must navigate from class to class in order to figure out how a larger piece of work gets accomplished.

So the question is: Do you want your tools organized into toolboxes with many
small drawers each containing well-defined and well-labeled components? Or do you want
a few drawers that you just toss everything into?
Every sizable system will contain a large amount of logic and complexity.
The primary goal in managing such complexity is to organize it so that a developer knows where
to look to find things and need only understand the directly affected complexity at any
given time. In contrast, a system with larger and multipurpose classes for us to read through lots of code with
we don’t need right now.

Minimal Classes and Methods:

Even concepts as fundamental as elimination of duplication, code expressiveness, and the Single Responsibility Principle(SRP) can be taken too far. In an effort to make our classes and methods small, we might create too many tiny classes and methods. So this rule suggests that we also keep our function and class counts low.

Our goal is to keep our overall system small while we are also keeping our functions and classes small. So, although it’s important to keep class and function count low, it’s more important to have tests, eliminate duplication, and express yourself.

Test-Driven Development (TDD):

One of the best ways to ruin a program is to make massive changes to its structure in the name of
improvement. Some programs never recover from such “improvements.” The problem is that it’s very hard to get the program working the same way it worked before the “improvement. To avoid this, I use the discipline of Test-Driven Development (TDD).
One of the central doctrines of this approach is to keep the system running at all times. In other words,
using TDD, I am not allowed to make a change to the system that breaks that system.
Every change I make must keep the system working as it worked before.

Use Thread-Safe Collections:

JDK in the java.util.concurrent package. The collections in that package are safe for multithreaded situations and they perform well. In fact, theConcurrentHashMap implementation performs better than HashMap in nearly all situations. It also allows for simultaneous concurrent reads and writes, and it has methods supporting
common composite operations that are otherwise not thread safe.

Keep Synchronized Sections Small:

The synchronized keyword introduces a lock. All sections of code guarded by the same lock are guaranteed to have only one thread executing through them at any given time. Locks are expensive because they create delays and add overhead. So we don’t want to litter our code with synchronized statements. On the other hand, critical sections13 must be guarded. So we want to design our code with as few critical sections as
possible.
Some naive programmers try to achieve this by making their critical sections very
large. However, extending synchronization beyond the minimal critical section increases
contention and degrades performance.

 

Congratulations you have reached the end. Now you are a better programmer. Make sure you implement these in your code.

 

 

Read my Clean Code Book Review.

Sree Hari Sanjeev

The founder and CEO of Wisdom Overflow. His enthusiasm and effort has taken the blog to next level. You will be motivated just by taking a look at his daily schedule.