Skip to content
Knowledge Hub

Pragmatic clean code: write code the next person can change safely Intermediate

This cluster answers: what “clean” means when measured by the cost of changing code, and how to refactor a real service step by step without breaking production.

“Clean” doesn’t mean pretty or short. This cluster’s working definition:

  • Cheap to change: a business change touches few files and few branches.
  • Safe to change: after the change, tests catch regressions and errors aren’t swallowed.
  • Easy to review: a reviewer grasps the intent in a few minutes, without running the code in their head.

Every rule must answer: “where does it make the next change cheaper or safer?” If it can’t, it’s dogma; skip it.

Loading diagram…

Part Question it answers
Safe refactoring What’s wrong with the “before” code? How do you refactor while proving behavior doesn’t change?
Functions and side effects How to use guard clauses, drop flags, add constants with units, separate a pure core from I/O?
Errors, comments, DRY Which errors are results and which are exceptions? What to comment? When is duplication better than abstraction?
Common mistakes BAD/GOOD code that devs often write, and exactly when it blows up
Symptom in code Default fix Why
if nested 3-4 levels Guard clauses, return early The main path reads straight through
boolean parameters (express, dryRun) Split the function, or use a union type Nobody can read the call site checkout(x, true, false)
More than ~3 parameters traveling together Parameter object Adding a field doesn’t break every call site
500000, 0.9, 12 in logic Named constants with units Change in one place, greppable
Computation mixed with DB/HTTP/new Date() Functional core, imperative shell Test the core without mocks
catch (e) { console.log(e) } Rethrow with context + cause, or actually handle it A swallowed error = silently wrong data
Expected business branches (declined, out of stock) Return a typed result Exceptions are for the unexpected
Two similar pieces of code Wait for the third The wrong abstraction costs more than duplication
Comments narrating what the code does Delete them, name things better; comment why “What” comments go stale quickly
  1. Are refactor commits and behavior-change commits separate? Does the refactor commit change tests (it shouldn’t)?
  2. Is there any catch that doesn’t rethrow, doesn’t really handle, and only logs?
  3. Do thrown errors carry enough context (id, amount, key) and keep the cause?
  4. Are expected business branches using exceptions as control flow?
  5. Are there boolean parameters on public methods? Can the call site be read without opening the definition?
  6. Do business numbers and strings have names and units? Is any value repeated in several places?
  7. Can the decision logic be tested without mocking DB/HTTP/the clock?
  8. Is there a new abstraction with only one caller, or a shared function that just got a flag added to “fit” a new caller?

Next: Safe refactoring