clean code principles summary


What is clean code?


Code which is readable, maintainable, testable and elegant. 




Why we should care? 


Because there is no such thing as write only once code. Over the time, software is like to change. There will be bug fixes, new features, new requirements and business changes. 




Why we should strive for clean code? 


  • Well Structured Code is easy to understand.
  • Better use of one’s time - for author and fellow developers
  • Saves Costs by Saving time - It is compound effect.
  • Easier Onboarding of new members.
  • Easier Debugging and efficient maintenance.  
  • It feels good and confident.


Of course bad code can be cleaned up. But it’s very expensive




Another quote


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




Clean Code Rules and Examples


Naming 

  • Use  complete , descriptive, understandable names for methods and variables. 
  • Use CamelCase - naming convention
  • Use names according to the context
  • Eg.  public String addComment()  instead of public String addcmt()
  • Eg.   for(String name : names)   instead of for(String x : xd) 
  • Names should be understandable. Write names as you speak it.
  • Eg.  public fileAgeInDays;  instead of int age;  or int a;
  • Be Consistent -  don’t use  get() in one place and fetch() in other.
  • Use Nouns for variables, parameters. Use Verbs for Methods. 


Avoid

Prefer

public  void priceIncrease()

public  void incrementPrice()

public  void lengthValidation()

public  void validateLength()



You should name a variable using the same care with which you name a first-born child.



Comments


  • Comments are hard to maintain and they are almost out of date. 
  • The code is the best documentation.  The code should read like a prose.
  • Don’t be redundant
  • Avoid unnecessary comments.
  • Use comments only as a clarification.
  • Don’t comment code. Delete it.  If needed retrieve from VCS.
  • If you are required to comment the code, consider refactoring it. 


Comments Rules

  • Most of the time – comments lie
  • Code is updated or moved, but not the comments
  • Comments do not make up for bad code. If code is unclear, rewrite the code.



Good Comments 


  • Can be used for TODOs but should only be temporary
  • Can be used for clarification like input format or clarification
  • Can also be used for warnings or consequences




Bad Comments

  • Noise comments -  about what code already speaks
  • Do not journal in comments. Use commit messages. 



Functions 


  • Functions length  -  The ideal length of functions should not be greater than 15.  Sometimes it can be more but should be clean to understand.
  • Function Arguments -  Three or less. If more than three, consider refactoring it into a class.  
  • Proper Indentation is must. It makes the code easier to read. Always format the code in IDE after writing.      
  • Keep lines Short. Don’t scroll horizontally.  
  • Functions should do exactly one task.  If a function does multiple tasks, split the tasks one per each function. 
  • Do not return null. Always return optional or empty collections.
  • Fail fast and return
  • Consider the following example

                                                                                                                 

public void validateAndSave()

public void  validate()

public void save()

 


Classes 


  • Same applicate to class as well. Each class should have single responsibility.
  • All methods must be written in scope of the thing class is About. Example - User class, all methods must be about user.  It should not contain methods about other class.
  • Keep Utility methods and variables private except in some cases. 
  • Expose only necessary variables through getters and setters.
  • Use access levels properly - public , protected and private 
  • Objects expose behavior and hide data. Conversely, data structures expose data and lacks of (significant) behavior.
  • Don’t create unnecessary Objects.  Use singleton pattern wherever applicable.





Constants 


  • Consistent Naming conventions  with upper case letters separated by underscore for Constants like  API_KEY   instead of  APIKEY
  • Don’t hard code. Define constant variables instead of  hardcode them. It will also be easier to change if used in multiple places.
  • Use named constants instead of magic numbers.
  • If you want to have restricted set of values, use enums  instead. 


Eg.  Instead of 


If (7 == weekDay){ // some code}


Use 


const int SUNDAY = 7;

if(SUNDAY == weekDay){//some code}




Logging  

  • Anyone who has ever laid their hands onto production code for debugging has yearned for more logs at some point in time.
  • The importance of logs can not be over-emphasized in development in general and maintenance in particular.
  • Avoid excessive logging, log only what might need in troubleshooting. 
  • A good practice is to log at every branching  and choose log levels wisely. 
  • Use external tools for tracing, aggregation and filtering  of log messages  for faster analytics. 



Refactoring 

  • If you don’t have unit tests, you don’t know what your code does. 
  • Refactoring rules
    • Bring the code under test.
    • Then safely and confidently update your code.
  • Be consistent. Follow conventions. If you do something in a certain way, do all similar things in the same way.


TDD Development cycle 


1.Add a test from requirements gathered from use cases and user stories before writing code.

2.Run all tests. The new test should fail indicating new code is needed for the desired feature

3.Write the simplest code that passes the new test.  No code should be added beyond  the tested functionality. 

4.All tests should now pass. If any fail, revise till it pass.

5.Refactor as needed, using tests after each refactor to ensure that functionality is preserved

6.Repeat for each new piece of functionality.




Tests 

  • Fast: Unit tests should be fast and being executed in a short time.
  • Independent: Tests should not depend on each other.
  • Repeatable: Tests should be reproducible in any environment.
  • One assert per test.
  • If you have the code covered by tests, you will not be afraid to modify it and break it.
  • Build your software using tests that guide your development. TDD encourages simple designs and inspires confidence


Clean coder rule 


Always leave the code cleaner than you found it.




General Design Rules 

  • Declare local variables as close as you can to their usage.
  • Declare instance variables at the top of the class
  • Constants should be declared at the top of the class or in a Constants class by example.
  • Use dependency injection
  • If you use third-party libraries, wrap them, so if they change, only your wrapper implementation needs to change
  • Place methods in a top-down direction.
  • Program to interface not implementation.
  • Prefer polymorphism to if/else or switch/case.
  • Separate multi-threading code
  • Follow Law of Demeter. A class should know only its direct dependencies.
  • Keep configurable data at high levels.
  • Be consistent. If you do something a certain way, do all similar things in the same way.


Code smells 


1.Rigidity. The software is difficult to change. A small change causes a cascade of subsequent changes.

2.Fragility. The software breaks in many places due to a single change.

3.Immobility. You cannot reuse parts of the code in other projects because of involved risks and high effort.

4.Needless Complexity.

5.Needless Repetition.

6.Opacity. The code is hard to understand.



Some general software engineering principles

  • DRY       -   Don’t Repeat Yourself. Opposite is WET.
  • KISS      -    Keep It Simple Stupid. 
  • YAGNI   -   You Aren’t Gonna Need It
  • SOLID


DRY - Don’t Repeat yourself 

  • A piece of  code should not be repeated across the software.
  • Reduce duplicate and increase reusability . 
  • Reusability means less code which leads to better maintainability. 
  • But remember, some reusability can actually increase readability and maintainability. 
  • Opposite of DRY is WET - Write Everything Twice,  Waste Everyone’s time , We Enjoy Typing.  This applies when you over repeat. 
  • General rule – when you write something thrice, it is time to refactor the logic to a function and reuse.


KISS - Keep It Simple Stupid 

  • Keep Code as Simple as possible. 
  • Engineers like to complicate things.  Unnecessary complexity should be avoided.
  • It makes it easy to understand and maintain.
  • How to keep code simple?
  • Follow the suggestions from previous slides to keep the classes and methods simple and concise.  
  • Break down the problem into small steps and  write minimal code to implement them.
  • Also, always ask yourself and the reviewer  - can this be written in simpler way?


YAGNI  Principle - You aren’t gonna need it 


  • Part of Extreme programming methodology,  yagni principle states that some capability we presume our software needs in the future should not be built now because "you aren't gonna need it".
  • Also, you should not optimize and increase complexity thinking about future.
  • It improves software quality  and increases responsiveness. 
  • Sometimes it may be good but think,  it may come in your way in future where you will encounter the cost of building the software wrong and leads to waste of effort and time.
  • It should be used with continuous refactoring, unit testing and integration.
  • It syncs with KISS on avoiding complexity.


SOLID Principles 


Brief Explanation


  • Single Responsibility Principle - interface, class or method  ideally should do one thing and do that well
  • Open-Closed Principle - Open for extension and closed for modification. You class should have everything - closed for modification but is open for extension through inheritance or composition. 
  • Liskov Substitution Principle  - Every subclass should be substitutable for their parent class. This helps in reducing coupling.
  • Interface segregation Principle - Every class must implement the methods of the interface it implements. This principle states that classes should not require to implement methods which they do not require. It helps in creating more smaller & focused interfaces. 
  • Dependency Inversion Principle - Classes should depend only on their abstractions, not on their implementations.  The classes should not instantiating their dependencies but they should be injected into the class. 



Single Responsibility Principle(SRP) 

  • Every module or class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class 
  • A method or class must not do  anything less or more.  There should be a single reason to change it. 
  • For example,  a class has methods for sign up and login functionality.  If  there is change in login  mechanism or sign up, we need to change the class for both the reasons. 
  • Break into two classes and we will have only one reason to change the respective class. 





Open-closed Principle  


The principle means  open for extension but closed for modification.







Liskov Substitution Principle(LSP) 


Every subclass should be substitutable for their parent class. The Objects in your sub class should work in same way as the super class






Interface Segregation Principle 


  • This principle states that classes should not require to implement methods which they do not require.
  • Clients should not be forced upon interfaces that they do not use. 




Solution 


Break our interface into multiple interfaces like TerrestialAnimals and FlyingAnimals. Animals interface can contain function eat()




Dependency Inversion Principle 


DIP states that

  • High level modules should not depend on low level modules. Both should depend on abstractions.
  • Abstractions should not depend on details.  Details should depend on abstractions.

Refer this example for better understanding.  




Tell, Don’t Ask Principle 


  • Tell-Don't-Ask is a principle that helps people remember that object-orientation is about bundling data with the functions that operate on that data. 
  • It reminds us that rather than asking an object for data and acting on that data, we should instead tell an object what to do. 
  • This encourages to move behaviour into an object to go with the data
tell don't ask principle



Refer martin fowler blog on this principle for detailed explanation. 




Favor Composition Over Inheritance

  • Composition – HAS –A  relationship 
  • Inheritance – IS –A relationship 
  • Both  provide polymorphic behavior . Explain how?

But why favor composition? 

  • With composition, it's easy to change behaviour on the fly with Dependency Injection .
  • Inheritance is more rigid as most languages do not allow you to derive from more than one type. 
  • Inheritance exposes subclass details of parent class therefore it may break encapsulation. 
  • Tight coupling – implementation of subclass may be bound to parent class implementation. 
  • Excessive reuse of code by subclasses can make stack go deeper and more confusing. 



Last points 

  • Code against interfaces  -  loose coupling
  • Never inject concrete implementations – tight coupling
  • Encapsulate what changes – Factory design pattern  which encapsulates object creation code. It provides flexibility to introduce changes without any impact on existing code.
  • Don’t do all stuff by yourself, delegate wherever applicable. Example is hashcode() and equals() method to compare two objects equality.

You cannot swim without getting wet


 Incorporate these principles into your development and make it a habit to use these principles to be a better programmer. 




More Reading

 




Keep Experimenting 🔎 

Keep Learning 🚀


Post a Comment

Previous Post Next Post