• 6 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/7/22

Write Self-Documenting Code

Use Naming Conventions

Apply Simple Naming Conventions

Naming conventions are a set of rules that make code more readable. It's simple—all code is full of names: variables, files, directories, functions, etc., and each of these represents a particular component. So give them as clear a name as possible to show what they do.

As you’ll see in this section, giving code components a clear name helps the team save time and work together more efficiently. Therefore, it’s an important part of ensuring professional quality. The most important thing is to choose the way you write your code and a naming convention and to stick to this throughout the project.

Now let’s look at some rules that make this task easier.

Remember that the names given to your classes, functions, and other variables should make it possible for an external developer to understand which component does what simply by reading their names.

Therefore, the names should be human-readable and make sense.

The names you choose should express an intention.

To illustrate this, consider the following code:

public double [] solPol2(double a, double b, double c){

double tab[] = {};

double d = b * b - 4.0 * a * c;
   if (d < 0.0) {
      System.out.println("No real solutions");
   } else if (d > 0.0) {
      return tab[(-b - Math.sqrt(d)) / (2.0 * a), (-b + Math.sqrt(d)) / (2.0 * a))] ;
   } else {
      Return tab[-b / (2.0 * a)];
   }
}

This code is pretty difficult to read.

  • What does  tab  do?

  • What are  a  ,  b  , and  c  ?

  • What is  d  for? 

You can make it a bit clearer like this:

public double [] solutionPolynomialDegree2(double coeffA, double coeffB, double coeffC){

double solutions[] = {};

double discriminantPolynomial = coeffB * coeffB - 4.0 * coeffA * coeffC;
   if (discriminantPolynomial < 0.0) {
      System.out.println("No real solutions");
   } else if (discriminantPolynomial > 0.0) {
      return solutions[(-coeffB - Math.sqrt(discriminantPolynomial)) / (2.0 * coeffA), (-coeffB + Math.sqrt(discriminantPolynomial)) / (2.0 * coeffA))] ;
   } else {
      Return solutions[-coeffB / (2.0 * coeffA)];
   }
}

What did I do here?

  • solPol2  is a function for calculating the solution to a polynomial of degree 2, so it can be given the name  solutionPolynomialDegree2  .

  • a  ,  b  , and  c  are the coefficients of the polynomial, so a clearer name for them is:  coeffA  ,  coeffB  , and  coeffC  .

  • tab  is a table containing the solutions to the equation, so you can call it  solutions .

This makes the feature readable and easier to understand without having to add too much code.

Choose an Existing Convention, Hungarian Notation

First, you should know that there is a whole range of conventions for standardizing code.

For example, every word in the camel case starts with a capital letter, except the first, which starts with a lowercase letter (see below). It’s a popular naming convention in JavaScript and C# developer communities.

const passwordHashed ;

Another typical example is the pascal case convention, where words are linked without spaces and each word starts with a capital letter. PHP and Java communities use this convention.

String PasswordHashed ;

Finally, you might also come across the snake case convention, in which an underscore “_” separates each word. Ruby and Python developers use this.

password_hashed = "$2$aefbofnbqofiudjbqodsihazfeo"

In addition to following the previous advice, it's also better to use verbs for your class methods.

This might seem very complicated. However, try using the Hungarian notation at first to begin implementing good naming conventions. It’s based on the camel case with a few extra rules, and it’s a simple way of creating readable and consistent names.

In this convention:

  • The name is typed, followed by a qualifier. These two identifiers should be separated from the rest by an underscore or a difference between lowercase and uppercase.

  • The qualifier ensures consistency between names of data of the same type.

  • Very short words are used for simple types to allow complex types to be built.

This table provides a few examples.

Qualifier

Type

Example

i

int

int iNoCars

str

string

string strOwnerName

b

bool

bool bHeadsOrTails

This type of notation has been around for a while and is subject to debate, but it can still serve as a basis for variables and functions naming conventions. So feel free to adapt it to your project and needs!

Now that we've gone over some general points, let’s focus on the readability of your classes and functions in the next section.

Improve the Readability of the Important Parts of Your Code

Before looking at classes and functions in more detail, try adopting this general rule: before each major element (file, class, function, etc.), add a description in a block comment. You can format this comment to suit its purpose and explain how the following code works.

There are several tools that can generate automatic documentation if you correctly formulate comments.

The Example of Javadoc

For example, Javadoc inspects Java code to produce documentation using the command line or the Eclipse IDE.

This tool uses naming conventions (now you see how it links up ) to produce documentation in HTML format.

Now you’ve had a brief introduction, let’s move on to classes and functions.

Name and Document Your Classes

Classes are important as they constitute the high-level logic of your applications. Therefore, it’s essential to learn how to write them correctly.

First, you'll want to maintain a simple and consistent organizational structure:

  1. Constants to be used written in capitals (if applicable).

  2. A list of variables.

  3. Then, the public functions.

  4. And finally, the private functions, if necessary.

You can then add a description as you saw in the previous section.

This provides several advantages:

  • It improves the readability of your code.

  • It makes it easier to automate documentation.

You'll see two classes that follow these rules to make documentation easier in the two examples below.

/**
*  Represents an app user
*
*  @author Francis
*  @version 1.1
*/

Package app;

import app.Password.Class;

public class User {
  private final String name;
  private final Password password;
  private final Email emailAddress;

  public User(final String name, final Password password, final Email emailAdress) {
      this.name = name;
      this.password = password;
      this.emailAdress = emailAddress;
  }

  public String getName() {
      return name;
  }

  public String getPassword() {
      return password;
  }

  public Email getEmailAddress() {
      return emailAdress;
  }

  public void setName(final String name) {
      this.name = name;
  }

  public String setPassword(final String password) {
      Password.hashPassword(password);
  }

  public void setemailAdress(final String emailAddress) {
      this.emailAddress = emailAddress;
  }
} 

In this example, you can see a comment before the User class that describes what the class represents (a user of the application) and the author and the class version.

No comment is needed in the class, as all names are self-explanatory and clear.

Package app;

/**
* Defines methods to encrypt passwords
* Use the Bcrypt tools
*
* @version 1.1
*/

public class Password {
  
  /**
    * Options for the Bcrypt algorithm
    */
  private static final int WORKLOAD = 12;

  /**
    * Used to generate a string representing an account password.
    *
    * @param password_plaintext The account's plaintext password as provided during account creation,
    *
    * @return String - the bcrypt hash password
    */
    public static String hashPassword(final String passwordPlaintext) {
      final String salt = BCrypt.gensalt(WORKLOAD2);
      final String hashedPassword = BCrypt.hashpw(passwordPlaintext, salt);

      return (hashedPassword);
  }

  /**
    * Used to verify a computed hash from a plaintext with that of a stored hash from a database.
    *
    * @param password_plaintext The account's plaintext password, as provided during a login request
    * @param stored_hash The account's stored password hash, retrieved from the authorization database
    * @return boolean - true if the password matches the password of the stored hash, false otherwise
    */
  private boolean checkPassword(final String passwordPlaintext, final String storedHash) {
      boolean passwordVerified = false;

      if(null == storedHash || !storedHash.startsWith("$2a$"))
          throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison");

      passwordVerified = BCrypt.checkpw(passwordPlaintext, storedHash);

      return(passwordVerified);
  }
} 

In this example, there are more comments, as the functions are a bit more complicated. As you can see, there's a comment explaining the role of the constant WORKLOAD.

There is also a comment for the  hashPassword  method and the  checkPassword  method. You can see two tags in these comments, as well as the descriptive text:

  • @param  followed by the method parameter name—this is a tag in Javadoc that indicates that you’re describing a parameter.

  • @return  followed by a type of variable: this is another Javadoc tag that indicates that you’re describing a method return value and its type.

Name and Document Your Functions

To improve the readability of your functions, you can use the following rules as a guide.

Firstly, we’ve already talked about the importance of naming—and naturally, this rule also applies to functions. Choose function names that describe what they actually do, even if it means that the name has to be fairly long.

Also, it's better to maintain the same vocabulary for your function names, such as  addProduct  ,  deleteProduct  ,  updateProduct  ,  createProduct  ,  addProductToShoppingCart  .

This will considerably improve the readability of your code and help you think about what you’re doing as if you were telling a story.

Next, try to keep arguments to a minimum, as they complicate reading and make tests difficult.

As you’ve seen, you can always add a comment for functions that require a more detailed explanation.

Finally, it’s a good idea to get rid of functions that aren’t called anywhere to improve the consistency of your code and make it easier to understand.

Format Your Code!

Of course, the code you produce needs to work—that’s the minimum expected of a developer! But there’s also another essential aspect, which is communication. Developers often overlook this for various reasons, but you should be constantly aware of it. Well-written code is readable code.

Let’s look at a few simple rules to make your code operational and professional.

Vertical Formatting

The vertical arrangement of your code significantly impacts its readability.

It’s a good practice to leave a blank line between code blocks with the same scope to separate them from the rest of the code. This will keep sections of code with a similar functional meaning together.

package app.school ;

public class Student {
   private Name name;
   private int username;

   public Student(String firstName, String lastName, int n) {
      name = new Name(firstName,lastName);
      number = n;
   }
   
   public void changeLastName (String s) {
      name.changeLastName(s);
   }

   public void changeFirstName (String s) {
      name.changeFirstName(s);
   }

   public void changeNumber (int n) {
      number = n;
   }

   public String toString() {
      return (name.toString() + "\nNumber : " + number);
   }
}

In this class:

  • Variables are declared together in the same block.

  • Methods are separated with a line in between each one to make the code readable and easy to understand.

Here’s the same code with no rules. What do you think of it? It’s much less clear, isn’t it?

package app.school ;
public class Student {
private Name name;
private int username;
public Student(String firstName, String lastName, int n)
{
name = new Name(firstName,lastName);
number = n;
}
public void changeLastName (String s) {
name.changeLastName(s);
}
public void changeFirstName (String s) {
name.changeFirstName(s);
}
public void changeNumber (int n) {
number = n;
}
public String toString() {
return (name.toString() +
"\nNumber : " + number);
}
}
Horizontal Formatting

To improve the horizontal readability, keep the lines of code as short as possible and use spaces to provide clarity for operators.

To illustrate this point, here’s a classic example of a selection sort.

public class Sort{

   public static void sort(int[] table){
      int i, j, key;
         
      for (i = 1; i < table.length; i++){
         key = table[i];
         j = i;

         while ((j >= 1) && (table[j - 1] > key)){
            table[j]  = table[j - 1] ;
            j = j - 1;
         }

         table[j] = key;
      }
   }
}

Another important point is indentation. Its purpose is to provide a clear hierarchy of statements within a code block. Don’t forget to use it! Without clean indentation, your code won’t be human-readable.

Certain tools can help you with this, such as Beautify, an extension for Visual Studio, which automates  JS, HTML, CSS, and JSON code indentation. What’s more, you can even configure the indentation!

Let’s Recap!

In this chapter, you saw how to write self-documenting code, i.e., code that is written and organized in a way that’s easy for humans to understand. You learned that:

  • Naming conventions provide greater readability and consistency in the data names used.

  • You have to be particularly careful with objects and functions. These are important components that need to be written with care and attention. 

  • You can use certain formatting and factoring rules to make code pleasant to read and professional.

In the next chapter, you’ll see how to make writing your code documentation easier.

Example of certificate of achievement
Example of certificate of achievement