Clean Code
Clean Code
Clean Code
● Learning to write clean code is hard work.
● It requires more than just the knowledge of principles and patterns.
● You must practice it yourself, and watch yourself fail.
● Perhaps you felt that you didn’t have time to do a good job; that your boss
would be angry with you if you took the time to clean up your code.
● Perhaps you were just tired of working on the project and wanted it to be
over.
● Or maybe you looked other stuff that you had promised to get done and
the time was short.
The Total Cost of Owning a Mess
Furthermore, they, and everyone else on the team, are under horrific pressure
What Is Clean Code?
● simple and direct
● readable
● contains no duplication
Meaningful Names - Use Intention-Revealing Names
● Compare:
int d; // elapsed time in days
● To:
int elapsedTimeInDays;
Meaningful Names - Use Intention-Revealing Names
● What is the purpose of this code?
● To:
class Customer {
private Date generationDate;
private Date modificationDate;
private final String recordId = "102";
/* ... */
}
● Intelligent conversation is now possible: “Hey, Michael, take a look at this record!
Meaningful Names - Use Searchable Names
● If a variable or constant might be seen or used in multiple places in a body
of code, it is imperative to give it a search-friendly name.
● Compare:
int s = 0;
for (int j=0; j<10; j++) {
s += (t[j])/5;
}
● To:
int NUMBER_TASKS = 10;
int sum = 0;
for (int j=0; j < NUMBER_TASKS; j++) {
sum += (taskEstimate[j]) / WORK_DAYS_PER_WEEK;
}
● The intentionally named code makes for a longer function, but consider
Meaningful Names - Avoid Encodings
● Hungarian Notation (sName, iAge, etc)
● Java programmers don’t need type encoding.
● They make it harder to read the code. And they create the possibility that
the encoding system will mislead the reader.
● PhoneNumber phoneString;
// name not changed when type changed!
Meaningful Names - Prefixes
● You also don’t need to prefix variables anymore.
● Compare:
public class Part {
private String m_dsc; // The textual description
void setName(String name) {
m_dsc = name;
}
}
● To:
public class Part {
String description;
void setDescription(String description) {
this.description = description;
}
}
● Besides, People quickly learn to ignore the prefix (or suffix) to see the meaningful part of the
name. The more we read the code, the less we see the prefixes.
Functions
● The functions should be small.
● Transparently obvious.
● Do one thing.
Functions
● FUNCTIONS SHOULD DO ONE THING.
● One way to know that a function is doing more than “one thing” is if you
can extract another function from it with a name that is not merely a
restatement of its implementation.
Functions - Use Descriptive Names
● Don’t be afraid to make a name long.
● A long descriptive name is better than a short enigmatic name.
● A long descriptive name is better than a long descriptive comment.
● Don’t be afraid to spend time choosing a name. You should try different
names and read the code again.
● Modern IDEs like Eclipse (Alt+Shift+R) make it trivial to change names.
● Choosing descriptive names will clarify the design of the module in your
mind and help you to improve it.
Functions - Function Arguments
● The ideal number of arguments for a function is zero.
● Arguments are even harder from a testing point of view. Imagine the
difficulty of writing all the test cases to ensure that all the various
combinations of arguments work properly.
Functions - Flag Arguments
● Flag arguments are ugly. Passing a boolean into a function is a terrible
practice.
● It does one thing if the flag is true and another if the flag is false!
● Compare:
Circle makeCircle(double x, double y, double radius);
● To:
Circle makeCircle(Point center, double radius);
Functions - Have No Side Effects
● Side effects are lies. Your function promises to do one thing, but it also does other hidden
things.
● This side effect creates a temporal coupling. That is, checkPassword can
only be called at certain times. If it is called out of order, session data may
be lost.
● If you must have a temporal coupling, you should make it clear in the name
of the function. In this case we might rename the function
checkPasswordAndInitializeSession, though that certainly violates “Do one
thing.”
Functions - Don’t Repeat Yourself
● Duplication may be the root of all evil in software. Many principles and
practices have been created for the purpose of controlling or eliminating it.
● Comments lie. Not always, and not intentionally, but too often. .
● Rather than spend your time writing the comments that explain the mess
you’ve made, spend it cleaning that mess.
Comments - Explain Yourself in Code
● Compare:
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
● to:
if (employee.isEligibleForFullBenefits())
● In many cases it’s simply a matter of creating a function that says the
same thing as the comment you want to write.
Comments - Good Comments
● Some comments are necessary or beneficial.
--Legal Comments
Sometimes our corporate coding standards force us to write certain
comments for legal reasons.
● You might not agree with the programmer’s solution to the problem, but at
least you know what he was trying to do.
Comments - Good Comments
-- Clarification
assertTrue(a.compareTo(b) == 0); // a == b
assertTrue(a.compareTo(b) != 0); // a != b
assertTrue(a.compareTo(b) == -1); // a < b
assertTrue(b.compareTo(a) == 1); // b > a
● TODOs are jobs that the programmer thinks should be done, but for some
reason can’t do at the moment.
● Whatever else a TODO might be, it is not an excuse to leave bad code in
the system.
Comments - Good Comments
-- Amplification
● The javadocs for the standard Java library are a case in point.
● If you are writing a public API, then you should certainly write good
javadocs for it.
● Usually they are excuses for poor code or justifications for insufficient
decisions.
Comments - Bad Comments
-- Redundant Comments
● The comment probably takes longer to read than the code itself.
Comments - Bad Comments
-- Misleading Comments
● It is just plain silly to have a rule that says that every function must have a javadoc, or every variable
must have a comment.
/**
*
* @param title The title of the CD
* @param author The author of the CD
* @param tracks The number of tracks on the CD
* @param durationInMinutes The duration of the CD in minutes
*/
public void addCD(String title, String author, int tracks, int durationInMinutes) {
CD cd = new CD();
cd.title = title;
cd.author = author;
cd.tracks = tracks;
cd.duration = duration;
cdList.add(cd);
}
● Sometimes people add a comment to the start of a module every time they edit it.
● These comments accumulate as a kind of journal, or log, of every change that has
ever been made.
● Long ago there was a good reason to create and maintain these log entries at the
start of every module. We didn’t have source code control systems that did it for
us. Nowadays, they should be completely removed.
Comments - Bad Comments
-- Noise Comments
● Sometimes we see comments that are nothing but noise. They restate the obvious and provide no new
information.
/**
* Default constructor.
*/
protected AnnualDateRule() {
}
/**
* Returns the day of the month.
*
* @return the day of the month.
*/
public int getDayOfMonth() {
Comments - Bad Comments
-- Attributions and Bylines
● Source code control systems are very good at remembering who added
what, when.
● Others who see that commented-out code won’t have the courage to delete it.
● They’ll think it is there for a reason and is too important not delete it.
● Why are those two lines of code commented? Are they important? Were they left
as reminders for some imminent change? Or are they just cruft that someone
commented-out years ago and has simply not bothered to clean up?
● Nowadays, we’ve good source code control systems. Those systems will
remember the code for us. We don’t have to comment it out any more. Just delete
the code. We won’t lose it. Promise.
Comments - Bad Comments
-- Too Much Information
● Don’t put interesting historical discussions or irrelevant descriptions of details into your comments. The
comment below was extracted from a module designed to test that a function could encode and
decode base64.
● Someone reading this code has no need for the arcane information contained in the comment.
/*
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 input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
When encoding a bit stream via the base64 encoding, the bit stream
must be presumed to be ordered with the most-significant-bit first.
That is, the first bit in the stream will be the high-order bit in
the first 8-bit byte, and the eighth bit will be the low-order bit in
the first 8-bit byte, and so on.
Formatting
● We should take care that our code is nicely formatted.
● If we are working on a team, then the team should agree to a single set of
formatting rules and all members should comply.
Formatting
---The Purpose of Formatting
● Perhaps we thought that “getting it working” was the first order of business
for a professional developer.
● Those thoughts should be separated from each other with blank lines.
Formatting
● Compare:
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 BoldWidget(ParentWidget parent, String text) throws Exception {
super(parent);
Matcher match = pattern.matcher(text);
match.find();
addChildWidgets(match.group(1));
}
public String render() throws Exception {
StringBuffer html = new StringBuffer("<b>");
html.append(childHtml()).append("</b>");
return html.toString();
}
}
Formatting
● To:
package fitnesse.wikitext.widgets;
import java.util.regex.*;
● If one function calls another, they should be vertically close, and the caller
should be above the callee, if at all possible. This gives the program a
natural flow.
loadPage(pageName, context);
if (page == null) {
return notFoundResponse(context, request);
} else {
return makePageResponse(context);
}
}
private String getPageNameOrDefault(Request request, String defaultPageName) {
return …
}
● Nowadays, the monitors are too wide, that we can get 120 or more
characters across the screen.
Formatting
-- Horizontal Alignment
● We used horizontal alignment to accentuate certain structures. Line up all the variable names in a set of declarations,
or all the values in a set of assignment statements.
● In Java, this kind of alignment is not useful.
● Compare:
● Compare:
public class FitNesseServer implements SocketServer { private FitNesseContext
context; public FitNesseServer(FitNesseContext context) { this.context =
context; } public void serve(Socket s) { serve(s, 10000); } public void
serve(Socket s, long requestTimeout) { try { FitNesseExpediter sender = new
FitNesseExpediter(s, context);
sender.setRequestParsingTimeLimit(requestTimeout); sender.start(); }
catch(Exception e) { e.printStackTrace(); } } }
● To:
public class FitNesseServer implements SocketServer {
private FitNesseContext context;
● Compare:
return “/login”;
}
return total;
}
● Every programmer has his own favorite formatting rules, but if he works in
a team, then should follow the team rules.
● A team of developers should agree upon a single formatting style, and then
every member of that team should use that style.
● Easy:
public class Triangle implements Shape {
private Point topLeft;
private double side;
public double area() {
return (side*side) / 2;
}
}
Hard:
● It makes it hard to add new behaviors to existing objects.
perimeter() ?
Objects and Data Structures
● Data structures expose data and have no significant behavior.
● This makes it easy to add new behaviors to existing data structures.
● It makes it hard to add new data structures to existing functions.
● They have functions that do significant things, and they also have either
public variables or public gets and sets that make the private variables
public.
● Such hybrids make it hard to add new functions and also make it hard to
add new data structures.
….set session…
…
}
}
Classes
● The problem is that too many of us think that we are done once the
program works.
● We move on to the next problem rather than going back and breaking the
classes into decoupled units with single responsibilities.
Classes
● Organizing for Change
● For most systems, change is continual.
● Every change subjects us to the risk that the remainder of the system no
longer works as intended.
● Compare:
public class LotDao {
public long insertLot(header, details)
public List selectLot(operationId)
public List selectLots(initialDate, finalDate, account)
public boolean updateLot(header. details, operationId)
public boolean deleteLot(operationId)
public boolean deleteLots(operationIds)
….
}
Classes
● To:
public class InsertLotDao {
public long insertLot(header, details)
…
}
public class SelectLotDao {
public List selectLot(operationId)
public List selectLots(initialDate, finalDate, account)
…
}
public class UpdateLotDao {
public boolean updateLot(header. details, operationId)
…
}
public class DeleteLotDao {
public boolean deleteLot(operationId)
public boolean deleteLots(operationIds)
…
Fim