Writing Clean Code: Principles That Actually Matter
I've reviewed hundreds of pull requests over the years. The difference between good and great developers isn't cleverness—it's clarity. Here are the patterns that make code maintainable.
Naming Things
The most important skill in programming:
// Bad
const d = new Date();
const arr = users.filter(u => u.a);
function proc(x) { /* ... */ }
// Good
const registrationDate = new Date();
const activeUsers = users.filter(user => user.isActive);
function processPayment(order) { /* ... */ }
Naming Conventions
- Variables: Nouns describing what they hold
- Functions: Verbs describing what they do
- Booleans: Start with is, has, can, should
- Constants: SCREAMING_SNAKE_CASE
const MAX_RETRY_ATTEMPTS = 3;
const isLoggedIn = true;
const hasPermission = checkPermission(user);
function validateEmail(email) { /* ... */ }
Functions
Keep Them Small
// Bad: Function doing too much
function processOrder(order) {
// validate
// calculate tax
// apply discount
// update inventory
// send email
// log analytics
// 200 more lines...
}
// Good: Single responsibility
async function processOrder(order) {
validateOrder(order);
const total = calculateTotal(order);
await updateInventory(order.items);
await notifyCustomer(order);
trackOrderPlaced(order);
}
Limit Parameters
// Bad
function createUser(name, email, phone, address, city, pincode, country) { }
// Good
function createUser(userData) {
const { name, email, phone, address } = userData;
}
// Better with TypeScript
interface UserData {
name: string;
email: string;
phone?: string;
address?: Address;
}
Comments
Comments should explain WHY, not WHAT.
// Bad: Obvious comment
// Loop through users
users.forEach(user => { /* ... */ });
// Good: Explains why
// Using forEach instead of map because we need side effects
// for legacy analytics tracking
users.forEach(user => trackUser(user));
// Good: Complex business logic
// Discount only applies if: order > ₹500, user registered > 30 days,
// and hasn't used discount this month (compliance requirement DISC-234)
if (isEligibleForDiscount(order, user)) {
applyMonthlyDiscount(order);
}
Error Handling
// Bad: Swallowing errors
try {
await processPayment(order);
} catch (e) {
console.log(e);
}
// Good: Meaningful error handling
try {
await processPayment(order);
} catch (error) {
if (error instanceof PaymentGatewayError) {
await notifyPaymentTeam(error);
throw new UserFacingError('Payment failed. Please try again.');
}
logger.error('Unexpected payment error', { orderId: order.id, error });
throw error;
}
Avoid Deep Nesting
// Bad: Arrow code
function processUser(user) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
if (user.subscriptionValid) {
// actual logic buried here
}
}
}
}
}
// Good: Early returns
function processUser(user) {
if (!user) return;
if (!user.isActive) return;
if (!user.hasPermission) return;
if (!user.subscriptionValid) return;
// clear path to actual logic
}
DRY vs Readable
Don't abstract too early:
// Premature abstraction (actually harder to understand)
const createHandler = (type) => (data) =>
fetch(`/api/${type}`, { method: 'POST', body: JSON.stringify(data) });
// Sometimes explicit is better
async function createUser(data) {
return fetch('/api/users', { method: 'POST', body: JSON.stringify(data) });
}
async function createOrder(data) {
return fetch('/api/orders', { method: 'POST', body: JSON.stringify(data) });
}
Refactoring Tips
- Make it work - Don't optimize too early
- Make it right - Refactor once it works
- Make it fast - Only if needed
Code is read 10x more than written. Optimize for readability.