Clean Code Part 1: The Fundamentals
Robert C. Martin’s Clean Code is essential reading for any serious developer. This first part covers the foundation: what clean code actually means, how to name things properly, how to write functions that do one thing well, and when comments help versus hurt.
Chapter 1: Clean Code
What Is Clean Code?
The book opens with a fundamental question: what separates good code from bad code? Martin collects perspectives from legendary programmers:
- Bjarne Stroustrup (creator of C++): Clean code is elegant and efficient. It does one thing well. Bad code tempts people to add more mess.
- Grady Booch: Clean code reads like well-written prose. It is crisp and to the point.
- Dave Thomas: Clean code can be read and enhanced by others. It has unit tests and meaningful names.
- Michael Feathers: Clean code looks like it was written by someone who cares.
- Ward Cunningham: Clean code makes the language look like it was made for the problem.
The Cost of Bad Code
Bad code slows teams down exponentially. Martin introduces the concept of the Total Cost of Owning a Mess:
- Initial development is fast but maintenance becomes a nightmare
- Each change risks breaking something else
- New team members take months to become productive
- Eventually, productivity approaches zero
The solution is not to “clean up later.” Later never comes. The only way to go fast is to keep the code clean from the start.
The Boy Scout Rule
“Leave the campground cleaner than you found it.”
Applied to code: every time you touch a file, leave it a little better. Rename a variable. Break up a long function. Remove dead code. Small improvements compound over time.
We Are Authors
Programmers are authors, and code is read far more than it is written. Martin cites a 10:1 ratio of reading to writing. This means readability is not optional—it directly affects productivity.
Chapter 2: Meaningful Names
Naming is one of the hardest problems in programming, but it matters enormously. A good name tells you what something does without needing to read the implementation.
Use Intention-Revealing Names
The name should answer: Why does it exist? What does it do? How is it used?
// Bad
int d; // elapsed time in days
// Good
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
If a name requires a comment to explain it, the name is wrong.
Avoid Disinformation
Don’t use names that mean something different than intended:
- Don’t call a grouping
accountListunless it’s actually aList - Avoid names that vary in small ways:
XYZControllerForEfficientHandlingOfStringsvsXYZControllerForEfficientStorageOfStrings - Don’t use lowercase
Lor uppercaseOas variable names (they look like 1 and 0)
Make Meaningful Distinctions
If names must be different, they should mean something different:
// Bad - noise words add nothing
getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
// What's the difference? Nobody knows.
Avoid number series (a1, a2, a3) and noise words (ProductInfo, ProductData - what’s the difference?).
Use Pronounceable Names
// Bad
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
}
// Good
class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
}
Programming is a social activity. You need to discuss code with others.
Use Searchable Names
Single-letter names and numeric constants are impossible to find:
// Bad - try searching for "7"
for (int j = 0; j < 7; j++) {
s += t[j] * 4;
}
// Good
int DAYS_PER_WEEK = 7;
int WORK_HOURS_PER_DAY = 4;
for (int day = 0; day < DAYS_PER_WEEK; day++) {
sum += hours[day] * WORK_HOURS_PER_DAY;
}
The length of a name should correspond to the size of its scope.
Avoid Encodings
Hungarian notation and type prefixes were useful when compilers didn’t check types. They’re obsolete now:
// Bad
String m_strName;
IShapeFactory shapeFactory;
// Good
String name;
ShapeFactory shapeFactory;
Modern IDEs and compilers make these prefixes unnecessary noise.
Class and Method Names
- Class names should be nouns or noun phrases:
Customer,WikiPage,Account - Method names should be verbs or verb phrases:
postPayment,deletePage,save
Use get, set, and is prefixes for accessors, mutators, and predicates following the JavaBean standard.
Pick One Word Per Concept
Use the same word for the same abstract concept:
- Don’t mix
fetch,retrieve, andgetfor the same operation - Don’t mix
controller,manager, anddriverfor equivalent classes
Consistency matters more than cleverness.
Don’t Pun
Avoid using the same word for two different purposes. If you have an add method that concatenates values, don’t use add for a method that inserts into a collection—use insert or append.
Use Solution Domain Names
Programmers read your code. Use computer science terms like Queue, Visitor, Factory when appropriate. Don’t make readers map your domain-specific names to concepts they already know.
Add Meaningful Context
Most names need context. state means nothing alone, but addrState or wrapping in an Address class makes it clear.
// Bad - what do these mean?
String firstName;
String lastName;
String street;
String city;
String state; // State of what?
// Good - context through class
class Address {
String firstName;
String lastName;
String street;
String city;
String state; // Now it's obvious
}
Chapter 3: Functions
Functions are the first line of organization. Getting them right is essential.
Small!
The first rule: functions should be small. The second rule: they should be smaller than that.
Martin argues that functions should rarely be more than 20 lines. Ideally, 5-10 lines. Each function should tell a story at a single level of abstraction.
Do One Thing
“Functions should do one thing. They should do it well. They should do it only.”
How do you know if a function does one thing? Try to extract another function from it with a name that isn’t just a restatement of the implementation.
One Level of Abstraction per Function
Mixing abstraction levels confuses readers:
// Bad - mixed abstraction levels
public void render() {
StringBuffer html = new StringBuffer();
html.append("<html>");
html.append(generateHeader());
for (PageData data : pages) {
html.append(data.getTitle()); // Low level
}
renderFooter(html); // High level
}
Keep each function at one abstraction level. High-level functions call mid-level functions, which call low-level functions.
The Stepdown Rule
Code should read like a newspaper article—headline, synopsis, then details. Each function should be followed by functions at the next level of abstraction.
Switch Statements
Switch statements inherently do N things. They can be tolerable if they appear only once, create polymorphic objects, and are hidden behind an inheritance relationship:
// Bad - will grow with each new type
public Money calculatePay(Employee e) {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
}
}
// Good - use polymorphism
public abstract class Employee {
public abstract Money calculatePay();
}
Use Descriptive Names
Long descriptive names are better than short enigmatic ones. A long name is better than a long comment.
// Bad
public void process();
// Good
public void includeSetupAndTeardownPages();
Don’t be afraid to spend time choosing names. Try several names and read the code with each.
Function Arguments
The ideal number of arguments is zero. Then one. Then two. Three arguments should be avoided. More than three requires special justification.
Why fewer is better:
- Easier to understand
- Easier to test (fewer combinations)
- Output arguments are confusing
Common monadic forms (one argument):
- Asking a question:
boolean fileExists("myFile") - Transforming input:
InputStream fileOpen("myFile") - Events:
void passwordAttemptFailedNtimes(int attempts)
Flag arguments are ugly. They immediately signal the function does more than one thing:
// Bad
render(true);
// Good
renderForSuite();
renderForSingleTest();
Have No Side Effects
Side effects are lies. A function promises to do one thing but also does hidden things:
// Bad - hidden side effect
public boolean checkPassword(String username, String password) {
User user = UserGateway.findByName(username);
if (user != null && user.password.equals(password)) {
Session.initialize(); // Side effect!
return true;
}
return false;
}
This function says it checks a password but also initializes a session. Rename it to checkPasswordAndInitializeSession if you must keep the behavior.
Command Query Separation
Functions should either do something or answer something, not both:
// Bad - what does this return?
if (set("username", "bob")) ...
// Good - separated
if (attributeExists("username")) {
setAttribute("username", "bob");
}
Prefer Exceptions to Returning Error Codes
Error codes force callers into immediate handling:
// Bad
if (deletePage(page) == E_OK) {
if (registry.deleteReference(page.name) == E_OK) {
logger.log("page deleted");
} else {
logger.log("reference delete failed");
}
} else {
logger.log("page delete failed");
}
// Good
try {
deletePage(page);
registry.deleteReference(page.name);
} catch (Exception e) {
logger.log(e.getMessage());
}
Extract Try/Catch Blocks
Error handling is one thing. Extract the bodies of try/catch blocks into separate functions:
public void delete(Page page) {
try {
deletePageAndAllReferences(page);
} catch (Exception e) {
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throws Exception {
deletePage(page);
registry.deleteReference(page.name);
}
DRY - Don’t Repeat Yourself
Duplication is the root of all evil in software. Every piece of knowledge should have a single, unambiguous representation.
Chapter 4: Comments
Comments are, at best, a necessary evil. They compensate for our failure to express ourselves in code.
Comments Don’t Make Up for Bad Code
The proper use of comments is to compensate for our failure to express intent in code. If you find yourself needing a comment, first try to refactor the code to make it clear without the comment.
// Bad - needs comment to explain
// Check to see if employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
// Good - self-documenting
if (employee.isEligibleForFullBenefits())
Good Comments
Some comments are necessary and beneficial:
Legal comments:
// Copyright (C) 2025 by Example Corp. All rights reserved.
Informative comments:
// Returns an instance of the Responder being tested
protected abstract Responder responderInstance();
Explanation of intent:
// We prefer to load the entire file to avoid multiple I/O calls
// even though it uses more memory
Clarification:
assertTrue(a.compareTo(b) == -1); // a < b
Warning of consequences:
// Don't run unless you have time to kill
public void testWithReallyBigFile() { ... }
TODO comments:
// TODO: This method should be removed after v2.0 migration
Amplification:
// The trim is really important. It removes trailing spaces
// that could cause the item to be incorrectly matched.
String listItemContent = match.group(3).trim();
Bad Comments
Most comments fall into this category:
Mumbling:
// Utility method to handle loading. See class notes.
// (What notes? Where? This helps nobody.)
Redundant comments:
// The name
private String name;
// The version
private String version;
Misleading comments: Comments that don’t accurately describe the code are worse than no comments.
Mandated comments: Requiring Javadoc for every function pollutes the code without adding value.
Journal comments:
// 2025-01-15: Added method foo
// 2025-01-16: Fixed bug in foo
// (This is what version control is for)
Noise comments:
// Default constructor
public MyClass() {}
Position markers:
// /////////////////// Actions /////////////////////
Closing brace comments:
} // end if
} // end while
} // end try
If your blocks are so long you need these, your function is too long.
Attributions:
// Added by Bob
Use version control instead.
Commented-out code: Delete it. Version control remembers. Commented code accumulates like sediment.
Nonlocal information: Don’t describe system-wide behavior in a local comment.
Too much information: Don’t put historical discussions or irrelevant details in comments.
Key Takeaways
-
Clean code is not about perfection—it’s about caring. It’s written by someone who took the time to keep it simple and orderly.
-
Names are crucial. Spend time choosing names. Change names when you find better ones. Good names reduce the need for comments.
-
Functions should be small and do one thing. Extract until you can’t extract anymore. The ideal function is 5-10 lines.
-
Comments are a last resort. If you need a comment, first try to rewrite the code. Comments lie and decay; code tells the truth.
-
The Boy Scout Rule compounds. Small, continuous improvements beat sporadic rewrites.