You inherit a codebase. No tests. Variables named data, temp, and x. Functions span 300 lines. Every change breaks something unrelated. This isn't bad luck—it's the absence of discipline. Clean code isn't aesthetic; it's survival.
Names Reveal Intent
A variable named d tells you nothing. elapsedTimeInDays tells you everything. Good names eliminate comments. If you need a comment to explain a name, rename it. Classes answer what. Methods answer how. Booleans sound like questions: isActive, hasPermission. Avoid mental mapping. The next reader—often you in six months—shouldn't decode abbreviations.
Functions: Small, Single, Pure
A function does one thing. It does it well. It does it only. If you describe it with “and,” split it. Keep it under 20 lines. Zero arguments is ideal. One or two is tolerable. Three demands an object. Flag arguments (render(true)) scream for two functions. Side effects? Isolate them. Pure functions are testable, cacheable, and reason-able.
"Functions should do one thing. They should do it well. They should do it only.
— Robert C. Martin
SOLID Isn't Dogma—It's Leverage
Single Responsibility: One reason to change. Open/Closed: Extend without modifying. Liskov Substitution: Subtypes behave like base types. Interface Segregation: Many specific interfaces beat one fat one. Dependency Inversion: Depend on abstractions, not concretions. Apply them when pain appears, not preemptively. A 50-line class doesn't need DI. A payment gateway does.
| Principle | Signal to Apply |
|---|---|
| SRP | Class has >3 reasons to change |
| OCP | Frequent if/else for new types |
| LSP | Subclass throws NotImplemented |
| ISP | Clients depend on unused methods |
| DIP | High-level module imports low-level concrete |
Comments Are Failures
Every comment is an apology for unclear code. // increment i insults intelligence. TODO comments rot. Commented-out code is version control's job. Exceptions: legal headers, regex explanations, and why not what. // HACK: API returns null on 204 until v3 saves hours. Delete the rest.
Error Handling Without Chaos
Try/catch blocks scatter logic. Extract them. try { return parseConfig(file); } catch { return defaultConfig; } becomes parseConfigSafe(file). Use Result types or Optionals over null. Null checks are technical debt. Throw early, catch late, log once. Custom exceptions (InsufficientFundsError) beat generic ones with error codes.
Structure Screams Architecture
Folder structure should reveal the domain, not the framework. src/controllers says nothing. src/billing, src/shipping, src/user screams business. Group by feature, not layer. Colocate tests with code. order.ts sits beside order.test.ts. Barrel files (index.ts) hide internal churn. Public API stays stable; private guts rotate.
✦
Your Next Refactor Starts Now
Pick one file. Rename three vague variables. Extract one 50-line function into three. Delete five useless comments. Run tests. Commit. Repeat tomorrow. Clean code isn't a destination—it's a habit. The codebase you touch next will thank you.










