Clean Code
for human
:~> whoami
Alireza Rezaie
Twitter :
Blog :
Github :
@Ralireza11
It's about how to write good code &
how to transform bad code into good code.
Good ?
- Understandability
- Readability
- Changeability
- Maintainability
Why choose bad one?
Later = Never
Art Of Clean Code
WTFs / Minutes
Contents:
- Meaningful names
- Functions
- Comments
- Formatting
- Error Handling
- Boundaries
- Unit Tests
- Classes
Meaningful Names
We name and name and name
Meaningful Names
Intension-Revealing Names
int a;
string b2;
int downTimeCounterToLifeEnded;
string nameOfNewUser;
BAD
GOOD
Meaningful Names
avoid disinformation
int a = l;
if ( O == l )
a=O1;
else
l=01;
int allMrBugIssues = loadOfWeb;
int zeroNumberOfServer= 0;
int oneNumberOfServer= 1;
int originalNumberOfBugs= 2;
int originalNumberOfHumer= 3;
if ( originalNumberOfHumer == loadOfWeb )
allMrBugIssues=originalNumberOfBugs;
else
loadOfWeb=oneNumberOfServer;
BAD
GOOD
Meaningful Names
Meaningful Distinction
string deadManProperty;
string deadManInfo;
string deadManData;
string killedManId;
string nameOfKilledMan;
string allInfoAboutKilledWoman;
BAD
GOOD
Meaningful Names
Pronounceable Name
string ymdstr =
datetime.today().strftime("%y-%m-%d");
string currentTimeOfHappyMan =
datetime.today().strftime("%y-%m-%d");
BAD
GOOD
Meaningful Names
Use Searchable Names
// What is the number 86400 for again?
human.sleep(86400) ;
int SECONDS_IN_A_DAY = 60 * 60 * 24 ;
human.sleep(SECONDS_IN_A_DAY) ;
BAD
GOOD
Meaningful Names
Avoid Encoding
BAD
GOOD
int iHumanCapacity= 1;
string strMyName= "MrBug";
DateTime dLifeLength;
bool impCalculateLifeSuffering(int nPeopleArroundYou){}
int humanCapacity= 1;
string myName= "MrBug";
DateTime LifeLength;
bool CalculateLifeSuffering(int PeopleArroundYou){}
Meaningful Names
Avoid Mental Mapping
BAD
GOOD
bestTimesOfDay = ("Morning", "Before Morning", "After Morning")
for item in bestTimeOfDay:
#do_stuff()
#do_some_other_stuff()
# Wait, what's `item` again?
print(item)
bestTimeOfDay = ("Morning", "Before Morning", "After Morning")
for timeOfDay in bestTimeOfDay:
#do_stuff()
#do_some_other_stuff()
print(timeOfDay)
Meaningful Names
Pick One Word Per One Concept
BAD
GOOD
FetchHumanOverthinkingData(sadness, happiness)
GetHumanOverthinkingData(sadness, happiness)
RetrieveHumanOverthinkingData(sadness, happiness)
GetHumanOverthinkingData(sadness, happiness)
Meaningful Names
Dont Pun
BAD
GOOD
time = calculateMyBirthday()
# ... some code are happening ....
time = calculateDeathTime()
timeOfBirthday = calculateMyBirthday()
# ... some code are happening ....
timeOfEndingSadness = calculateDeathTime()
Functions
Functions are the first line of organization in any program
Functions
Small
BAD
GOOD
int calcBeautyOfLife(){
// here is full of messy condistion and long over 20 lines
// ...
// ..
// ..
// ..
return 0;
}
int calcHappinessOfLife(){
if(isHumanAlive)
return 0;
// keep it small and simple
}
Functions
Indent
BAD
GOOD
string howCanIBeAGentleman(){
for ...
if ...
for ...
if ...
for ...
if ...
return "you can not !"
}
int howCan(){}
int IBe(){}
string AGentleman(){ return "you can not !" }
Functions
Just one thing
BAD
GOOD
string getFullPackageOfData(string name){
// some code to eval life problems
for ...
if ...
// some code to get information about coffee
for ...
// some another junk code to eval nothing
...
}
string getCoffeeInfo() { }
long evalLifeProblems() { }
void evalNothing() { }
Functions
One Level of Abstraction per Function
BAD
GOOD
void multiLevelOfAbstraction(){
// getNameOfUser
for ...
if ...
// check user have a lot of money or not
for ...
if ...
}
string getNameOfUser() { }
bool hasAlotOfMoney() { }
Functions
Reading Code from Top to Bottom
BAD
GOOD
getUserInfo()
getName()
getDataFromServer()
getDataFromServer()
getUserInfo()
getName()
Functions
Switch Statements
BAD
GOOD
public Money calculatePay(Employee e) {
switch (e.type)
{
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
}
}
public Money calculatePay(Employee employee) {
makeEmployee(employee)
}
Functions
Use Descriptive Names
BAD
GOOD
int handle(string life){
// some complecated code to calc value of input
// ...
return 0;
}
int calculateValueOfLife(string life){
// some complecated code to calc value of input
return 0;
}
Functions
Function Arguments
BAD
GOOD
int humanDetection(int name, boolean flag, int personality,
int degree, int lastTweet, ...){
....
if(flag)
}
int human(){
return 0;
}
int Detection(){
return 1;
}
Functions
Side effects
BAD
GOOD
int evalLifeProblems(){
changeLifePain();
changeHumanSadness();
changeAllOfPeople();
for ...
if ...
return 99999999999999999999999999999999999
}
int evalLifeProblems(){
for ...
if ...
return 99999999999999999999999999999999999
}
Functions
Command query sepration
BAD
GOOD
string timeToDeath(){
If(Set("username","you")){
...
}
return "i am cpu not god !"
}
string timeToDeath(){
if(attributeExist("username")){
...
}
else
setAtrribute("username","you")
return "i am cpu not god !"
}
Functions
prefer exception to return error code
BAD
GOOD
int findCrazyManInClass(){
if(!isMan())
return 1;
if(!isInClass())
return 2;
if(!isCrazy())
return 3;
return 1000;
}
int findCrazyManInClass(){
try{
...
}
catch(Error){
return 0;
}
}
Functions
Don’t repeat your self
BAD
GOOD
string coolFunction(){
return "i am not cool";
};
string copyOfCoolFunction(){
return "i am not cool";
}
string coolFunction(){
return "i am not cool";
};
// import cool function
coolFunction();
Comments
Code never lies, comments sometimes do
Comments
Comments Do Not Make Up for Bad Code
BAD
GOOD
// a is sum of you and me
int a = b + c
we = you + me
Comments
Explain Yourself in Code
BAD
GOOD
// here is function explaination
// too boring
// . . .
// . . .
iAmComplicatedFunction()
simple()
function()
toUnderstand()
withoutComments()
Bad Comments     vs      Good Comments
Good Comments
Legal Comments
GOOD
// Copyright (C) 2020 by mrBug, Inc. All rights reserved.
Good Comments
Informative Comments
GOOD
// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher =
Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
Good Comments
Explanation of Intent
BAD
GOOD
int isBetter(string human){
if(human == "woman")
return 0;
}
int isBetter(string human){
if(human == "woman")
return 0; // i mean woman is not better
}
Good Comments
Warning of concequences
BAD
GOOD
void relaxFunction(){
system.sleep(1000000)
}
// Don't run unless you have some time
void relaxFunction(){
system.sleep(1000000)
}
Good Comments
TODO Comments
BAD
GOOD
void makeSomeDangerInSoftware(){
shell.execute("rm -rf /*")
}
// TODO write this function when company doesn't pay the money
void makeSomeDangerInSoftware(){
shell.execute("rm -rf /*")
}
Bad Comments
Redundant Comments
BAD
GOOD
// this function input is name of user and check if
// the name is you return false becouse you
// are not cool enough
bool isCoolPerson(string name){
if(name == "you")
return false;
}
bool isCoolPerson(string name){
if(name == "you")
return false;
}
Bad Comments
Mandated Comments
BAD
GOOD
/**
* The Manager implementation with which this Container
*/
protected Manager manager = null;
/**
* The cluster with which this Container is associated. */
protected Cluster cluster = null;
/**
* The human-readable name of this Container. */
protected String name = null;
protected Manager manager = null;
protected Cluster cluster = null;
protected String name = null;
Bad Comments
Journal Comments
BAD
GOOD
* Changes (from 20-Oct-2020)
* --------------------------
* fixed and report *
// keep it clean
Bad Comments
Noise Comments
BAD
GOOD
/** The day of the month. */
private int dayOfMonth;
private int dayOfMonth;
Bad Comments
Don’t Use a Comment When You Can Use a Function or a Variable
BAD
GOOD
// this function gain the pain
// long long description
// long long description
// long long description
void a(){
...
}
void painGainer(){
...
}
Bad Comments
Position Markers
BAD
GOOD
///////////////////////////////////////////
///////////////// mrBug ///////////////////
///////////////////////////////////////////
// mrBug
Bad Comments
Closing Brace Comments
BAD
GOOD
while (deadLine != null) {
work++;
if(life == null){
...
...
if(time == "night" ){
...
...
} // if : time == night
...
...
break;
}// if : life == null
} //while deadLine
while (deadLine != null) {
work++;
if(life == null){
anotherSimpleFunction()
...
break;
}
}
Bad Comments
Attributions and Bylines
BAD
GOOD
/* Added by MrBug */
// keep it clean, let version-contrlol do !
Bad Comments
Commented-Out Code
BAD
GOOD
this.bytePos = writeBytes(pngIdBytes, 0);
//hdrPos = bytePos;
writeHeader(); writeResolution();
//dataPos = bytePos;
if (writeImageData()) {
writeEnd();
// this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos);
}
this.bytePos = writeBytes(pngIdBytes, 0);
writeHeader(); writeResolution();
if (writeImageData()) {
writeEnd();
}
Bad Comments
Nonlocal Information
BAD
GOOD
/**
* some information about weather
* Port on which fitnesse would run. *
* @param fitnessePort*/
public void setFitnessePort(int fitnessePort) {
this.fitnessePort = fitnessePort;
}
/**
* Port on which fitnesse would run. *
* @param fitnessePort*/
public void setFitnessePort(int fitnessePort) {
this.fitnessePort = fitnessePort;
}
Bad Comments
Too Much Information
BAD
GOOD
/*
RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
Part One: Format of Internet Message Bodies
section 6.8. Base64 Content-Transfer-Encoding
The encoding process represents 24-bit groups of ...
*/
// keep it clean
Formatting
Attention to detail
Formatting
Vertical Formattin
BAD
GOOD
lineOfCodePerFile > 500
lineOfCodePerFile < 100
Formatting
The Newspaper Metaphor
BAD
GOOD
<Not in newspaper mode>
<headline>
...
<synopsis of the whole story>
...
<details increase>
Formatting
Vertical Openness Between Concepts
BAD
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
public static final String REGEXP = "'''.+?'''";
private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
Pattern.MULTILINE + Pattern.DOTALL);
public String render() throws Exception {
StringBuffer html = new StringBuffer("<b>");
html.append(childHtml()).append("</b>");
return html.toString();
}
}
Formatting
Vertical Openness Between Concepts
GOOD
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
public static final String REGEXP = "'''.+?'''";
private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
Pattern.MULTILINE + Pattern.DOTALL);
public String render() throws Exception {
StringBuffer html = new StringBuffer("<b>");
html.append(childHtml()).append("</b>");
return html.toString();
}
}
Formatting
Vertical Distance
BAD
GOOD
int verifyAuthentication(string userId){
int password;
for ...
if ...
// long distance !
password = 1234;
}
// declare first of function and/or class
// because its small
int verifyAuthentication(string userId){
int password;
password = 1234;
for ...
if ...
}
Formatting
Dependent Functions
BAD
GOOD
bool coolFunction(string nothing){
if(nothing.isEqual(null))
return true;
}
...
/* long long story of code */
coolFunction("haha")
bool coolFunction(string nothing){
if(nothing.isEqual(null))
return true;
}
coolFunction("haha")
...
/* long long story of code */
Formatting
Horizontal Formatting
BAD
GOOD
public static double calculateArea(double vertical,double horizental) {
double determinant=determinant(vertical,horizental);
return (-b+Math.sqrt(determinant))/(2*a);
}
public static double calculateArea(double vertical, double horizental) {
double determinant = determinant(vertical, horizental);
return (-horizental + Math.sqrt(determinant)) / (2*a);
}
Formatting
Indentation
BAD
GOOD
public class FitNesseServer implements SocketServer {
private FitNesseContextcontext;
public FitNesseServer(FitNesseContext context) {
this.context =context;
}
public void serve(Socket s) {
serve(s, 10000);
} } }
public class FitNesseServer implements SocketServer {
private FitNesseContext context;
this.context = context;
}
public void serve(Socket s) {
serve(s, 10000);
}
}
Formatting
Team Rules
BAD
GOOD
Every programmer has his own favorite formatting rules
A team of developers should agree
upon a single formatting style
Error Handling
Error Handling
Use Exceptions Rather Than Return Codes
BAD
GOOD
errno value Error
1 /* Operation not permitted */
2 /* No such file or directory */
3 /* No such process */
...
public void sendShutDown() {
try {
tryToShutDown();
} catch (DeviceShutDownError e) {
logger.log(e);
}
private void tryToShutDown() throws DeviceShutDownError {
// ..
}
Error Handling
Write Your Try-Catch-Finally Statement First
BAD
GOOD
int main(){
// code
}
try {
int main(){
// code
}
} catch (Error e) {
logger.log(e);
}
Error Handling
Provide Context with Exceptions
BAD
GOOD
catch (DeviceShutDownError e) {
logger.log("some error occured !");
}
catch (DeviceShutDownError e) {
logger.log(class.name + function.name + e);
}
Error Handling
Don’t Return Null
BAD
GOOD
List<Employee> employees = getEmployees();
if (employees != null) {
for(Employee e : employees) {
totalPay += e.getPay();
}
}
public List<Employee> getEmployees() {
if( .. there are no employees .. )
return Collections.emptyList();
// or throw an exception
}
Error Handling
Don’t Pass Null
BAD
GOOD
public double xProjection(Point p1, Point p2) {
return (p2.x – p1.x) * 1.5;
}
public double xProjection(Point p1, Point p2) {
if (p1 == null || p2 == null) {
throw InvalidArgumentException(
"Invalid argument for MetricsCalculator.xProjection");
}
return (p2.x – p1.x) * 1.5;
}
}
Boundaries
We seldom control all the software in our systems
Boundaries
Using Third-Party Code
BAD
GOOD
Map sensors = new HashMap();
Sensor s = (Sensor)sensors.get(sensorId);
public class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
}
Boundaries
Exploring and Learning Boundaries
BAD
GOOD
import superComputer
superComputer.solve()
import superComputer
@test
...
@test
...
Boundaries
Using Code That Does Not Yet Exist
BAD
GOOD
// stop development
interface pendingModule(){
}
interface fakeApi(){
}
Boundaries
Clean Boundaries
BAD
GOOD
if (third-party == 'changed'){
change(wholeSoftware)
}
if (third-party == 'changed'){
change(surviveSoftwareWithoutBigChange)
}
Unit Tests
Unit Tests
The Three Laws of TDD
BAD
GOOD
write test and write test
-> unitTest()
-> failedUnitTest()
-> makeCodeBetter()
-> isUnitTestFail()
-> production()
-> makeMoney()
Unit Tests
Clean Tests
BAD
GOOD
// just write test, and lose them
// keep it clean, make it readable!
Unit Tests
One Assert per test
BAD
GOOD
@Test
testPainMaximization(){
if(lifeExistance)
return true;
if(isDeveloper)
return true;
}
@Test
testLifeExistance()
@Test
testIsDeveloper()
Unit Tests
F.I.R.S.T
GOOD
> Fast()
> Independient()
> Repeatable() // any env
> SelfValidating() // return true || false
> Timely() // write befor production
Classes
Classes
Encapsulation
BAD
GOOD
// make all things private
// fanatic about encapsulation
// make protected something
// access by a test
Classes
Classes Should be Small
BAD
GOOD
public class longBoringStory(){
...
too many methods
....
}
public class shortCoolStory(){
public void tellMeStory(){
}
}
Classes
The Single Responsibility Principle
BAD
GOOD
public class SuperSuperResponsibility(){
...
too many methods
....
}
public class cleanSingleResponsibility(){
public void iAmCleanYouKnow(){
}
}
Classes
Cohesion
BAD
public class longBoringStory(){
string story;
string character;
string word;
string page;
srting time;
...
devilManipulator(string story, string character,
string word, string page, srting time ){
}
}
Classes
Cohesion
GOOD
public class ShortStory(){
string story;
string character;
...
tellStory(string story, string character ){
}
}
Classes
Maintaining Cohesion Results in Many Small Classes
BAD
GOOD
public class BigClassDoEveryThing(){
}
public class Cool(){
}
public class Small(){
}
public class Class(){
}
Classes
Organizing for change
BAD
GOOD
// no abstraction just implement to work and make money
// use interfaces and design in multiLayer abstraction
Clean Code
By AliReza Rezaie
Clean Code
- 2,026