Clean Code
Benedikus H Tjuatja
David Setyanugraha
Fakhri
Lutvianes Advendianto
Overview
● Issues
● Solution
● Conclusion
3
© GDP Labs 2017
Issues !
● Bad Code
● Great vs Bad Company
● Who is Responsible?
4
© GDP Labs 2017
Bad Code
● Technical debt:
the eventual consequences of poor software
architecture and software development within a code
base.
○ Rushed the product to the market
○ Had made a huge mess in the code
○ Added more features
○ The code got worse and worse
5
© GDP Labs 2017
Bad Code
● LeBlanc’s Law:
Later equals never
○ They never go back
to fix the bad code
● Sink projects, careers
and companies
● Example: netscape and
myspace
6
© GDP Labs 2017
Death Spiral:
The downward,
corkscrew-motion of a disabled
aircraft which is unrecoverably
headed for a crash
7
Great Company vs Bad Company
8
© GDP Labs 2017
Great Company vs Bad Company
9
© GDP Labs 2017
Great Company vs Bad Company
10
© GDP Labs 2017
Who is ● BOD?
● Management?
Responsible ● Program Manager?
for Bad Code? ●
●
Product Manager?
Unrealistic schedule?
Unprofessional Software Engineers.
It is your job to defend your code with equal passion.
11
© GDP Labs 2017
It is a crime if a
surgeon does not
“clean” his hands
12
For the things we have
to learn before we can
do them, we learn by
doing them - Aristotle
13
Solution
● Naming
● Function
● Comment
● Formatting
● Objects and Data Structures
● Error Handling
● Class
● Emergence
14
© GDP Labs 2017
Naming
“ The name of a variable, function, or
class, should answer all the big
questions. It should tell you why it
”
exists, what it does, and how it is used.
- Robert C. Martin
15
© GDP Labs 2017
Naming
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
Say that we’re working in a mine sweeper game.
16
© GDP Labs 2017
Naming
Use intention-revealing names
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
17
© GDP Labs 2017
Naming
Use intention-revealing names
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
18
© GDP Labs 2017
Naming
Avoid misleading and unmeaningful distinctions
int a = l;
if (O == l)
a = O1;
else
l = 01;
MISLEADING
private void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
UNMEANINGFUL DISTINCTIONS
19
© GDP Labs 2017
Naming
public class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
private final String pszqint = ”102”;
/* … */
} CAN YOU PRONOUNCE THEM?
20
© GDP Labs 2017
Naming
Use pronounceable names
public class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
private final String recordId = ”102”;
/* … */
}
21
© GDP Labs 2017
Naming
Classes and objects should be noun,
methods should be verb
public class Point {
private int x, y;
public Point(int x, int y) {...}
public int getX() {...}
public void setX(int x) {...}
public int getY() {...}
public void setY(int y) {...}
}
22
© GDP Labs 2017
Naming
public class Circle {
double radius;
String color;
public double getRadius() {...}
public String fetchColor() {...}
public double retrieveArea() {...}
}
GET OR FETCH OR RETRIEVE?
23
© GDP Labs 2017
Naming
Pick one word per concept
public class Circle {
double radius;
String color;
public double getRadius() {...}
public String getColor() {...}
public double getArea() {...}
}
24
© GDP Labs 2017
Naming
for (int i=0; i<34; i++) {
s += (t[i]*4)/5;
} NOT EASY TO SEARCH
if single-letter names and numeric constants are
used across body of text, they are not easy to locate
25
© GDP Labs 2017
Naming
Use searchable names
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int i=0; i < NUMBER_OF_TASKS; i++) {
int realTaskDays = taskEstimate[i] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
26
© GDP Labs 2017
Function
“ The first rule of functions is that they
should be small. The second rule of
functions is that they should be
smaller than that.
- Robert C. Martin
”
27
© GDP Labs 2017
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite
) throws Exception {
boolean isTestPage = pageData.hasAttribute("Test");
if (isTestPage) {
WikiPage testPage = pageData.getWikiPage();
StringBuffer newPageContent = new StringBuffer();
includeSetupPages(testPage, newPageContent, isSuite);
newPageContent.append(pageData.getContent());
includeTeardownPages(testPage, newPageContent, isSuite);
pageData.setContent(newPageContent.toString());
}
return pageData.getHtml();
} TOO BIG
28
Function
Do one thing
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception {
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
Function is doing more than “one thing” if you can
extract another function from it with a name that is not
merely a restatement
29
© GDP Labs 2017
Function
public boolean set(String attribute, String value);
if (set(”username”, ”unclebob”))
Is it asking whether the “username” attribute was
previously set to “unclebob”? Or is it asking whether the
“username” attribute was successfully set to “unclebob”?
30
© GDP Labs 2017
Function
Do something or answer something,
but not both
if (attributeExists(”username”)) {
setAttribute(”username”, ”unclebob”);
}
31
© GDP Labs 2017
Function
Transformational function should appear as the return
value
void transform(StringBuffer out)
These implementation simply returns the input argument
StringBuffer transform(StringBuffer in)
32
© GDP Labs 2017
Function
Avoid too many function parameters
Car createCar(float wheelDiameter, float
wheelColor, float wheelMaterial, float
wheelManufacturer, String engineType, String
engineColor, String engineManufacturer);
Too many parameters will make function hard to be
understood and maintained.
Car createCar(Wheel wheel, Engine engine);
33
© GDP Labs 2017
Comments
“ Good code is its own best documentation.
As you’re about to add a comment, ask
yourself, ‘How can I improve the code so
”
that this comment isn’t needed?'
- Steve McConnell
34
© GDP Labs 2017
Comments
Redundant Comments
The comment is not more informative than the code
// Throws an exception
// if the timeout is reached.
public synchronized void waitForClose(
final long timeoutMillis) throws Exception {
if(!closed) {
wait(timeoutMillis);
if(!closed)
throw new Exception(
"MockResponseSender could not be closed");
}
}
35
© GDP Labs 2017
Comments
Noise Comments
Only restate the obvious and provide no new information
/**
* Returns the day of the month.
* @return the day of the month.
*/
public int getDayOfMonth() {
return dayOfMonth;
}
36
© GDP Labs 2017
Comments
Explain yourself in code
// Check to see if the employee is eligible for full
benefits
if ((employee.flags & HOURLY_FLAG) &&
(employee.age > 65))
if (employee.isEligibleForFullBenefits())
37
© GDP Labs 2017
Comments
// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*,
\\w* \\d*, \\d*");
INFORMATIVE
public void writeJournal(Diary diary) {
for (Thread thread : threadList) {
thread.run(() -> {
// Prevent original object to be modified
Diary copyOfDiary = new Diary(diary);
write(copyOfDiary);
});
}
} EXPLANATION OF INTENT
38
© GDP Labs 2017
Comments
public static SimpleDateFormat makeStandardHttpDateFormat() {
//SimpleDateFormat is not thread safe,
//so we need to create each instance independently.
SimpleDateFormat df = new SimpleDateFormat(
”EEE, dd MMM yyyy HH:mm:ss z”);
df.setTimeZone(TimeZone.getTimeZone(”GMT”));
return df;
}
WARNING OF CONSEQUENCES
39
© GDP Labs 2017
Formatting
40
© GDP Labs 2017
Formatting
Variables
Should be declared as close to their usage as possible
public void paySalary(Employee employee) {
float bonus;
float totalSalary;
bonus = calculateBonus(employee.getSalary());
totalSalary = bonus + employee.getSalary();
sendMoney(employee, totalSalary);
}
41
© GDP Labs 2017
Formatting
Variables
Should be declared as close to their usage as possible
public void paySalary(Employee employee) {
float bonus;
bonus = calculateBonus(employee.getSalary());
float totalSalary;
totalSalary = bonus + employee.getSalary();
sendMoney(employee, totalSalary);
}
42
© GDP Labs 2017
Formatting
Instance Variables
Should be declared at the top of the class
public class Employee {
private String name;
private String id;
private float salary;
public void getName() { … }
}
43
© GDP Labs 2017
Formatting
Dependent Functions
Should be vertically close, and the caller should be above
the called
public void paySalary() {
calculateBonus(salary);
}
private float calculateBonus(float salary) {
return (salary / 10);
}
44
© GDP Labs 2017
Formatting
Conceptual Affinity
Certain bits of code want to be near other bits
public class Employee {
public void payTax() {
}
public void payOverdueTax(Date date) {
}
public void increaseSalary() {
}
public void decreaseSalary() {
}
}
45
© GDP Labs 2017
Formatting
Space
public float volume (float length,float width,float height) {
// code
}
public float volume(float length, float width, float height) {
// code
}
46
© GDP Labs 2017
Formatting
Horizontal Alignment
public class WebService {
private Request request;
private Response response;
private FitnesseContext context;
protected long requestTimeLimit;
}
DIFFICULT TO MAINTAIN
47
© GDP Labs 2017
Formatting
Horizontal Alignment
public class WebService {
private Request request;
private Response response;
private FitnesseContext context;
protected long requestTimeLimit;
}
48
© GDP Labs 2017
Formatting
Indentation
public String functionName() {return "";}
public String functionName() {
return "";
}
49
© GDP Labs 2017
Objects and Data
Structures
50
© GDP Labs 2017
Objects and Data Structures
Data structures expose data and have no behavior
Data structures make it easy to add functions without the
need to modify existing structures.
public class Point {
public double x;
public double y;
}
51
© GDP Labs 2017
Objects and Data Structures
Object expose behavior and hide data
Objects make it easy to add classes without the need to
modify existing functions.
public class Vehicle {
public getFuelCapacity() {...}
public getPercentageFuelRemaining() {...}
}
52
© GDP Labs 2017
Objects and Data Structures
Law of Demeter
Given method f of class C, f should only call methods of:
● C
● An Object created by f
● An Object passed as an argument to f
● An instance variable of C
53
© GDP Labs 2017
Objects and Data Structures
Law of Demeter
Given method f of class C, f should only call methods of:
● C
public class Vehicle {
public getFuelCapacity() {...}
public getPercentageFuelRemaining() {
return (fuel / getFuelCapacity() * 100);
}
}
54
© GDP Labs 2017
Objects and Data Structures
Law of Demeter
Given method f of class C, f should only call methods of:
● An Object created by f
public void registryEmployee() {
Employee newEmployee = new Employee();
registry.createEmplyeeId(newEmployee);
employeeIdList.add(newEmployee.getId());
}
55
© GDP Labs 2017
Objects and Data Structures
Law of Demeter
Given method f of class C, f should only call methods of:
● An Object passed as an argument to f
public void calculateEmployeeBonus(Employee employee) {
return (employee.getSalary() / 10);
}
56
© GDP Labs 2017
Objects and Data Structures
Law of Demeter
Given method f of class C, f should only call methods of:
● An instance variable of C
public class Car {
private Engine engine;
public String getCarFuelType() {
return engine.getFuelType();
}
}
57
© GDP Labs 2017
Error Handling
58
© GDP Labs 2017
Error Handling
Prefer exceptions to return error code
if (deletePage(page) == E_OK) {
if (registry.delete(page.reference) == E_OK) {
logger.log(“page deleted”);
} else {
logger.log(“delete registry failed”);
}
} else {
logger.log(“delete failed”);
}
59
© GDP Labs 2017
Error Handling
Prefer exceptions to return error code
try {
deletePage(page);
registry.delete(page.reference);
} catch (Exception e) {
logger.log(e.getMessage());
}
60
© GDP Labs 2017
Error Handling
● Functions should do one thing and error handling is
one thing
○ Implement the normal flow of the function
● Don’t return NULL
○ So other function doesn’t need to implement error
handling
● Don’t pass NULL
○ So other function doesn’t need to implement error
handling
61
© GDP Labs 2017
Class
62
© GDP Labs 2017
Class
Class Organization
Declare the constants, variables, and methods in this order:
public class TimeCalculator {
public static final int TIME = 25; // public static constant
public static int DURATION = 25; // private static variables
private int now; // private instance variables
public int addTime(int time) { // public functions
…
configTime = getConfigTime()
…
}
private int getConfigTime() { … } // private utilities
}
63
© GDP Labs 2017
Class
Classes should be small!
● The first rule is that they should be small
● The second rule is that they should be smaller than that
Single Responsibility Principle (SRP)
● A class or module should have one, and only one,
reason to change
● SRP is one of the more important concept in OO design
64
© GDP Labs 2017
Class
Refactored:
public class TimeCalculator {
…
public int addTime(int time) { … }
…
}
public class UserConfiguration {
…
public int getConfigTime() { … }
…
}
65
© GDP Labs 2017
Class
Cohesion
● Classes should have a small number of instance
variables
● The more variables (or class modules) a method
manipulates the more cohesive that method is to its
class.
● A class in which each variable is used by each
method is maximally cohesive.
● Maintaining cohesion results in many small classes
66
© GDP Labs 2017
public class CustomStack {
private int topOfStack = 0;
private int duration = 100;
List<Integer> elements = new LinkedList<Integer>();
public int size() { … }
public void push(int element) { … }
public int pop() throws PoppedWhenEmpty { … }
public void sleep() {
TimeUnit.SECONDS.sleep(duration);
}
public void log() {
…
logger.log(Level.WARNING, ”This is a warning!”);
…
}
…
} LOW COHESION
67
© GDP Labs 2017
public class Stack {
private int topOfStack = 0;
List<Integer> elements = new LinkedList<Integer>();
public int size() {
return topOfStack;
}
public void push(int element) {
topOfStack++;
elements.add(element);
}
public int pop() throws PoppedWhenEmpty {
if (topOfStack == 0)
throw new PoppedWhenEmpty();
int element = elements.get(--topOfStack);
elements.remove(topOfStack);
return element;
}
} HIGH COHESION
68
© GDP Labs 2017
Emergence
69
© GDP Labs 2017
Emergence
● Runs all the tests. To make it easy, make sure:
○ Low coupling
○ SRP
● Contains no duplication (Refactoring)
● Expresses the intent of the programmer (Refactoring)
○ Easy to read and understand
● Minimizes the number of classes and methods
(Refactoring)
○ But don’t take it too far
70
© GDP Labs 2017
public void scaleToOneDimension(…) {
…
RenderedOp newImage = ImageUtilities.getScaledImage(
image, scalingFactor, scalingFactor);
image.dispose();
System.gc();
image = newImage;
}
public synchronized void rotate(int degrees) {
RenderedOp newImage = ImageUtilities.getRotatedImage(
image, degrees);
image.dispose();
System.gc();
image = newImage;
} DUPLICATION
71
© GDP Labs 2017
public void scaleToOneDimension(…) {
…
replaceImage(ImageUtilities.getScaledImage(
image, scalingFactor, scalingFactor));
}
public synchronized void rotate(int degrees) {
replaceImage(ImageUtilities.getRotatedImage(
image, degrees));
}
private void replaceImage(RenderedOp newImage) {
image.dispose();
System.gc();
image = newImage;
}
DUPLICATION REFACTORED
72
© GDP Labs 2017
Give a man a fish and you feed
him for a day; teach a man to fish
and you feed him for a lifetime.
- Anne Thackeray Ritchie
73
Conclusion
1. Practice
Is there a set of simple practices that
can replace experience? Clearly not
2. Beware of Bad Code or Code Smell
3. Refactor
74
© GDP Labs 2017
Clean Code:
A Handbook of Agile
Software Craftsmanship
Robert C. Martin
©2009 | Prentice Hall
75
© GDP Labs 2017
A programmer who writes clean
code is an artist who can take a
blank screen through a series of
transformations until it is an
elegantly coded system
76
Thank You
Q&A
speakerdeck.com/gdplabs
77
We Are Hiring!
jobs@gdplabs.id
78
Cracking the
Coding Interview:
189 Programming
Questions and Solutions
Gayle Laakmann McDowell
©2015 | CareerCup
79
© GDP Labs 2017
Extras
● Code Smells
● OO Design Principles
80
© GDP Labs 2017
Code Smells
● Large class ● Duplicated code
● Feature envy ● Too many parameters
● Inappropriate intimacy ● Long method
● Refused bequest ● Excessive return of data
● Lazy class ● Excessively long
● Excessive use of literals identifiers
● Cyclomatic complexity ● Excessively short
● Data clump identifiers
● Orphan variable or
constant class
81
© GDP Labs 2017
OO Design Principles: S.O.L.I.D
S Single-responsibility principle
O Open-closed principle
L Liskov substitution principle
I Interface segregation principle
D Dependency Inversion Principle
82
© GDP Labs 2017