Your One Stop Clean Code Guidelines Article

Your room can be messy but your code shouldn't, I am not the most organized person in the world and my room is not the best piece of art out there, and I hear the whole universe in a Morgan Freeman's voice telling me that "Its okay", unless you have a girlfriend or a spouse,then that is a different story. The Point is, your room is YOUR room, but when you are working on a team of more than 20 people, then the code you commit is NOT your code, even if you are the only one working on that project, having your code all messy, dirty and unorganized is a very bad thing and is gonna cost you a lot of time debugging and extending it.

Writing a clean code is very critical, and there might be a lot of information out there, but here is my one stop clean code article, it might be lengthy, but its very important, and I am going to cover a lot of things on how to write Clean Code, and also you can refer to this one stop when writing your code and see how you can make it better, and also read it from time to time so you know it by heart! lets get started.

Before we dive into clean code, lets first start by this great quote by Martin Fowler and reflect on it:

Any fool can write code that a computer understands, Good programmers write a code that humans understand.

Its good to have a working code, no doubt, you can write a dirty code, the compiler compiles it, the machine understands it and all cool, however, what if you want to debug this code a few months from now, what if someone else from your team wants to extend a feature on top of a dirty code that you wrote, that is a nightmare, and you, other developers are going to have a lot of WTF moments. So its better you save yourself and others the trouble and make your code clean and readable and that everyone can understand.

Write Code for Humans.

I recommend, before you dive into TDD, DDD, Design Patterns, SOLID Principles, Refactoring, its better if you start by Clean Code, its the foundation and the mother of all!

Okay, lets talk a bit more about the philosophy and the principles of Clean Code, there are three principles for Clean Code as follows:

  1. The right tool for the Job
  2. High Signal to Noise Ratio
  3. Self Documenting

Before getting hands dirty and writing clean code examples its important to understand the principles.

  1. The right tool for the Job: If you want to play music with high volume, then sure as hell you might use your existing laptop, and that might give you good a enough high volume, but better than that is to use speakers with dedicated amplifiers than can give you a high quality volume, using the right tool for the job is very important. In the Clean Code Context, using the right tool for the job means that you should watch out for boundaries and not to mix different tools together, you want to style your website, sure you can put inline styles in your html code, but its better if you separate your styling in a different .css file, I hope you get the idea and here are some examples and boundaries to watch out for:
No alt text provided for this image

The key point is,try to stay native whenever you can, and avoid using one language to write another language, try not to mix technologies, and this way you get a more reusable, syntax checked, colored code.

2: Maximize Signal to Noise Ratio: This concept is really important, as we agreed on earlier, your code should be written for humans, the signals are the useful information that you want yourself or the developers to get from the code, its the intent, the noise are the things that getting this intent more difficult to grasp, in a way that the mind cannot easily understand such as unnecessary comments, poorly named variables, huge classes, repetition, no white spaces.

In Summary your Signals should follow the TED Rule :

  • Terse : your code should be brief and using only few meaningful syntax
  • Expressive: remember your code should be self documenting so instead of writing unnecessary comments, you can use functions and variable names to make your code more expressive and self explaining
  • Do one thing:

Noise can be :

  • Unnecessary comments
  • Poorly named Structures
  • High cyclomatic complexity (more on that later)
  • Huge classes
  • Long Methods
  • No White Spaces
  • Excessive indentations .. etc

The importance of this comes because our brains act like the compilers and the mess of a high noise ratio builds up and makes code more difficult to follow.


The DRY Principle : Our talk about Signal to Noise Ratio wouldn't be complete without mentioning the famous DRY Principle (Don't Repeat Yourself) and you need to watch out when you copy and paste code because that mostly leads to :

  • Decreasing Signal to Noise Ratio
  • Increasing the number of lines of code, more lines of code means that you have more than what is necessary to convey your intent
  • Creating maintenance problems , if you need to change a piece of code, you need to make sure that you change all the other places where that code is duplicated. Its a mess!

3: Finally the Third Principle is Self Documenting Code: If you write expressive code, with clear function names and meaningful variable names, your code will be self documenting, its Important not only to organize your code but also to organize your packages/ namespaces , classes and methods. When you are reading a book, its organized in Chapters, in these Chapters you can have Headings to explain what the next text is about, and Finally you have your Paragraphs, you can implement the same concept in your code as follows:

No alt text provided for this image

So the Key points are:

  • try to have your code self documenting and have a clear intent
  • try to have lyres of abstraction in a sense that it makes your code and classes easier to navigate, just like a book
  • try to favor code over comments
  • indent and format your code for easier readability

I hope the previous Theory and Philosophy of the Clean Code Principles have prepared you for the Juice, for the Meat, for the actual Clean Code examples that will be discussed now with code snippets.

Next, I will be talking in details about different areas of Clean Code, we will start by Naming your classes, your methods and your variables. Then we will touch on how to write Clean Conditionals, after that we will talk about writing Clean Functions , Classes and finally we will touch on writing Comments. Lets get started:

Note: the code I am writing is ABSTRACT code, and doesn't necessarily belong to a specific programming language, I found that easier to convey the intent of the article

1: Naming : Naming matters, clear names can easily convey intent and make your code easier to follow:

Dirty:

List<decimal> p = new ArrayList<>() {5  , 10 , 20 , 30 } ; 

Clean:

List<decimal> prices = new ArrayList<>() {5  , 10 , 20 , 30 } ;

1.1 Naming Classes, They Should Be Nouns and Do One Thing:

When writing your classes, name your classes with the following guidelines:

  • Classes should be Nouns, Clean: User , Account , Product , ParkinLot
  • Avoid Generic Classes that can become a magnet where lazy developers can come and write on them, so be specific. Dirty : Utility , Common
  • Single Responsibility, your class should have a single responsibility, I don't expect your User Class to ever handle Payments, Dirty : MyFunctions Class
  • Avoid Generic Suffixes , Dirty *Manager , *Processor

1.2 Naming Methods Should Clearly Express Intent:

The method name should say it all, and it mostly a verb, it should clearly express your intent

Dirty :

get ();

process();

start();

destroy();

Clean :

getRegisteredUsers();

isValidUser();

exportHistoryReportToExcel();

sendEmail();

1.3 Warning if you see 'And' , 'If' , 'Or' ?

Here are some signs that your classes or functions are doing more than one thing: If you ever see a function or a class name that has : And , If , Or , its a sign that they are doing more than one thing and its important to step back and separate the logic into different functions or classes:

Dirty

signupAndSendVerificationEmail ();


Clean:

signup();

sendVerificationEmail();

1.4 Watch out for side effects ?

Side effects can come in a form where you have a function that does more than what it says:

  • checkPassword() shouldn't log the user out
  • signup() shouldn't send emails

1.5 Avd Abbr ?

Do you know what are we talking about? smart you guessed it! Avoid Abbreviations ! its not the 70s any more and by having powerful IDEs you can have auto-completion out of the box, you need to avoid abbreviations because it creates ambiguity, and it makes your intent not clear and subjected to misinterpretation:

Dirty :

regUsr();

regisUsr();

calP();

Clean :

registerUser();

calculateTotalPrice ();

1.6 Naming Booleans should sound like true/false questions

Dirty : open , status , login

Clean : isOpen , isActive , loggedIn

The dirty examples doesn't have the nature of true false questions, open might have the intent of opening something and it sounds more like a function (verb) however if you use isOpen, your intent here is clear that you are answering a true/ false question

Dirty:

if (login) {

// do something 

}

Clean:

if (loggedIn ) {

//do something 

}

1.7 Be Symmetrical when Naming Variables that deal with states that toggle :

Dirty:

// on / disable

// slow / max


Clean:

// on / off

// min / max

And these were most of the things that you need to know about naming, lets now talk about conditionals and for some reason its my favorite part.

2: Conditionals:

No alt text provided for this image

Conditionals are a fork in the road, and when writing conditional code its important to understand the original intent of the programmer, it might sound intuitive but a lot of things can go wrong when writing your conditionals:

Understanding the original programmer's intent is the most difficult problem. (Fjelstad & Hamlen 1979)

Here are some guidelines that you should try to follow when writing conditionals:

2.1 Compare Booleans Implicitly and not Explicitly :

Its better to compare your booleans implicitly, be DRY, lets take these two examples:

Dirty explicit comparison:

if (loggedIn == true ) {

// do something 

}

Clean implicit comparison:

if (loggedIn) {

// do something 

}

The implicit comparison is less code, more intuitive in the sense that when you read it or when other developers read it, it sounds something like :

" If loggedIn then do something "

While the explicit comparison will sound something like:

If loggedIn is true then do something

The explicit comparison doesn't sound intuitive and its always a good idea to see how your code will sound when its read.

2.2 Assign Booleans Implicitly as well :

Dirty:

bool goingBackToMyCountry ;

if ( cashInWallet > 5000 ) {


goingBackToMyCountry = true;
} else {

goingBackToMyCountry = false;
}

Clean:

bool goingBackToMyCountry = cashInWallet > 5000 ;

Now this reads like a speech "I will go back to my country if the cash in my wallet is greater than 5000 " and has fewer lines, also you don't have a separate line for initialization.

2.3 Don't be Anti-negative : it creates confusion !

Dirty:

if (!isNotLoggedIn )

Clean:

if (loggedIn )

2.4 Use Ternary, its elegant :

int entranceFee ;

if (isLady ) {

entranceFee = 0 ;

} else {

entranceFree = 50; // true story

}

Clean:

int entranceFee = isLady ? 0 : 50 ;

Don't repeat yourself.

2.5 Use Enums and avoid being Stringly Typed :

Dirty:

if ( status == "success" )

Clean:

if (status == Status.Success )

We notice the following benefits when you use Enums:

  • its strongly typed and checked
  • you can go to the Status Enum and navigate through all the available and possible statuses
  • its easily searchable

2.6 Avoid Magic Numbers!

As we mentioned before, its really important to understand the programmer's original intent, when you have magic numbers that you only understand it will be difficult for other developers to know your intent take this as an example:

Dirty:

if (age > 21 ) {

// do something 

}

21 is a magic number and you might have guessed it, but we don't want you to guess it, we want you to be sure that this is the intent, here is a better implementation:

Clean:

int DRINKING_AGE = 21 ;
if (age > DRINKING_AGE ) {

// do something 

}

2.7 Avoid Complex Conditionals by Intermediate variables or by Encapsulating it via a function:

I was guilty of this big times, when you have complex conditionals that check different conditions, you might end up having a code with no clear intent. lets discuss some examples:

2.7.1 Using intermediate variables:

Dirty:

if ( employee.age > 55 
    && employee.yearsEmplyed > 10
    && employee.isRetired ) {

// do something 

}

As you can see that the intent here is not clear! and I don't think you will be able to guess it, a more elegant solution is to use intermediate variables that convey the intent :

Clean:

bool eligibleForPension = employee.age > minRetirementAge
                       && employee.yearEmployed > minPensionEmploymentYears
                       && emplyee.isRetired ; 

Now here we introduced an intermediate variable eligibleForPension that clearly conveys the intent, and also, we got rid of the magic numbers of 55 and 10.

2.7.2 Encapsulate logic via a function:

The great thing about functions are that they can explain themselves and they make the intent clear, lets take this example:

Dirty:

// check for valid file extentions
  if (fileExtention == "mp4" ||
      fileExtention == "mp3" ||
      fileExtention == "avi" ) {

// do something 

}

Please favor expressive code over comments, you can extract this logic into a function:

Clean:

private bool isValidFileExtention (string fileExtention ) {
return (fileExtention == "mp4" ||
        fileExtention == "mp3" ||
        fileExtention == "avi"  ) ;

This is cleaner now, a cleaner way would be to put all the valid file extensions in an array or any data structure and check if the fileExtention exists, I will leave that to you.

2.8 Be declarative if possible by using the right tool for the job:

Lets take this example:

Dirty:

for (User user : users) {
    if (user.getBalance() > 50 
     && user.getStatus() == Status.ACTIVE)
        matchingUsers.add(user);
}

Clean:

matchingUsers = users.stream()
                           .filter(user -> user.getBalance() > 50
                                && user.getStatus() == Status.ACTIVE )
                           .collect(Collectors.toCollection(ArrayList::new));

When you are writing the for loop way, you actually care about how to do the filtering by using if statement, while in the second case you care about the what, its clear that you are filtering the users based on a specific criteria, and this is using the right tool for the job.

So the rule of thumb would be to more declarative, and when you are dealing with such data structures, its better to use the lambdas or any other tool instead of a for loop. I have mentioned this before in a different article that nowadays in modern programming you are rarely going to use a raw for loop.

2.9 When Dealing with lots of if statements, Code might not be the answer :

Lets take this example of which is snippet of a method to calculate the tax rate:

Dirty:

if (salary < 3000 ) return 0;

else if (salary < 6000 ) return 3;

else if (salary < 10000 ) return 7;

else if (salary < 15000 ) return 15;

The cleaner way to write this is not in code, this can be a database table with tax rates and you can simply

Clean:

return Repository.getTaxRate(salary);

This called Table Driven Methods and it has the following advantages:

  • its great for dynamic logic as the tax rate might change
  • it makes you avoid hard-coding
  • it makes you write less code
  • its easily changeable without the need to recompile/redeploy your code

3: Functions: in this section I will talk about functions, functions are the same like paragraphs in a book, and its important that these function to have clear names, first lets start by when to use functions.

There are four main reasons to create a function:

  1. Avoid Duplication, as a programmer, try to honor the DRY principle, the code you are writing is a liability and as the great saying says:
Less is more

Writing functions will also help you maintain your code, if you have code duplication and you change it, you need to make sure that you changed all the other places where this piece of code is duplicated.

2: Indentation: developers might think that avoiding duplication is the only reason to write functions, but that is not the case, even if your function is called once. Indentation is a sign of complexity and as you remember, understanding the intent is the harder part, extracting the indented logic into functions is another reason to write functions.

3: Unclear intent: you see me talk about the intent a lot, because its really important in Clean Code, if the intent is not clear you can extract the logic with a well descriptive function names to convey the intent

4: Having more than one task: to honor the single responsibility principle, its important that your functions should do only one thing, having a function that does more than one thing is another reason to create another function.

No alt text provided for this image

Now after we know, when to write a function, lets dig deeper and see how to write them with dirty and clean examples:

1- Avoid Duplication:

3.1.1 look for patterns it might not be obvious :

The duplication might not be obvious all the time, you might find yourself using the same regular expression several times, in that case you need to extract it to avoid the duplication.

2- Excessive Indentation :

When our logic grows complex, its common to see arrow shape code, and means that the code reader has to keep all these levels in his head while reading the code, it reduces readability and it might be a source of bugs and less test-ability, if you see lots of indentation in your code, then its a bad sign and that needs to be refactored or written in a clean way.

No alt text provided for this image

To solve the issue of the arrow shape code there are three possible solutions:

  1. Extract methods
  2. Return early
  3. Fail fast

3.2.1 Avoid excessive indentation by extracting methods:

A picture worth a thousand word:

No alt text provided for this image

You see how the complicated function is extracted and how that now is more readable, lets take another example and lets try to generalize it:

Dirty:

try {

// so 

// many 

// piece 

// of 

//logic 

// save 

// planet
}
catch (ArgumentOutOfRangeException e ) {

// do something with the error

}

A cleaner way would be to extract the logic of the Try block into its own function

Clean:

try {

saveThePlanetFromGlobalWarming();

} catch (SpaceSataliteNotFoundException e)

// do somethng 

}

private void saveThePlanetFromGlobalWarming ()
{


// so 

// many 

// piece 

// of 

//logic 

// save 

// planet
}

So to try to generalize this rule, whenever you have code blocks like while loop, if statement, try code block, its better to try to extract the logic into its own functions if that would make it more readable.

3.2.2 Avoid Excessive Indentation by failing fast

When your code might be failing, its better to fail fast, because if it is failing, then there is no point to continue until the end, lets take this example:

Here is a Dirty example of failing slow:

public void registerUser (String username, String password) {

  if (!String.isNotNullOrWhiteSpace (username)) {

       if (!String.isNotNullOrWhiteSpace (password) {
              // register the user
       }
       else {
            throw new ArgumentException("username is required ");
       }
  else {
       throw new ArgumentException("password is required ");
  }
}

Fail fast! Clean:

if (String.isNotNullOrWhiteSpace (username)) throw new ArgumentException("username is required ");

if (String.isNotNullOrWhiteSpace (password)) throw new ArgumentException("passwordis required ");

// register user here

You see here we inverted the logic to when the logic fails, in this case when the username or password is null or has white space and then we failed fast, because there is not point to continue on a failing code, and now that makes your code more readable and reduces the excessive indentation problem.

3.2.3 Avoid Excessive Indentation By Returning Early:

Another almost similar approach to fail fast is to return early, try to use your return statements as early as possible and that will make your code better and less indented. lets take an example:

Dirty:

private bool isUserNameValid (String username) {

  int MIN_USERNAME_LENGTH = 6 ;
  if (username.length >= MIN_USERNAME_LENGTH ) {
     MAX_USERNAME_LENGTH = 25 ; 
     if (username.length <= MAX_USERNAME_LENGTH ) {
         bool isAlphaNumeric = username.isAlphaNumeric ();
         if (isAlphaNumeric ) {
            if (!containsCurseWords(username)) {
                isValid = isUniqueUsername(username);
            }
         }
      }
  }
return isValid;
}


A Cleaner version would be to return early:

private bool isUserNameValid (String username) {

   int MIN_USERNAME_LENGTH = 6 ;
   if (username.length < MIN_USERNAME_LENGTH ) return false;
   
   MAX_USERNAME_LENGTH = 25 ;
   if (username.length > MAX_USERNAME_LENGTH ) return false ;

   bool isAlphaNumeric = username.isAlphaNumeric ();
   if (!isAlphaNumeric ) return false;

   if (containsCurseWords(username)) return false;

   return isUniqueUsername(username);

}



You see how that reduced the levels of indentation and how that made the code more simpler by returning early.

3: Convey Intent:

The third reason to write function is to convey intent, as mentioned before, functions can have a clear name that convey the intent, lets take a familiar example:

Dirty:

// check for valid file extentions
  if (fileExtention == "mp4" ||
      fileExtention == "mp3" ||
      fileExtention == "avi" ) {

// do something 

}

Clean:

private bool isValidFileExtention (string fileExtention ) {

return (fileExtention == "mp4" ||
        fileExtention == "mp3" ||
        fileExtention == "avi"  ) ;

}

A yet Cleaner way is to return early :

private bool isValidFileExtention (string fileExtention ) {

if (fileExtention == "mp4" ) return true;
if (fileExtention == "mp3" ) return true;
if (fileExtention == "avi" ) return true;
return false;

}

4: Do one thing:

That is the fourth reason to write a function, can you read a book without paragraphs, having function that do one thing can aid the reader even if the function is not reused, if you have a lot of logic, try to extract it into a function with a meaningful name. try to avoid functions that do more than one thing (side effect).

Our discussion about functions wouldn't be complete without talking about when to initialize variables, should you declare a variable at the beginning of the function and then use it later? should you initialize it just in time ? lets see with examples:

3.5 General rules:

3.5.1 Initialize variables Just-In-Time:

You remember how while reading code is that our brain is the compiler ? when you declare a variable in one line and use it 15 lines later, you are not helping the case, its always better to initialize variables just in time, compare the following two scenarios :

Dirty:

bool a = false;
int b = 9;
String s = "Sabir"

// save

// the 

// earth 

// from 

// hunger 

a = isItTrue();

if (a) {

   if (s.length > b ) {
   // do 
   
   // something 

   }

}


Clean:

// save

// the 

// earth 

// from 

// hunger 

bool a = isItTrue();

if (a) {
   String s = "Sabir";

   int b = 9;

   if (s.length > b ) {
   // do 
   
   // something 

   }

}

Well you might want your variables to have a wider scope, you sure as hell can do that, but make sure to initialize in time so that it makes reading the code easier, but declaring a variable at some line and then initialize it god know when line, is not a good idea.

3.5.2 Strive for 0 - 2 parameters in functions :

Functions with lots of parameters are hard to test, hard to understand and most probably its doing more than one thing, strive to have your functions with only 0 - 2 parameters.

Dirty:

public void saveUser (String username, String password, int age , bool sendEmail)

Clean:

public void saveUser (User user)

3.5.3 Watch out for flag Arguments, its a sign that your function is doing more than one things:

Dirty:

public void saveUser (User user , bool sendEmail) {

  // save user 

  if (sendEmail) {

    // email user 
  }

}

Clean:

public void saveUser (User user) {

// save user 

}

public void emailUser (User user ) {

// email user 

}

This way you don't have side effects, where saving the user doesn't actually send an email to the user and if other developers want just to save the user without sending an email, they can just use the saveUser() function.

3.5.4 Avoid very long functions and short functions:

Long functions are hard o digest, it reduces readability and might be a sign that the function is doing too much :

Rarely be over 20 lines Hardly ever over 100 lines No more than 3 parameters Robert C. Martin , “Clean Code”

4: Classes:

The second last piece of the puzzle in clean code, is to have clean classes. If you are working on an Object oriented language, classes can be a huge impact on maintainability and clarity of intent, in this section we will when to write classes and how to design them by discussing the great outline rule.

When to create a class:

The same way we have reasons to create functions, there are reasons to create classes as follows:

  1. New Concept : you write classes when you have new concepts, it helps abstract the real world and make your code more approachable. by creating well designed member variables and methods. ie. MeetingScheduler class. It can provide a high level of intent and where you can have well-named method.
  2. Low Cohesion: low cohesion means that the methods in your classes are not closely related, following the single responsibility principle you should aim for high cohesion in your classes, if your classes methods have low cohesion, then its a sign that you need to different classes.
  3. Promote Reuse: when you organize your code into classes, it promotes reuse, even you have your logic in big classes, its better to organize into smaller logic with smaller classes so that it can be use by another program.
  4. Reduce Complexity: ?when your write a class you solve a problem, and when the reader comes, he/she can reuse that class and trust that class you wrote solve whatever complexity that you intended to solve.
  5. Clarify Parameters: when you are passing a lot of parameters into a function (like the loose of bag parameters of a User for example: username, password, age ) creating classes for such data structures can clarify the parameters and make it easier to follow and understand, like creating a User class in this case.

Now lets talk about some guidelines:

4.1 Create Classes that have highly cohesion (high related responsibilities ) :

Lets take this example:

Low cohesion class:

Vehicle class with the following methods has low cohesion:

  • Edit vehicle options
  • Update pricing
  • Schedule maintenance
  • Send maintenance reminder
  • Select financing
  • Calculate monthly payment

You can see that the class is doing things that are related to Vehicle maintenance and finance, a better way to organize your classes is by making it have more cohesion:

Vehicle

  • Edit vehicle options
  • Update pricing

VehicleMaintenance

  • Schedule maintenance
  • Send maintenance reminder

VehicleFinance

  • Select financing
  • Calculate monthly payment

Now that you can see we have a higher level of cohesion by breaking down the class into different classes.

4.2 Avoid Very Generic Classes, its less cohesive and attract lazy developers:

When you have a very generic class, it attracts lazy developers to add code on top, examples for that are:

MyFunctions

That is a bad when because it invites every lazy developer to write their code here.

4.3 Avoid very Small classes, having two classes that heavily call each other methods is a sign to watch out for:

When you have two classes that heavily call each other, maybe its a sign that they should be one class, again rarely you find that but just use your best judgment when you think a class is too small.

4.4 Avoid primitive obsession and abstract into data structure classes:

Dirty:

private saveUser(String firstName, String lastName, String state, String phone )

Clean:

private void saveUser (User user)

Again, writing a class for user, helps the reader conceptualize, and it aids maintainability since if a new parameter is added, the method signature won't change.

4.5 Follow the principle of Proximity by Striving to make code read from top to bottom and keeping related actions together:

When the reader is reading your code, they read it from top to bottom, you often see that some function is calling another function, in that case try to the follow the proximity principle by placing functions that are related together, its understood that functions are called from different places, but whenever possible, keep related functions in a class together, lets take this example:

Clean:

private void validateCandidate(Candidate canidate) {

     validateCandidateData (canidate);
     
     if (!candidateMeetsRequirements()) {
         throw new CandidateDoesntMeetRequirementsException(" The Candidate Doesn't meet our standards. ") ;
     }
      approaveInterviews();
}

private void validateCandidateData (Candidate candidate) {
int MIN_YEARS_OF_EXPERIENCE = 5; 

if (canidate.yearsOfExperience <  MIN_YEARS_OF_EXPERIENCE ) 
throw new ExperienceYearsNotMetException(" The candidate does not have enough years of experience");

if (!canidated.isAwareOfCleanCodePrinciples )
throw new CanidateDoesntPracticeCleanCodeException ;

}

private bool candidateMeetsRequirements (){

return canidateHasExceptionalCV () || !noObviousRedFlags();
}

In the previous example you notice the function validateCandidate is calling the function validateCandidateData and how easy and convenient it was for the reader to get to read and check the code.

4.6 Strive for the Outline Rule, Collapsed Code Should Read Like Outlines:

Again, a picture is worth a thousands words:

No alt text provided for this image

Back to our metaphor of arranging your code like a book, and by following the proximity principle, try to apply the same concept in your code, keep related functions together, collapsed code should read like outlines.

5: Comments:

The last piece of the puzzle of clean code is comments, as mentioned before, try to favor self-documenting code over comments, use comments ONLY when code by itself is insufficient.

No alt text provided for this image

Now we are going to talk about Bad Comments and Good Comments.

5.1 Bad Comments :

The Category of Bad Comments has more than the good ones!

5.1.1 Avoid Redundant Comments:

They are simply redundant and its better to assume that the reader can read and know that specific programming language, lets take some examples:

Dirty:

int x = 1; // set x to 1

// default constructer

public User() {

}

// calculate total price 
private void calculateTotalPrice(){

// total price should be calculated

}

5.1.2 Avoid Intent Comments:

Most probably they can be explained in code by using the guidelines and principles we discussed earlier ! lets take an example:

Dirty:

// make sure the user account is deactivated
if (user.status == 2)

Clean:

if (user.status == Status.Inactive) 

5.1.3 Avoid Apology Comments:

Please don't apologize, Fix it before you commit/merge.

Dirty:

// I was too tired to refactor this

5.1.4 Avoid Warning Comments:

Please don't comment with warnings, Fix it before you commit/merge.

Dirty:

// See Paul to understand this code

5.1.5 Kill Zombie Code:

Zombie Code is a commented code, its not compiled and its not part of the production code but its still a code haunting us while reading the code, its a Zombie Code that should be killed.this mostly happen in two cases:

  1. Risk Aversion : when you think this code is too risky to remove
  2. Hoarding Mentality : when you think this code can be good for readers (Hint: its not )

Please remove zombie code, its noisy and it increases the Signal To Noise Ratio, if a code was used before and you are afraid to delete that it might be reused again, remove it, you can get from source control, and if you are not using source control, please start using it.

5.1.6 Avoid Divider Comments, they are mostly assign that your code is doing to much :

Divider Comments are comments are used to separate different parts of the logic, you might think its good but it can be avoided by breaking the long code into smaller functions. Lets take an example:

Dirty:

private void myVeryLongFunction(){

lots 

of 

code

// start verifying the candidate

verify

the

candidate

// end of verifying thecandidate

some

more 

code

5.1.7 Avoid Brace Tracker Comments:

Sometimes you find yourself using an if statement to check for some condition, if the condition is true, you want to perform some other logic, and sometimes this logic gets too long that you need to write comments to track the end of your statements, specially if you have different layers of them. Its a bad comment and it can be avoided. Lets take an example:

Dirty:

if (isValidLogin) {

//do 

//lots 

//of things 

//to

// log

// user 

//in

} // end of user login  

Its Cleaner if you extract this login logic into its on function, Clean:

if (isValidLogin) {

loginUser();

} 

5.1.8 Avoid Bloated Headers:

Sometimes you find some unnecessary headers on the top of files, these headers describes the author of this file, the creation date of the file and some other unnecessary comments, be DRY, this information already can be found in source control, and as longs as you can find that information, don't repeat it. In case a header is necessary, please look for the conventions on how to write a header for that specific language. Lets take an example:

Dirty:

//*****************************************************
// Filename: MyAwesomeClass.Java                      *
//                                                    *
// Auther : Jin Foong                                 *
//                                                    *
// Weather : It was very cold, and it was a public    *
// holiday !                                          *
//*****************************************************

5.1.9 Avoid Defect Log Comments:

If you ever fix a null point exception bug and it was, don't write comments to describe the situation, tickets and source control are the place for this and not the code. Lets take an example:

Dirty:

// Bug 187
// we weren't checking for null 

if (username != null ){

//do something

}

Good Comments:

Enough with negativity, comments can be good sometimes.

We can group the good comments into three categories:

5.2.1 Its okay to write //TO DO comments:

To Do comments can be a great help when you want to do something later, its okay to write them, but when you write them, its better if you and your team agree on some standards and some wordings so that its easily searchable or there are some tools that can find them for you if you follow some standards. Lets Take an example:

Clean:

// TO DO : refactor out duplication

5.2.2 Its okay to write Summary Comments, Only when it can't be expressed in code:

In rare cases, the code is not enough to convey the intent, and you might want to write a high level summary that might not be clear from the code, in that case its okay to write summary comments.

// generate custume emails

5.2.3 Its okay to write Documentation Comments, Only when it can't be expressed in code:

Sometimes the code or the api you are using might be a bit complicated and can't be expressed in code, in that case it can be a good idea to have a documentation source in your comments.

// see www.facebook.com/api

// some code that is unique to this api

Now we know all about the good and bad comments and to wrap it up, before yo write comments, ask yourself the following questions:

  1. Can I express this in code? If yes, then please do and be DRY.
  2. Am I just explaining a bad code instead of refactoring? If yes, then please refactor.
  3. Should this Simply be a message in the source control commit? If yes, then please be DRY.

Now all the pieces of the puzzle is complete and hopefully this can give you most of the directions you need in order to write clean code.

IMPORTANT: Before The End, I would like to mention a few notes:

The way I wrote this article is that I divided it into sections to cover different areas, most of the clean code guidelines are in separate, clear, numbered format, the intention from this is that, if you came across this article and you are doing code reviews, you can simply refer the author of the code to the article by mentioning the guideline number. for example, if you see the author is having a lot of deeply indented code, you can refer them to guideline number 3.2.1 and 3.2.2.

Also, Code is liability, Less is more, if you find this article useful, please do share it with your colleges, teammates , friends. Lets Strive for making all the developers write a clean code.

Credits to Pluralsight Clean Code, Writing Code For Humans Course. It inspired me to write this article.

Benny Skjold Tordrup

Software Architect at Visma Software A/S

3 年

Any thoughts on organizing classes and files? Especially in C++? Should it be one class - one file (in C++ a .h and a .cpp file), or is it considered clean code to have multiple class definitions in one .h file and implementation in one .cpp file?

回复
Denis kisina

Senior Software Engineer - Backend // Building Scalable, Data-Intensive Distributed Systems & Cloud Infrastructure

4 年

Thank you for this awesome article. I love how well explain and broadness of topics covered.

回复
Momen K Tageldeen, PhD

Senior Engineer @ AMD | ASIC for AI

5 年

Great effort! It also good to find an article that compiles all relevant aspects of a topic. It is even better when said article is as clean as it can be. (pun intended)?

回复
Afiq Asyraf Ahmad Suhaimi

Senior Development Specialist | DevOps | Java | Kubernetes | Unix | Docker

5 年

Its really an interesting article and i love the way you explain it.. Keep it up

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了