Code is read far more often than it is written. Clean code means names that reveal intent, small functions with a single job, and structure you can navigate without thinking too hard. The goal is to be able to make self-explanatory code that can be easily navigated while reducing the confusion tax for new contributors and future you. Code that makes you proud to ship it, and to maintain it.
Key principles
- Names should reveal intent — if you need a comment to explain a variable, rename it
- Use SOLID principles to keep code modular and decoupled, but avoid over-engineering
- Functions should do one thing, at one level of abstraction
- Prefer pure functions and immutability where the domain allows
- Kill dead code on sight — version control remembers it
- Tests are documentation that cannot lie
Common pitfalls
- Reading about it without doing it — refactor your own code as you learn
- Over-engineering before it is necessary — YAGNI is a real principle
- Over-commenting code that should just be renamed, or saying obvious things
- Letting code rot — if it isn't clean, fix it or delete it
SOLID is a way of thinking, not a checklist. Each principle targets a specific failure that shows up as systems grow: rigidity, fragility, immobility, and viscosity. Apply them where change is likely; ignore them where it is not.
Key principles
- S — Single Responsibility: a class has one reason to change
- O — Open/Closed: open for extension, closed for modification
- L — Liskov Substitution: subtypes must be usable wherever the base type is
- I — Interface Segregation: many small interfaces beat one fat one
- D — Dependency Inversion: depend on abstractions, not concretions
Common pitfalls
- Treating SOLID as mandatory everywhere — it is overkill for scripts and prototypes
- Inversion-of-control frameworks so abstract the code becomes untraceable
- Interface explosion — one interface per method with no real substitution happening
Architecture is the set of decisions that are expensive to change. Good architecture keeps those decisions few, explicit, and reversible — drawing boundaries around what varies so the stable core never has to know about it.
Key principles
- Depend on abstractions across boundaries; keep the domain free of frameworks
- Make the expensive-to-change decisions explicit, and defer them as long as you can
- The structure should reveal what the system does, not which framework it uses
- A boundary is worth its cost only where two sides change for different reasons
Tests pin down behavior so you can change code without fear. The aim is fast feedback and confidence, not coverage theater — test behavior through public seams, not the implementation behind them.
Key principles
- Test behavior, not implementation — couple to the contract, not the internals
- A failing test should point at exactly one cause
- Fast tests get run; slow suites get skipped
- TDD where the design is unclear; test-after where it is obvious
Refactoring is a disciplined technique: small, behavior-preserving steps backed by tests. It is how clean code stays clean as requirements move out from under it.
Key principles
- Refactor in small steps, staying green between each one
- Keep refactoring commits separate from behavior changes
- Refactor before adding to messy code, not after
- If it is hard to test, that is the design talking — refactor toward seams
Automation is leverage. It is also a tax — every script is code you must maintain. The skill is knowing which tasks earn their automation.