Files
org_web/books/clean-code/clean-code-notes.org
2026-02-22 22:09:44 +00:00

613 lines
20 KiB
Org Mode
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#+TITLE: Clean Code Notes
#+OPTIONS: num:nil tags:t toc:t
#+DATE: <2025-08-10 Sun 19:45>
#+FILETAGS: :books:notes:
/Author: Robert C. Martin/
* Chapter 1: Clean Code
Referenced Items:
- Implementation Patterns, Kent Beck, Addison-Wesley, 2007.
- Literate Programming, Donald E. Knuth, Center for the Study of Language and Information, Leland Stanford Junior University, 1992.
Principles mentioned:
Single Responsibility Principle (SRP), the Open Closed Principle (OCP), and the Dependency Inversion Principle (DIP)
* Chapter 2: Meaningful Names
** Use intention revealing names:
Names should reveal intent, there is no revelation in naming an integer ~d~, intending it stands for days. Instead, you should use the following names:
#+begin_src java
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
#+end_src
** Avoid disinformation
Don't postfix the word 'list' to the name 'accounts' unless it's actually a list. This is because the reader will /assume/ the data type of accountsList is indeed a list, instead choose a name like ~accountsGroup~.
** Make Meaningful Distinctions
While it is possible to name by being disinformative, it is also possible to name being non informative. Consider:
#+begin_src java
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
#+end_src
What on earth does ~a[1]~ and ~a[2]~ even stand for? We are better off using names like source and destination (due to the function's intent of copying the array).
Furthermore, noise words are redundant. We should never use the word ~variable~ when naming a variable, or ~table~ when naming a table.
** Use Pronouncable Names
This is quite straightforward. Do not use a name like ~genymdhms~ to refer to generation date, year, month, day, hour, minute,
and second. Instead use ~generationTimeStamp~.
** Use Searchable Names
In modern IDE's, it is still quite difficult to search for single-lettered variables. The writer states a personal preference of using single-letter names only as local variables and inside short methods. The following principle is given:
/The length of a name should correspond to the size of its scope/
** Avoid Encodings
Don't prefix variables with letters like m_ as was done in the past. Do not type encode as well, an example of this is: ~PhoneNumber phoneString;~ we can see the reader being misled into thinking the phone number is a String.
** Avoid Mental Mappings
Clarity is king, don't use a name for a variable that only you know what it stands for. For example: using the letter r as the lower-cased version of the url with the host and scheme
removed. That's being smart, not professional.
** Class Names
{{{epigraph_single(Classes and objects should have noun or noun phrase names like Customer\, WikiPage\,
Account\, and AddressParser. Avoid words like Manager\, Processor\, Data\, or Info in the name
of a class. A class name should not be a verb)}}}
** Method Names
Methods should have verb or verb phrase names.
** Don't be cute/Don't use puns
Do not use names that are only understandable to people whom you share jokes etc with. Furthermore, do not use colloquialism and slang in names.
- Example: ~HandGrenade~ instead of ~DeleteItems~
- Example: ~whack()~ instead of ~kill()~
** Pick one word per concept
If you have multiple choices for naming a concept, use one and stick with it. For instance if your options are fetch, get and retrieve, use one and stick with it throughout.
** Solution Domain Names and Problem Domain Names
Where possible use solution domain names, as the people that are going to be reading the code are programmers. Therefore, do not shy away from using CS terms, algorithm names, math names and so forth.
However when it is not possible to use solution domain names (in other words, when there is no "programmer-eese" then use the name from the problem domain. The other programmers can ask the domain expert for clarification. If the code is more to do with the problem domain concepts, then the names should be drawn from them.
** Add Meaningful Context
Enclose names with well-named classes, functions, or namespaces. When all else fails, then prefix with something that provides more context.
** Don't add gratuitous context
Shorter names are better than longer ones, generally. This is so long as the context and intent is clear. Don't add redundant or irrelevant additions to the name in the for the sake of 'context'.
* Chapter 3: Functions
** Functions should be small
Functions should be extremely short—ideally just a few lines, so they remain easy to understand and maintain.
Avoid deeply nested blocks; keep indentation shallow (12 levels), often replacing blocks with descriptive function calls.
A small function tells a concise, self-contained story, making it easier for readers to follow the programs intent.
The smaller the function, the more descriptive and accurate its name can be, improving self-documentation.
Large functions hide complexity and mix abstraction levels, making errors and duplication more likely.
** Do One Thing & One Level of Abstraction
A function should do exactly one conceptual task, and all its statements should exist at the same abstraction level.
Mixing details (like string concatenation) with high-level actions (like rendering a page) causes confusion.
The Stepdown Rule: organise functions so they read like a top down narrative, each calling the next abstraction level.
If you can extract a subfunction with a name that isnt a restatement, the original function is doing too much.
Functions that “do one thing” cannot be logically split into sections such as “initialize,” “process,” “finalize.”
** Switch Statements
Switch statements naturally violate “do one thing” by handling multiple cases; they also grow in size over time.
They break the Single Responsibility Principle (multiple reasons to change) and Open-Closed Principle (must change for new cases).
Preferred approach: hide switch statements inside a factory and dispatch behavior polymorphically through an interface.
Allow only one visible switch in your system, used solely for object creation, then encapsulate it.
This removes duplication and keeps high-level code unaware of concrete type distinctions.
Example:
#+begin_src java
public abstract class Employee {
public abstract boolean isPayday();
public abstract Money calculatePay();
public abstract void deliverPay(Money pay);
}
-----------------
public interface EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
-----------------
public class EmployeeFactoryImpl implements EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
switch (r.type) {
case COMMISSIONED:
return new CommissionedEmployee(r) ;
case HOURLY:
return new HourlyEmployee(r);
case SALARIED:
return new SalariedEmploye(r);
default:
throw new InvalidEmployeeType(r.type);
}
}
}
#+end_src
** Use Descriptive Names
A functions name should clearly state its purpose. Long, descriptive names beat short, cryptic ones.
Consistent naming patterns (shared verbs/nouns) help code read like a coherent story and aid predictability.
Descriptive names reduce the need for comments and improve comprehension without external documentation.
Renaming functions can reveal design improvements, so try multiple options until the best emerges.
IDE refactoring tools make renaming safe, encouraging experimentation.
** Function Arguments
{{{epigraph_single(The ideal number of arguments for a function is zero (niladic). Next comes one (monadic)\, followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldnt be used anyway.)}}}
Fewer arguments = better; aim for 02, avoid more than 3 unless absolutely necessary.
Flag arguments (booleans) are a red flag—they imply the function does multiple things.
Group related parameters into objects (e.g., ~Point~ for ~x~ and ~y~) to reduce argument count and improve clarity.
Output arguments are confusing—prefer returning values or mutating the owning objects state.
Match function/argument names in verbnoun or keyword style (e.g., ~writeField(name)~, ~assertExpectedEqualsActual~).
** Have No Side Effects
A function should do only what its name promises. Hidden state changes are misleading and dangerous.
Side effects create temporal coupling, meaning the function must be called in a certain sequence to be safe.
If unavoidable, make side effects explicit in the name (e.g., ~checkPasswordAndInitializeSession~).
Clear separation of command and query functions avoids ambiguity in meaning and intent.
Functions that modify state and return information often cause confusion and should be split.
** Error Handling
Error handling is a single responsibility—separate it from normal logic to keep both paths clear.
Prefer exceptions over error codes to avoid cluttering the happy path and to reduce dependency magnets.
Extract try/catch bodies into their own functions for cleaner structure.
See below:
#+begin_src java
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);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessage());
}
#+end_src
Keep functions small enough that occasional multiple return or break statements are acceptable.
Avoid duplication in error handling, and follow the DRY principle to ensure changes occur in one place.
* Chapter 4: Comments
Comments are a necessary evil—they exist because code fails to express intent clearly.
Outdated comments are dangerous; they can mislead more than help.
Strive to write code that explains itself; comments should be minimised.
Truth is always in the code, not in the comments.
** Comments Do Not Make Up for Bad Code
Dont use comments to excuse messy, unclear code. Clean the code instead.
Clear, expressive code with few comments > cluttered code with many comments.
#+begin_src java
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
// Better:
if (employee.isEligibleForFullBenefits())
#+end_src
** Good Comments
Only write them when unavoidable. Such as in the following instances:
*** Legal Comments
Sometimes required for copyright/licensing.
Keep them short; refer to standard licenses rather than embedding full legal text.
*** Informative Comments
Explain return values, formats, or patterns.
Prefer naming/structuring code to make such comments unnecessary.
#+begin_src java
// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
#+end_src
*** Explanation of Intent
Describe why a certain approach was chosen.
Helps future maintainers understand reasoning behind code.
#+begin_src java
public int compareTo(Object o)
{
if(o instanceof WikiPagePath)
{ WikiPagePath p = (WikiPagePath) o;
String compressedName = StringUtil.join(names, "");
String compressedArgumentName = StringUtil.join(p.names, "");
return compressedName.compareTo(compressedArgumentName);
}
return 1; // we are greater because we are the right type.
}
#+end_src
*** Clarification
Translate obscure values into readable terms.
Useful when working with unchangeable APIs/libraries, but risky if incorrect.
*** Warning of Consequences
Alert others about performance, thread-safety, or side effects.
For example, in code you can say:
~// SimpleDateFormat is not thread safe, so create each instance independently.~
*** \TODO\ Comments
Mark incomplete work or planned improvements.
Should be reviewed regularly; not an excuse for bad code.
*** Amplification
Highlight the importance of seemingly small details.
~// the trim is real important. It removes starting spaces...~
*** Javadocs in Public APIs
Public APIs should have clear documentation.
Javadocs can also mislead. Keep them accurate and up-to-date.
** Bad Comments
- Don't place a comment just because you feel like it.
- Remove redundant comments.
- Avoid misleading comments.
- Don't mandate everything (not every function needs a Javadoc).
- No need for journal comments, we have source control.
- Remove noise comments.
*** Dont Use a Comment When You Can Use a Function or Variable
Replace explanatory comments with expressive variable or function names.
Refactor code to remove comment redundancy.
*** Position Markers
Avoid decorative banners like ~// Actions ///////////////////////~, they add clutter.
Use sparingly and only for meaningful grouping.
Overuse makes them blend into background noise.
*** Closing Brace Comments
Comments on closing braces (} // while) are unnecessary for small, well structured functions.
Prefer short, clear functions over brace markers.
*** Attributions and Bylines
Dont add personal tags like ~/* Added by Rick */~, use version control for authorship history.
Such comments become outdated and irrelevant over time.
*** Commented Out Code
Never keep old code commented out; delete it and rely on version control history.
Commented-out code adds clutter and confuses future maintainers.
#+begin_src java
// Old cruft that should be deleted:
//hdrPos = bytePos;
//dataPos = bytePos;
#+end_src
*** HTML Comments
Avoid HTML markup inside code comments, it makes them harder to read in the editor.
Let documentation tools (like Javadoc) handle formatting.
*** Nonlocal Information
Comments should describe nearby code only, not unrelated parts of the system.
Avoid embedding global/system details that the function cant control.
*** Too Much Information
Avoid long, unnecessary historical or technical explanations.
Keep only relevant context (e.g., “RFC 2045” reference is fine, not the full spec).
*** Inobvious Connection
Ensure the relationship between comment and code is clear.
Dont make readers guess what part of the code the comment refers to.
#+begin_src java
// plus filter bytes ... but which part is “filter”?
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];
#+end_src
*** Function Headers
Short, single purpose functions with good names dont need header comments.
Let the function name explain the purpose.
*** Javadocs in Nonpublic Code
Javadocs are useful for public APIs, but excessive formality in internal code is just noise.
Internal methods should be self explanatory without full doc comments.
* Chapter 5: Formatting
** Vertical Formatting
- Vertical openness (blank lines) separates concepts and improves readability.
- Too much density makes code look like a muddle and harder to scan.
*** Vertical Density
- Tightly related lines should appear vertically dense.
- Avoid useless comments that interrupt association.
- Example (bad):
#+BEGIN_SRC java
public class ReporterConfig {
/**
,* The class name of the reporter listener
,*/
private String m_className;
#+END_SRC
- Example (better):
#+BEGIN_SRC java
public class ReporterConfig {
private String m_className;
private List<Property> m_properties = new ArrayList<>();
#+END_SRC
*** Vertical Distance
- Related concepts should be kept close together to reduce scrolling and searching.
- Local variables → as close to use as possible, usually at top of function.
- Control variables → declared inside loop headers.
- Instance variables → declared at the top of class (common Java convention).
#+BEGIN_SRC java
for (Test each : tests) {
count += each.countTestCases();
}
#+END_SRC
- Dependent functions: caller above callee for natural top-down reading.
#+BEGIN_SRC java
public Response makeResponse(...) {
String pageName = getPageNameOrDefault(request, "FrontPage");
loadPage(pageName, context);
return makePageResponse(context);
}
private String getPageNameOrDefault(Request request, String defaultPageName) { ... }
#+END_SRC
*** Conceptual Affinity
- Group functions with similar naming or shared purpose.
- Example (JUnit assert methods):
#+BEGIN_SRC java
static public void assertTrue(String message, boolean condition) { ... }
static public void assertTrue(boolean condition) { ... }
static public void assertFalse(String message, boolean condition) { ... }
static public void assertFalse(boolean condition) { ... }
#+END_SRC
*** Vertical Ordering
- Organise code top down:
- High-level concepts first (main logic).
- Lower-level details later.
- Readers can skim like a newspaper: important first, details last.
- Contrast: C/C++ require declarations before use, Java does not.
*** Summary - vertical
- Use vertical openness to separate concepts.
- Use vertical density to group related ones.
- Keep related variables, methods, and concepts close together.
- Order code top down for natural readability.
** Horizontal Formatting
Keep lines short. Most professional code naturally stays within ~45 characters, with ~80 as an upper bound. Lines beyond 100120 characters are generally careless.
Avoid shrinking font or overly wide monitors to fit more code, readability > fitting more characters.
Example limit guideline:
#+begin_src java
// Good (short)
int sum = a + b + c;
// Bad (too long)
int sum = a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q;
#+end_src
*** Horizontal Openness and Density
Use spaces to separate low-precedence operators (e.g., +, -, =) and improve readability.
Do not put spaces between function names and parentheses, they are closely related.
Example (Quadratic formula formatting):
#+begin_src java
return (-b + Math.sqrt(determinant)) / (2*a);
#+end_src
Separate arguments with spaces after commas to show distinct parameters.
*** Horizontal Alignment
Avoid aligning variable declarations or assignments in columns, it draws the eye to the wrong place.
Long aligned lists usually mean the class is too large and should be split.
Example (preferred unaligned):
#+begin_src java
// Prefer this:
private Socket socket;
private InputStream input;
private OutputStream output;
//instead of:
private Socket socket;
private InputStream input;
private OutputStream output;
#+end_src
*** Indentation
Indent according to scope hierarchy:
Classes → no indent
Methods → 1 level
Method bodies → 2 levels
Inner blocks → +1 for each nesting
Indentation makes scopes visually obvious; without it, code is hard to scan.
Avoid collapsing scopes onto one line, always use braces and proper indenting.
*** Dummy Scopes
Avoid dummy bodies in loops (e.g., empty while or for loops).
If unavoidable, place semicolon on its own indented line to make it visible.
#+begin_src java
while (dis.read(buf, 0, size) != -1)
;
#+end_src
** Team Rules
Teams must agree on a single formatting style for consistency.
Use IDE formatters to enforce these rules across all files.
Consistent formatting builds trust and reduces mental load for readers.
** Uncle Bobs Formatting Rules (Example in CodeAnalyzer.java)
Short, clear methods with consistent spacing and indentation.
Use spaces around assignment and low-precedence operators, no space for high precedence operators.
Avoid deeply nested structures. Prefer clear, flat logic.
Example snippet:
#+begin_src java
private void measureLine(String line) {
lineCount++;
int lineSize = line.length();
totalChars += lineSize;
lineWidthHistogram.addLine(lineSize, lineCount);
recordWidestLine(lineSize);
}
#+end_src