“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live”
John Woods
If reading a few lines of code makes your head boil or incites a sudden urge to smash your computer screen—you have spotted bad code. The extent to which a code is bad can be determined by the number of curse words coming out of your mouth per minute.
Bad code is not always faulty. Most of the time it works—perfectly. However, it is not sustainable in the long term. Often times the developer who has written the code can not understand it properly.
Clean coding is an important element of software craftsmanship. When you are working in a software team, writing clean code makes the lives of your team members easier.
If you want to learn the art of writing clean code, look no further. This comprehensive clean code cheat sheet will take you through all the industry-standard clean coding principles. Let’s get started.
1. KISS—Keep It Simple Stupid
“Simplicity is prerequisite for reliability.”
Edsger W. Dijkstra
Dijkstra’s shortest path algorithm has come a long way. In pathfinding applications, it serves as a fundamental and reliable algorithm. It is proof that simple algorithms can solve some of the most complex problems.
KISS—one of the first design principles, preaches coding simplicity. It is one of the general rules that applies to various domains. It means writing code that gets the job done in the simplest manner. It does not suggest using trivial methods to solve problems or finding the shortest possible solution.
Simpler SDLC
In general software development, the goal of the KISS principle is to reduce complexity throughout the software development lifecycle (SDLC). From requirement gathering to production, a simpler development lifecycle is easier to maintain in the long run.
KISS Examples
Using web development frameworks like Django, Spring Boot, and .NET can simplify the web development process when building enterprise-scale applications. Their built-in functionalities can greatly improve project efficiency. But using web frameworks to build a website for a small e-commerce business makes no sense. It is better to use website builders like WordPress, Wix, or Squarespace.
Similarly, when working in Artificial Intelligence (AI), it is often said that there is no need to train a complex AI model when a simple if-else statement can do the job.
2. DRY—Don’t Repeat Yourself
Code duplication is frowned upon in the software community. The DRY principle states:
“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.”
Every clean code cheat sheet must contain the DRY principle. Clean coding is not possible without DRY programming. If the code is not DRY, then it is WET—We Enjoy Typing or Write Everything Twice.
If a software program contains duplicate code blocks, it is better to make functions and call them twice, as duplication can lead to potential errors.
“Repetition is the root of all software evil.”
Martin Fowler
DRY Examples
A programming library works on the DRY principle. It can be imported into a program and only those functionalities are invoked which are required for execution.
Many programming frameworks are designed by following the clean coding DRY principle. Like a popular front-end framework React ensures DRY programming by making components and state variables reusable.
3. SRP—Single Responsibility Principle
Introduced by Robert C. Martin, SRP is one of the five SOLID design principles which serve as a foundation for coding maintainability. It allows the developer to write code that can be managed, scaled and extended.
Single Responsibility Principle states:
“A class should have one and only one reason to change, meaning that a class should have only one job.” Not only that, it
SRP Explanation
In software development, requirements change quite often. In the initial phase, both the client and the development team are unclear about the final product. This is where SRP plays a vital role.
SRP ensures that each module, class, and function of a software program works independently. It urges developers to rewrite the dependent functions as independent methods. Developers must write small functions that perform only a single operation and prefer fewer arguments.
SRP works well with popular design architectures like microservices.
In large-scale software development, complete independence is difficult to achieve. That should not stop developers from trying to split the project into small portions like microservices. If the requirements change, only a small portion of the project should be updated rather than updating multiple modules. There must be no or minimum direct dependencies among the modules.
If dependencies are unavoidable, try to use dependency injection to add them. Dependency injection is a design pattern that allows the creation and usage of dependent objects between two or more classes.
SRP Application
Java Persistence API is a great application for SRP because it only has one responsibility i.e. to apply the standardized Object Relational Mapping (ORM) model for managing persistence in relational databases.
SRP Code Example
Let’s say that you are required to implement an automated stock trader. A stock trader must have three methods: buy, sell, and hold. A newbie developer would be tempted to design a Stock class with these three responsibilities. The SRP principle prohibits a class from having multiple responsibilities. In this case, it is better to separate the three methods into three separate classes namely Buy, Sell, and Hold. Consider the code snippets below:
Before Applying SRP—A Single Stock Class With Multiple Responsibilities
class Stock {
void Buy(string name, float price){
// Buy stock code implementation
}
void Sell(string name, float price){
// Sell stock code implementation
}
void Hold(string name, float price){
// Hold stock code implementation
}
}
After Applying SRP—Three Classes With Single Responsibility
// Responsible for buying stocks
class Buy {
void run(string name, float price){
// Buy stock code implementation
}
}
// Responsible for selling stocks
class Sell {
void run(string name, float price){
// Sell stock code implementation
}
}
// Responsible for holding stocks
class Hold {
void run(string name, float price){
// Hold stock code implementation
}
}
// Responsible for executing stock methods only
class Stock {
Buy buy;
Sell sell;
Hold hold;
void Buy(string stock, float price){
// Call run method from Buy class
buy.run(stock, price);
}
void Sell(string stock, float price){
// Call run method from Sell class
sell.run(stock, price);
}
void Hold(string stock, float price){
// Call run method from Hold class
hold.run(stock, price);
}
}
There are many other ways of implementing the same functionalities. We can make static methods or even prefer polymorphism. With SRP, there are no strict rules as to how a class must be designed. Developers can have conflicting ideas about class responsibilities, which makes it tricky to implement SRP.
4. Oh FRIC! (Fragility, Rigidity, Immobility, Complexity)
Bad software design smells and stinks.
Code smells is a term coined by Kent Beck to possibly identify bad code. It has various indicators like fragility, rigidity, immobility, complexity, needless repetition, and viscosity. Any clean code cheat sheet must include these indicators. These are some of the general rules for clean coding.
What is Fragility?
With more fragility, the program is likely to crash by introducing a small change. Software programs must be robust enough to accept new changes without disrupting the existing functionalities. If a critical software program breaks, it can turn out to be a financial or humanitarian disaster.
What is Rigidity?
Code smells rigidity. Rigidity means that adding new code or making changes to the existing code would have a domino effect on the entire system. If modules have a high logical dependency then various modules must be updated to make a small change.
What is Immobility?
Immobility refers to the inability of code reusability. The program cannot be divided into small modules. Hence the code cannot be reused.
What is Complexity?
Needless complexity refers to unused functionalities in a program. A developer may add a feature that remains unused throughout the software lifecycle. This can add undesired problems while testing related code.
Minimizing the extent of these indicators can make your program fragrant.
5. The Boy Scout Rule
Wouldn’t it be nice if you don’t have to waste time fixing someone’s bad code? It is possible if every developer follows the boy scout rule.
The boy scout rule is a common entry in any clean code cheat sheet. It states:
“Always leave the code you are editing a little better than you found it.”
Anti-Code Smells
It is one of the best remedies against the smelling code. With continuous development, software quality may degrade, and developers have to stop working on new features to clean the codebase.
Boy scout rule enables software teams to improve the quality of the source code structure over time. With each commit, every team member makes a small improvement which bears fruit in the long run.
6. POLA—Principle of Least Astonishment
Developers hate surprises. A surprise means a potential error in the system.
Principle of Least Astonishment or Principle of Least Surprise is another clean code design principle that ensures the minimum risk of software failure.
POLA Explanation
Each software module or functionality should only do what it is made for. For example, if on pressing the checkout button the system places an order without asking for the delivery address or the payment method. Well, in this case, the end-user might be happy but the developer would find himself in hot waters with the manager.
POLA recommends rigorous testing on each module. Each module should work independently based on SRP.
7. Follow a Consistent Naming Convention
“You should name a variable using the same care with which you name a first-born child.”
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Different programming languages have different naming conventions for variables, functions, and classes but a few clean code conventions are the same.
Meaningful names
Variables must have descriptive and unambiguous names. Self-explanatory variables are always preferred. The variable name must indicate its purpose. If an array contains names of different cars, then it must be called cars, not array, or arr, or arrayofcars, etc
It is better to avoid variable or function names with only one character or incomplete words. For example, to store a dog’s name, you can simply write dogname or dog_name (referred to as Snake case), instead of d, dname, or dgname.
Some guidelines prefer Camel case writing as well e.g dogName or Pascal case e.g DogName.
Constants are written in all caps.
All major programming languages have their own coding style guide which must be followed to ensure coding consistency.
8. Write Good Comments—Take Your Time
“Redundant comments are just places to collect lies and misinformation.”
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Code commenting is massively stressed upon in the software industry. A developer should think about his peers while writing code and add appropriate comments so they may understand the code later.
What Makes Comments Good?
Relying too much on the comments indicates bad coding practices.
The code should be easily understandable and the comments should be compact and precise.
Avoid redundant comments because they indicate code duplication.
Don’t add comments for each line of code instead target a block of code like a function, class, or module. Summarize the code block in simple words using block comments.
Block Comment Example
Consider the following Person class:
class Person
{
/* Person class constructor.
Initialize the age and height property of the Person object.
*/
public Person(int personAge, int personHeight)
{
age = personAge;
height = personHeight;
}
public int age { get; set; }
public int height { get; set; }
}
In the above example, we have only written a block comment for the Person constructor and ignored the comments for the age and height’s getter and setter methods because they are quite obvious.
Lastly, don’t forget to remove the absurd comments (“HIIII” or “Helloooo”) added during debugging.
9. Structure Your Code Aesthetically
Proper code structuring is one of the most important clean coding practices. It includes many components like the properly formatting internal structure of a function or a class, white spacing, blank lines, indentation, and maximum line length, etc.
Indentation
Programming languages have different indentation formats. Some define an indent level with 4 spaces and some with 8 spaces.
Indentation takes the center stage in programming languages like Python that do not have coding parenthesis. Be careful not to break indentation.
Control Flow—If-else Conditions & Loops
In all languages, it is important to maintain indentation levels for each control flow level to improve code readability. For example, nested for loops and if-else statements must be properly indented.
Bad Indentation Example
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (i > 5)
{
//print Hello
cout<<"Hello";
}
}
}
Good Indentation Example
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (i > 5) {
//print Hello
cout<<"Hello";
}
}
}
Maximum Character Limit
Most programming languages support 80 to 120 characters per line. It is always better to break down large chunks of code into multiple lines.
Blank lines are also used to identify separate code functionalities and improve code readability.
Separate Multi-Threading Code
In the case of parallelism, separate multi-threading code must be managed. It is easy to lose track of multi-threaded code. Try to make the threads as independent as possible and avoid logical dependency.
10. Error Handling
Error handling is another important tip in the clean code cheat sheet. It is often ignored by the developer community. It can prevent the software system from complete failure in case of undesired bugs and errors.
A code that throws managed exceptions is better than the one which crashes completely.
Try-Catch Block
For proper error handling, use try-catch-finally block. Its implementation can vary for different programming languages. The aim is to catch unwanted exceptions like dividing a number by zero or if a file is not found. These exceptions may cause a program to crash.
Code Template
try {
// A protected code block that may throw exceptions
} catch (ExceptionName e) {
// Catch exceptions and print relevant error message
// A single catch block can only catch one exception
// Multiple catch blocks can be used to handle multiple exception
} finally {
// This block can free up any occupied resources
// like closing the database connection or closing the file stream
}
Error handling Example
All HTTP error codes (like 404 Not Found, 500 Internal Server Error, etc) must be handled by the developers to display appropriate error messages to the client, instead of a system crash.
Another common exception handling example is connecting to a database server. The server may be inaccessible due to a loss of internet connection. If the program is running clean code, all exceptions are redirected suitably.
11. Refactor Code Again & Again
Code refactoring is one of the most potent clean coding techniques. It is a systematic and incremental process by which developers can improve the quality of the code without changing or adding any new functionality.
Improve Code Efficiency
It allows the developer to restructure the code and make it more efficient. It can reduce code duplication and remove dead code. In doing so, dirty code which results from tight deadlines, inexperienced developers, and quick fixes, is automatically eliminated.
A simple code refactoring exercise can be removing scattered instance variables and maintaining them at the start of the code after assigning descriptive names. Or aligning UI elements and ensuring consistent button and font sizes.
Testing Refactored Code
Code refactoring and code testing go hand in hand. When a portion of the code is refactored, it must be tested to ensure clean execution. The refactored code must pass all existing test cases.
A well-organized code is easy to understand and maintains subsequent changes.
12. Choose A Good IDE
Integrated Development Environment or IDE ensures many clean coding practices automatically.
An IDE is a collection of various development tools like a text editor, compiler, programming languages, web frameworks, debugging tools, testing frameworks, and intelligent code completion system―in some cases.
IDE Advantages
IDEs guarantee clean coding by offering syntax highlighting, catching syntax errors, and indenting source code automatically.
IDEs can write CRUD operations and generate unit tests, allowing developers to save a lot of overhead time and focus more on writing the core code.
If you are working on a traditional software development project, you cannot live without an IDE. IDEs can manage project directories. They can connect with version control systems like Github and Gitlab from within, allowing quick and easy code versioning.
IDE Examples
Developers can choose an IDE as per their project requirements. Like Microsoft Visual Studio is compatible with C# programming and PhpStorm works well for PHP programming.
BONUS—Know When To Be Inconsistent
The clean code tips discussed above might not fulfill the criteria of a good code in certain cases. If applying the clean coding techniques add more complexity to the existing system, then it is best to avoid them.
Maintaining Legacy Systems
When working on legacy software systems, developers can observe outdated coding conventions. In such cases, it is better to stay consistent with the old coding guidelines, rather than adapting to the modern coding standards which can add more complexity.
If the system is using deprecated methods in the entire application then there is no point in removing these methods or updating them to the latest version (unless approved by your manager).
In some cases, if it is not possible to apply the DRY principle, then it is time to go WET. Code duplication may save countless development hours instead of wasting time in finding a way to avoid it.
Concluding Thoughts
“If you want to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to read”―Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Mastering clean coding skills require practice and patience. New developers can learn quickly by getting their codes reviewed by senior developers. They can often face harsh criticism for writing dirty code―as they should. The key is to take criticism constructively with a desire to learn and improve.
The clean coding techniques shared in this guide can be a great start for improving the quality of your future coding endeavors.
References
- https://brilliant.org/wiki/dijkstras-short-path-finder/
- https://reactjs.org/docs/thinking-in-react.html
- http://cleancoder.com/products
- https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design
- https://microservices.io/patterns/decomposition/decompose-by-business-capability
- https://jcp.org/en/jsr/detail?id=338
- https://www.kentbeck.com/
- https://visualstudio.microsoft.com/
- https://www.jetbrains.com/phpstorm/