Clean Code 101

Currently, there’s no single definitive explanation of “What is Clean Code?”, since people interpret this differently. However, most people seems to agree with several points about clean code:

  • Clean Code is meaningful, easy to read and understand.
  • Clean Code is easy to maintain, develop and scale.
  • Clean Code is Best Practices(or at least a big part of it).
  • Clean Code is easy to test and debug.

From the 4 points above, now we can quite understand the common grounds of Clean Code, and thus we can start writing Clean Code, keeping the 4 points above in mind, as our starting point.

Meaningful, Maintainable, Testable, Consistent, and Best Practices.

So, since we want to write Clean Code, we need to know how to achieve the 4 points above. First of all, when writing code, whether it’s a variable, function, class, comments or documentation, we need to make sure it fulfills at least the first point. It should be meaningful, why it exists, what it does, how to use it, and when to.

Meaningful Name

For example, there’s 2 different variable with the same purpose:

int n;------------------------------ VS ------------------------------int count;

By looking at the variable name alone, we can immediately tell that the count variable must be used to count something(or used for counting, whichever works). The variable n literally tells nothing but it’s type, which is an integer.

This also applies to method/function names. Another example(taken from my PPL implementation and test code):

Example method snippet taken from register usecases
Example test method snippet taken from register testcases

Both the above methods are meaningful, and fully describes what the method is doing. The first one creates a model for user binding. The second one tests the annotator user creation functions with account binding type “FB”, profile image(shortened as profile since the context would immediately refer to profile image), and bank info model exist.

Even if the method only acts as a wrapper for extra layer and functions as a helper method, using meaningful names will help a lot in understanding and using the method.

Example server views function

The above method handles and wraps the request for 3 different API type. It uses the api_type variable to choose which API needs to be called. Pretty straightforward.

Easy to Read, Easy to Test.

For example, there’s 2 different class with the same purpose:

class objhuman{ private String n;private String j;private int a;}------------------------------ VS ------------------------------class Human_Object
{
private String name;
private String job;
private double age; // In year
}

I had a slight discomfort just because of writing the first example at 1 am. It would took people minutes to understand the first example, but only seconds(or even immediately) for the second example. Readability improves productivity, yours and whoever reads and uses the code. It also makes debugging a lot easier.

If we found a bug related to the class above, we can directly tell which part of the class caused the error. For example, a test asserts that a human age must be in Integer. When the test returns an error, we can directly look to the class data fields, identifies that the “age” variable is double instead of int, then changes it. In a matter of seconds.

Another example:

Another example method snippet taken from register usecases

Let’s say we want to test the exact above code. We want to check whether the method successfully creates a new ProjectSupplier model and stores it to database. All we need to do is to setup the test, call the method, then call the model data(either via own-made functions or directly call ProjectSupplier.objects.get(), but in this example I use my own made function which does the same thing but it’s clean and better by implementing modularization).

Another example test method snippet taken from register testcases

Simple. After calling the model, we can directly assert each of it’s value(since it’s also used to test inserted value, if we want to test whether the method succeeds we can just assert it’s status). The above test works, by the way.

Easy to Understand, Maintain and Scale.

For example, there’s 2 different function doing the same thing:

function f(String lalala){
int c = 0;
for(String s : lalala){c++;}
return c;
}
------------------------------ VS -------------------------------function countStringLength(String str){
int count = 0;
for(Character c : lalala){
count++;
}
return count;
}

Yes, this looks like a very simple function that counts the length of a given string and returns the count. However, if we were using the first example code, we’d be pretty confused with the function and variable names, such that it’ll took a long time just to understand what’s exactly the purpose of this function.

The second function on the other hand, it’ll took just a second(or a fraction of it) to understand and use the function, even without any documentation or something similar.

Let’s say that we finally finished 2 different app, both with the first and the second example in the code. The first app’s future developer will have little to worry since the example is easy to fix incase of an error. But, if there’s a lot of them(and they all are badly written) then it’ll become a nightmare.

The second app’s future developer, on the other hand won’t have too much trouble maintaining and scaling up the app, since the codebase is already clean, understandable, thus easy to maintain. For example, the developer wanted to “upgrade” the function by changing the maximum length it can count(2,147,483,647 is the default max digit that integer can store), to the unsigned long datatype(4,294,967,295 max digit). All the developer needs to do is replace the int with ulong(depends on language, this is just an example). Then do some testing, and it’s done.

Another example in my PPL project:

Currently, our project has 3 user types: Admin, Annotator and Project Supplier. Let’s say we want to add a new user type: The “Supplier Network”. This new user type does the following:

  • Stores a list of Project Supplier affiliated, or working with the Supplier Network. It’ll be a Many-to-One relationship.
  • Can show which Project Supplier has an open project for annotator to apply and work on.
  • Can also show a brief description, focus, etc.

To add this new user type, we can do something like the following steps:

  1. Create a new model called SupplierNetwork, and it’s field data.
  2. Create a Many to One relationship with ProjectSupplier model.
  3. Write a method to create a SupplierNetwork model(like the examples from previous screenshots). Or create a test for creating a SupplierNetwork model first, if you’re doing TDD. Then implement the method.
  4. Work upwards, to the usecases section(everything related to data processing is handled here, the repository section purely handles CRUD) where the data is processed such that it can be directly passed. Remember, Test first if you’re doing TDD.
  5. Finally create a view at the server section(the section where the URL mapping, request handler, etc. exist) that enables us to create a new SupplierNetwork model. Remember, Test first if you’re doing TDD.

And we’re done. Since we already implemented clean code we don’t need (or would) modify any other part of existing code, purely adding a new implementation. This also comply with several Design Principles, namely “Open Closed Design Principle”, and “Favor Composition over Inheritance”.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store