Sagadenov.com

A personal journey through ideas, leadership and innovation.

Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin

“Clean code is not code that follows rules. It’s code that’s written with care.”
— Robert C. Martin​

Robert Martin argues that bad code slows everything down — it blocks team progress, makes changes risky, and breaks the product.
In contrast, clean code is:

  • readable;
  • logically structured;
  • covered with tests;
  • free of duplication;
  • understandable without comments.

But most importantly — it’s written as if the author cared.


Key takeaways:


A function should do one thing
If a function does more than one thing — that’s a red flag. Better to split than to explain with a flag.

render(true); // what does this even mean?

// Better
renderForSuite();
renderForSingleTest();

Clear and self-explanatory.


Comments are the last resort
If you need a comment to explain something, the code should probably be rewritten.

Martin also emphasizes that code should explain itself. He challenges the traditional idea that documentation lives separately in Confluence or Wiki pages. Instead, he insists that clear naming and structure are documentation. If you’re writing an API, make it self-explanatory — your users shouldn’t need to read a manual. He writes: “The proper use of functions and naming should make the code self-documenting.”
One of the best examples in the book shows how deeply nested logic is turned into a clean, readable method.

if (pageData.hasAttribute("Test")) {
  if (includeSuiteSetup) {
    WikiPage suiteSetup = ...
    buffer.append("!include -setup .")
  }
  ...
}
//Better
public static String renderPageWithSetupsAndTeardowns(...) {
  includeSetupPages(...);
  ...
  includeTeardownPages(...);
}

Instead of layers of logic — clear, named actions.


The Boy Scout Rule
If you open a file — leave it cleaner than you found it. Even a small rename or removed comment is progress.

// Before
int d; // elapsed time in days

// After
int elapsedDays;

Flag arguments are a code smell
They mean the function does more than one thing. Split into separate methods — it improves clarity and reduces branching.

// Bad
save(data, true); // true = draft?

// Better
saveDraft(data);
saveFinalVersion(data);

Formatting = thinking structure
Martin promotes the newspaper metaphor: start with headlines, then dive into details.
Code should be readable top-down like an article.

// Bad (details come first)
int x = 5;
...
mainLogic();

// Better (headline first)
public void processOrder() {
  validate();
  calculateTotals();
  notifyCustomer();
}

Formatting is communication, not decoration
Code is read far more often than it is written. That’s why formatting matters — it guides the reader’s eye.

Martin refers to the newspaper metaphor:

  • first, show high-level structure (like imports and declarations)
  • then, the overview (top-level functions)
  • only then, the details
// Bad formatting — hard to read
public void calculate(){int sum=0;for(int i=0;i<orders.length;i++){sum+=orders[i].getAmount();}System.out.println(sum);}

// Better formatting — communicates intent
public void calculate() {
  int sum = 0;
  for (int i = 0; i < orders.length; i++) {
    sum += orders[i].getAmount();
  }
  System.out.println(sum);
}

Good formatting supports understanding, not just aesthetics.


Tests should be F.I.R.S.T.

  1. Fast «Tests should run quickly.»
  2. Independent «Tests should not depend on each other.»
  3. Repeatable «They should run in any environment and produce the same result.»
  4. Self-validating «A test should have a boolean output. It either passes or fails.»
  5. Timely — written before the implementation, not after «They need to be written before the production code.»

Small, constant improvements matter
You don’t need to clean everything at once.
Martin encourages many small, safe edits — like renaming a variable, simplifying a loop, or extracting a method.
Each improvement compounds. Over time, your codebase becomes easier to work with.

// Before
for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}

// After
for (String item : list) {
  System.out.println(item);
}

Classes should be small and focused
The size of a class is not measured by lines of code but by the number of responsibilities it handles.
If it does more than one thing — it’s harder to test, understand, and change.
Split responsibilities into separate classes, each with a single reason to change.

// Bad
class ReportManager {
  void renderReport() { ... }
  void calculateTotals() { ... }
}

// Better
class ReportRenderer { ... }
class ReportCalculator { ... }

Avoid duplication — abstract instead
Repetition is a sign of missing abstraction.
When you see the same logic or structure more than once, it’s time to extract a method, use a design pattern, or rethink your structure.
Duplication increases maintenance cost and hides bugs.

// Before
sendEmailToAdmin();
sendEmailToUser();
sendEmailToSupport();

// After
sendEmail("admin");
sendEmail("user");
sendEmail("support");

Don’t return or pass null
Returning null forces the caller to write defensive code.
It increases the risk of runtime errors and clutters logic.
Instead, return empty objects, empty collections, or use Optional types when appropriate.

// Bad
User user = repo.findById(id);
if (user != null) {
  sendEmail(user);
}

// Better
repo.findById(id).ifPresent(this::sendEmail);

Prefer exceptions over error codes
Returning error codes pollutes the main logic and forces repetitive checks.
Clean code handles errors using exceptions — separating error-handling paths from normal execution.
Keep the “happy path” clear and linear.

// Bad
if (save(user) == -1) {
  log("Failed");
}

// Better
try {
  save(user);
} catch (SaveFailedException e) {
  log("Failed");
}

Inject dependencies — don’t create them
Code should not be responsible for instantiating its own dependencies.
Instead, they should be provided from the outside (constructor injection, factory, or IoC container).
This reduces coupling and makes testing easier.

// Bad
class UserController {
  private EmailService service = new EmailService();
}

// Better
class UserController {
  private final EmailService service;

  public UserController(EmailService service) {
    this.service = service;
  }
}

Final thoughts

Clean Code isn’t about code style — it’s about how you think as a software engineer.
It encourages care, discipline, and long-term thinking.
After reading it, you’ll start to question shortcuts like “it works for now” — and you’ll build the habit of writing code that others (and your future self) will appreciate.