Published May 30, 2026 ยท 8 min read ยท ๐ท๏ธ Best Practices
# Code Quality Best Practices for Teams
Good code is not just about functionality โ it's about readability, maintainability, and collaboration. These practices help teams write code that stands the test of time.
## Naming Conventions
Names should be self-documenting. If you need a comment to explain what a variable is, rename it:
// Bad
const x = users.filter(u => u.s === 'active');
const d = new Date();
const tmp = processData(data);
// Good
const activeUsers = users.filter(user => user.status === 'active');
const currentTimestamp = new Date();
const processedData = processData(rawData);
**Variable names:** nouns, descriptive (`userData`, `totalAmount`)
**Function names:** verbs or verb phrases (`fetchUser`, `calculateTotal`, `isValidEmail`)
**Constants:** ALL_CAPS with underscores (`MAX_RETRY_COUNT`, `DEFAULT_TIMEOUT`)
## Functions: Do One Thing
Each function should do one thing well. A good test: if you need "and" to describe what it does, split it:
// Bad: Does multiple things
function processUser(user) {
validateUser(user); // validation
saveToDatabase(user); // persistence
sendWelcomeEmail(user); // side effect
return user;
}
// Good: Single responsibility
function validateUser(user) { ... }
function saveUser(user) { ... }
function notifyUser(user, type) { ... }
function registerUser(user) {
validateUser(user);
const savedUser = saveUser(user);
notifyUser(savedUser, 'welcome');
return savedUser;
}
## Keep Functions Small
Ideal: less than 20 lines. Good: less than 50 lines. If a function exceeds 100 lines, strongly consider splitting it.
// Bad: Function doing too much
async function handleOrder(order) {
// 200+ lines of validation, calculation, persistence, notification...
}
// Good: Composed of smaller functions
async function handleOrder(order) {
const validated = validateOrder(order);
const calculated = calculatePricing(validated);
const saved = await persistOrder(calculated);
await notifyCustomer(saved);
return saved;
}
## Avoid Magic Numbers
// Bad: What do these numbers mean?
if (user.age > 18 && retryCount 5000) {
processPayment(amount * 0.95);
}
// Good: Named constants explain the intent
const MINIMUM_AGE = 18;
const MAX_RETRY_COUNT = 3;
const PAYMENT_TIMEOUT_MS = 5000;
const LOYALTY_DISCOUNT = 0.05;
if (user.age > MINIMUM_AGE && retryCount PAYMENT_TIMEOUT_MS) {
processPayment(amount * (1 - LOYALTY_DISCOUNT));
}
## Early Returns (Guard Clauses)
Handle edge cases and invalid inputs at the start with early returns:
// Bad: Nested conditionals
function processUser(user) {
if (user) {
if (user.email) {
if (user.isActive) {
// main logic here, deeply nested
}
}
}
}
// Good: Early returns for edge cases
function processUser(user) {
if (!user) return null;
if (!user.email) return null;
if (!user.isActive) return null;
// main logic at top level
return processActiveUser(user);
}
## Error Handling
Handle errors explicitly. Never swallow exceptions silently:
// Bad: Silently ignoring errors
try {
await sendEmail(user.email);
} catch (e) {
// Nothing happens, error is lost
}
// Good: Explicit handling
try {
await sendEmail(user.email);
} catch (error) {
logger.error('Failed to send email', { userId: user.id, error });
throw new EmailDeliveryError('Failed to send notification', { cause: error });
}
// Or handle gracefully
try {
await sendEmail(user.email);
} catch (error) {
logger.warn('Email failed, will retry', { userId: user.id });
await queueForRetry(user.id);
}
## Comments That Add Value
Good comments explain _why_, not _what_. The code shows what, comments explain why:
// Bad: Comments that state the obvious
// Increment counter by 1
counter++;
// Loop through users
for (const user of users) { }
// Good: Comments explain non-obvious decisions
// Using linear search here despite O(n) because:
// 1. Array is guaranteed to be small ( $100
// See: Tax Compliance Doc #2847
const taxRate = order.total > 100 ? 0.08 : 0;
## Consistent Code Formatting
Use automated formatting. Debating formatting in code reviews is a waste of time:
// Use Prettier for JavaScript/TypeScript
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
// Use ESLint for code quality rules
// .eslintrc
{
"extends": ["eslint:recommended"],
"rules": {
"no-unused-vars": "error",
"prefer-const": "error"
}
}
## Type Safety
Use TypeScript or JSDoc for JavaScript. Types catch bugs at compile time:
// Bad: Any type, no safety
function processOrder(order) {
return order.total * order.quantity; // What if these are undefined?
}
// Good: Explicit types
interface OrderLineItem {
total: number;
quantity: number;
}
interface Order {
items: OrderLineItem[];
customerId: string;
}
function processOrder(order: Order): number {
return order.items.reduce((sum, item) => sum + item.total * item.quantity, 0);
}
## Writing Testable Code
Testable code is usually better code. Characteristics:
- **Pure functions**: Same input โ Same output, no side effects
- **Dependency injection**: Pass dependencies as parameters
- **Small, focused functions**: Easy to test in isolation
- **Single responsibility**: Clear input/output
// Hard to test: dependencies hardcoded
function getUser() {
const db = new Database(); // Hardcoded
return db.query('SELECT * FROM users');
}
// Easy to test: dependency injection
function getUser(db: Database) {
return db.query('SELECT * FROM users');
}
// Even better: pass interface
interface UserRepository {
findById(id: string): Promise;
}
function getUser(repo: UserRepository, id: string) {
return repo.findById(id);
}
## Code Review Checklist
When reviewing code, check for:
- Does the code do what the PR claims?
- Are edge cases handled?
- Is error handling appropriate?
- Are there tests for new logic?
- Are names descriptive?
- Is the code simpler than before?
- Any security concerns?
- Any performance issues?
## The Boy Scout Rule
Leave the code a little better than you found it. Every PR is an opportunity to improve the surrounding code slightly:
- Rename a poorly-named variable while changing the function
- Extract a magic number while adding new logic
- Add a missing test while fixing a bug
Small improvements compound. The codebase gets better over time, not worse.
Top comments (0)