正在加载,请稍候…

Clean Code Principles: Writing Readable and Maintainable Code

Apply Clean Code principles from Robert Martin. Learn naming, functions, comments, error handling, and code structure best practices.

Clean Code Principles: Writing Readable and Maintainable Code

Clean code reads like well-written prose—its intent is clear without explanation.

Meaningful Names

// Bad
const d = 86400; // what is d?
function calc(a: number[], b: number) { /* ... */ }
class Mgr { /* ... */ }

// Good
const SECONDS_PER_DAY = 86400;
function calculateDailyRevenue(transactions: Transaction[], taxRate: number) { /* ... */ }
class AccountManager { /* ... */ }

// Variables should reveal intent
// Bad
const list = users.filter(u => u.a > 18);

// Good
const adults = users.filter(user => user.age > LEGAL_DRINKING_AGE);

Small Functions

Functions should do one thing, do it well, and do it only.

// Bad: function does too many things
async function processUserRegistration(data: any) {
  // validate
  if (!data.email || !data.password) throw new Error('Missing fields');
  if (!/^[^@]+@[^@]+$/.test(data.email)) throw new Error('Invalid email');

  // hash password
  const salt = await bcrypt.genSalt(10);
  const hash = await bcrypt.hash(data.password, salt);

  // save to database
  const user = await db.query('INSERT INTO users ...');

  // send email
  await emailClient.send({ to: data.email, subject: 'Welcome!' });

  return user;
}

// Good: each function has one responsibility
async function processUserRegistration(data: RegistrationData) {
  validateRegistrationData(data);
  const hashedPassword = await hashPassword(data.password);
  const user = await createUserAccount(data.email, hashedPassword);
  await sendWelcomeEmail(user.email);
  return user;
}

function validateRegistrationData(data: RegistrationData): void {
  if (!data.email || !data.password) throw new ValidationError('Missing fields');
  if (!isValidEmail(data.email)) throw new ValidationError('Invalid email');
}

Meaningful Comments

// Bad: comment explains WHAT (obvious from code)
// increment counter
i++;

// Bad: commented-out code
// const oldResult = computeOldWay(x);
const result = computeNewWay(x);

// Good: comment explains WHY
// Using 2^31 - 1 to avoid integer overflow in legacy 32-bit systems
const MAX_SAFE_ID = 2147483647;

// Good: clarify complex algorithm
// This implements the Levenshtein distance algorithm
// See: https://en.wikipedia.org/wiki/Levenshtein_distance
function editDistance(s1: string, s2: string): number { /* ... */ }

Error Handling

// Bad: error codes and null returns
function findUser(id: string): User | null {
  // caller must check null every time
  return null;
}

// Good: throw domain exceptions
class UserNotFoundError extends Error {
  constructor(id: string) {
    super(`User with id ${id} not found`);
    this.name = 'UserNotFoundError';
  }
}

async function findUser(id: string): Promise<User> {
  const user = await db.users.findById(id);
  if (!user) throw new UserNotFoundError(id);
  return user;
}

// Good: Result type for expected failures
type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };

async function tryLogin(email: string, password: string): Promise<Result<User, LoginError>> {
  const user = await userRepo.findByEmail(email);
  if (!user) return { success: false, error: new LoginError('Invalid credentials') };
  const valid = await verifyPassword(password, user.passwordHash);
  if (!valid) return { success: false, error: new LoginError('Invalid credentials') };
  return { success: true, data: user };
}

Don't Repeat Yourself (DRY)

// Bad: duplicated validation
function createUser(email: string) {
  if (!/^[^@]+@[^@]+$/.test(email)) throw new Error('Invalid email');
  // ...
}

function updateUserEmail(userId: string, email: string) {
  if (!/^[^@]+@[^@]+$/.test(email)) throw new Error('Invalid email');
  // ...
}

// Good: extracted function
function assertValidEmail(email: string): void {
  if (!/^[^@]+@[^@]+$/.test(email)) throw new ValidationError('Invalid email');
}

Clean code is not written in one pass—it's continuously refined through refactoring.