Writing Clean Code: Principles That Actually Matter
I’ve read Uncle Bob’s “Clean Code” (and the internet’s many takes on it). Some advice is gold, some is… debatable. Here are the principles I actually use and why they matter.
Principle 1: Names Should Explain Themselves
Bad:
const d = 86400000;
Good:
const millisecondsPerDay = 86400000;
// or
const MS_PER_DAY = 86400000;
If you need a comment to explain what a variable does, the name isn’t good enough.
More examples:
// ❌ Bad
function proc(x) { ... }
// ✅ Good
function processPayment(order) { ... }
// ❌ Bad
const stuff = [];
// ✅ Good
const activeUsers = [];
Exception: Loop counters (i, j, k) are fine in small scopes. Everyone knows what they mean.
Principle 2: Functions Should Do One Thing
Bad:
function handleUserData(user) {
// Validate
if (!user.email) throw new Error('Email required');
// Transform
user.email = user.email.toLowerCase();
// Save
database.save(user);
// Send email
emailService.sendWelcome(user);
// Log
logger.info('User created');
}
Good:
function createUser(user) {
validateUser(user);
const normalizedUser = normalizeUser(user);
const savedUser = saveUser(normalizedUser);
sendWelcomeEmail(savedUser);
logUserCreation(savedUser);
return savedUser;
}
Each function does one thing at one level of abstraction.
Principle 3: Avoid Flag Arguments
Bad:
function bookFlight(passenger, isPremium) {
if (isPremium) {
// Premium logic
} else {
// Standard logic
}
}
Good:
function bookPremiumFlight(passenger) { ... }
function bookStandardFlight(passenger) { ... }
Boolean arguments usually mean your function is doing two things.
Principle 4: Keep Functions Small
Not a hard rule, but if your function doesn’t fit on one screen, it’s probably doing too much.
My guideline: If I can’t understand what a function does in 10 seconds, it needs refactoring.
Principle 5: Don’t Repeat Yourself (DRY)
Bad:
// In 5 different places
if (!user || !user.email || user.email.trim() === '') {
throw new Error('Invalid email');
}
Good:
function requireValidEmail(user) {
if (!user?.email?.trim()) {
throw new Error('Invalid email');
}
}
// Now use it everywhere
requireValidEmail(user);
But don’t go crazy: Sometimes duplication is better than the wrong abstraction.
Principle 6: Comments Should Explain WHY, Not WHAT
Bad:
// Increment counter
counter++;
I can see that. Thanks.
Good:
// Using exponential backoff to avoid overwhelming the API
await sleep(Math.pow(2, retries) * 1000);
This explains the reasoning behind the code.
Even better:
const backoffMs = Math.pow(2, retries) * 1000;
await sleep(backoffMs); // Exponential backoff
Name explains what, comment explains why.
Principle 7: Return Early
Bad:
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.isPaid) {
// Process order
}
}
}
}
Nesting hell!
Good:
function processOrder(order) {
if (!order) return;
if (order.items.length === 0) return;
if (!order.isPaid) return;
// Process order
}
Guard clauses make the logic flat and clear.
Principle 8: Use Constants for Magic Numbers
Bad:
setTimeout(callback, 3600000);
Good:
const ONE_HOUR_MS = 60 * 60 * 1000;
setTimeout(callback, ONE_HOUR_MS);
Now I don’t have to count zeros.
Principle 9: Consistent Style Matters
Use a formatter (Prettier, Black, gofmt). Never debate style again.
These should all look the same:
function getUserById(id) { ... }
function getOrderById(id) { ... }
function getProductById(id) { ... }
Not a mix of camelCase, snake_case, and PascalCase.
Principle 10: Delete Dead Code
Bad:
// This is old code but might be useful someday
// function oldImplementation() {
// ...
// }
Good:
// Just delete it
That’s what Git is for. If you need it, check the history.
What I DON’T Obsess Over
Function Length
Some people say “functions must be < 20 lines.” I say: if it’s clear and focused, length doesn’t matter.
Perfect Abstraction
Don’t abstract until you have 3 examples. Premature abstraction is worse than duplication.
100% Coverage
Write tests for important logic. Don’t test getters/setters just to hit 100%.
Zero Comments
Some logic is complex. A good comment explaining the business rule is valuable.
The Real Goal
Clean code isn’t about following rules. It’s about:
- Being kind to future developers (including future you)
- Making bugs obvious
- Enabling change without fear
If your code does these things, it’s clean enough.
My Process
- Make it work - Get the feature done
- Make it right - Refactor for clarity
- Make it fast - Only if needed
Most code doesn’t need step 3.
Tools That Help
- ESLint/Prettier - Automatic style
- TypeScript - Catch mistakes early
- Code reviews - Fresh eyes catch issues
- Refactoring tools - VS Code’s rename/extract are amazing
Conclusion
Clean code is a journey, not a destination. Every refactor makes it better. Don’t aim for perfection—aim for “better than yesterday.”
And remember: working code that’s slightly messy is better than perfect code that doesn’t exist.
Now go clean something up! 🧹