Clean Code and SOLID without dogma: what to actually apply
Clean code isn't aesthetics. It's the difference between changing a rule in an afternoon and spending two weeks afraid to touch a file. The principles made famous by Robert C. Martin, and the refactoring practices described by Martin Fowler, exist to reduce that fear — not to produce small files out of obligation.
Readability is economy, not vanity
Code is read far more times than it is written. Precise names, functions with a clear single responsibility, and the absence of surprises reduce the time anyone needs to understand what already exists — and that time is the main cost of maintenance.
The practical test is simple: can someone who didn't write that piece predict what it does just from the name and signature? If they need to read the whole implementation to be sure, there's room to improve.
- Names that reveal intent, not implementation.
- Functions short enough to fit in your head, not to satisfy an arbitrary metric.
- Explicit side effects, never hidden behind an innocent-looking name.
SOLID explained by the pain it avoids
Single responsibility keeps a reporting change from breaking a tax calculation. Open/closed avoids a cascade of changes when a new type appears. Liskov substitution keeps an alternative implementation from breaking whoever relies on the abstraction. Interface segregation avoids forcing implementers to fill in methods that don't make sense. Dependency inversion keeps business rules from depending on a database or vendor detail.
None of these principles calls for an extra layer by default. They all call for attention to the point where change tends to hurt.
When not to apply them
An abstraction created too early is expensive: it freezes a hypothesis before evidence exists. Duplicating once is usually cheaper than abstracting wrong. The third repetition is a good sign that the real pattern has emerged.
Kent Beck sums up the practical sequence well: make it work, make it right, make it fast. Reversing that order is the origin of much of the unnecessary complexity found in old codebases.
Refactoring is routine backed by tests
Refactoring means changing structure without changing behavior — and that's only verifiable with tests. Without a safety net, what looks like refactoring is actually a risky rewrite. Small, continuous improvements made alongside each release avoid the renovation project that never gets approved.
In short
Apply a principle where change hurts, not where a checklist demands it. Clean code is what lets you change things confidently tomorrow.
Use cases
Legacy codebase without tests
Characterization tests first, to freeze current behavior before any structural change.
Business rule scattered across the codebase
Consolidation into a domain module, with the interface depending on it rather than the other way around.
Vendor integration
Isolation behind a thin abstraction, allowing the vendor to be swapped without touching the rule.
Common mistakes
- Creating an interface for everything, with no second implementation ever planned.
- Splitting functions by line count instead of by responsibility.
- Treating SOLID as a review checklist instead of a decision criterion.
- Postponing refactoring until it becomes a separate project.
Best practices
- Leave the code a little better than you found it, with every change.
- Abstract on the third repetition, with evidence.
- Write the test before refactoring a critical piece.
- Prefer composition over inheritance when in doubt.
Recommended books
Clean Code — Robert C. Martin
Objective criteria for readability and responsibility at the function level.
Refactoring — Martin Fowler
Catalog of safe transformations backed by tests.
A Philosophy of Software Design — John Ousterhout
A valuable counterpoint on module depth and the cost of excessive fragmentation.
Go deeper
Frequently asked questions
- Does SOLID still make sense today?
- Yes, as a decision criterion about coupling and dependency. As a memorized rule applied to everything, it creates complexity without benefit.
- What's the ideal size for a function?
- Whatever is enough to express one clear responsibility. Line count is a hint, not a criterion.
- When should you refactor?
- Continuously, alongside delivery, and always with tests covering the behavior that must not change.
- How do you convince the team to invest in quality?
- Translate it into time: show the time lost to rework and to fear of touching critical areas.
References
- Robert C. Martin — responsibility and dependency principles
- Martin Fowler — refactoring and code smells
- John Ousterhout — complexity and module design
Original content by the i9 Conecty team. Classic concepts are explained in our own words and credited to their authors.