Files
2025-11-29 18:19:48 +08:00

15 KiB

Code Quality Examples

Real-world examples demonstrating DRY, KISS, and CLEAN CODE principles in practical scenarios.

Example 1: User Authentication Module

Scenario: User Login and Registration Logic

Before: Violates DRY and CLEAN CODE

// auth.js
export function loginUser(email, password) {
  // Validate email
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailRegex.test(email)) {
    throw new Error('Invalid email format');
  }

  // Validate password
  if (!password || password.length < 8) {
    throw new Error('Password must be at least 8 characters');
  }

  // Authenticate
  const user = database.findUserByEmail(email);
  if (!user) {
    throw new Error('User not found');
  }

  if (!bcrypt.compareSync(password, user.passwordHash)) {
    throw new Error('Invalid password');
  }

  return generateToken(user);
}

export function registerUser(email, password, confirmPassword) {
  // Validate email
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailRegex.test(email)) {
    throw new Error('Invalid email format');
  }

  // Validate password
  if (!password || password.length < 8) {
    throw new Error('Password must be at least 8 characters');
  }

  // Validate confirm password
  if (password !== confirmPassword) {
    throw new Error('Passwords do not match');
  }

  // Check if user exists
  const existingUser = database.findUserByEmail(email);
  if (existingUser) {
    throw new Error('Email already registered');
  }

  // Create user
  const passwordHash = bcrypt.hashSync(password, 10);
  return database.createUser({
    email,
    passwordHash
  });
}

Issues:

  • Email validation duplicated (DRY violation)
  • Password validation duplicated (DRY violation)
  • Error messages not consistent
  • Unclear variable names (emailRegex could be EMAIL_REGEX)
  • Magic number 8 for password length (CLEAN CODE)
  • Magic number 10 for bcrypt rounds (CLEAN CODE)

After: DRY, KISS, CLEAN CODE

// auth.js
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const MIN_PASSWORD_LENGTH = 8;
const BCRYPT_ROUNDS = 10;

function validateEmail(email) {
  if (!EMAIL_REGEX.test(email)) {
    throw new Error('Invalid email format');
  }
}

function validatePassword(password) {
  if (!password || password.length < MIN_PASSWORD_LENGTH) {
    throw new Error(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
  }
}

export async function loginUser(email, password) {
  validateEmail(email);
  validatePassword(password);

  const user = database.findUserByEmail(email);
  if (!user) {
    throw new Error('User not found');
  }

  const isPasswordValid = bcrypt.compareSync(password, user.passwordHash);
  if (!isPasswordValid) {
    throw new Error('Invalid password');
  }

  return generateToken(user);
}

export async function registerUser(email, password, confirmPassword) {
  validateEmail(email);
  validatePassword(password);

  if (password !== confirmPassword) {
    throw new Error('Passwords do not match');
  }

  const existingUser = database.findUserByEmail(email);
  if (existingUser) {
    throw new Error('Email already registered');
  }

  const passwordHash = bcrypt.hashSync(password, BCRYPT_ROUNDS);
  return database.createUser({ email, passwordHash });
}

Improvements:

  • DRY: Email and password validation extracted to reusable functions
  • CLEAN CODE: Magic values (8, 10) defined as named constants
  • CLEAN CODE: Variable names are clear (isPasswordValid vs implicit boolean)
  • KISS: Functions are shorter and focused
  • DRY: Validation logic centralized

Example 2: E-commerce Order Processing

Scenario: Calculating Order Total with Taxes, Discounts, and Shipping

Before: Violates KISS and CLEAN CODE

function calc(items, c, s, t) {
  let st = items.reduce((a, b) => a + (b.p * b.q), 0);
  let d = 0;

  if (c) {
    if (c.type === 1) d = st * (c.val / 100);
    else if (c.type === 2) d = c.val;
    else if (c.type === 3 && c.minAmount && st > c.minAmount) {
      d = st * (c.val / 100);
    }
  }

  let sh = s ? (st > 100 ? 0 : 15) : 0;
  let tx = t ? ((st - d + sh) * 0.1) : 0;

  return {
    st: st,
    d: d,
    sh: sh,
    tx: tx,
    tt: st - d + sh + tx
  };
}

Issues:

  • Cryptic parameter names (c, s, t, items)
  • Cryptic variable names (st, d, sh, tx, tt)
  • Magic numbers without explanation (1, 2, 3, 100, 15, 0.1)
  • Over-nested conditions (KISS violation)
  • No documentation about what the function does
  • Return object has abbreviated keys
  • Hard to understand discount logic

After: KISS and CLEAN CODE

/**
 * Calculates order total including taxes, discounts, and shipping
 * @param {Object[]} items - Array of {price, quantity}
 * @param {Object} discount - Optional {type, value, minAmount}
 * @param {boolean} includeShipping - Whether to add shipping cost
 * @param {boolean} includeTax - Whether to add sales tax
 * @returns {Object} Order summary with breakdown
 */
function calculateOrderTotal(items, discount = null, includeShipping = true, includeTax = true) {
  const SHIPPING_COST = 15;
  const FREE_SHIPPING_THRESHOLD = 100;
  const TAX_RATE = 0.1;

  // Calculate subtotal
  const subtotal = items.reduce((total, item) => {
    return total + (item.price * item.quantity);
  }, 0);

  // Calculate discount
  const discountAmount = calculateDiscount(subtotal, discount);

  // Calculate shipping
  const shippingCost = calculateShipping(subtotal, includeShipping);

  // Calculate tax (applies to subtotal after discount + shipping)
  const taxableAmount = subtotal - discountAmount + shippingCost;
  const taxAmount = includeTax ? taxableAmount * TAX_RATE : 0;

  const total = subtotal - discountAmount + shippingCost + taxAmount;

  return {
    subtotal,
    discountAmount,
    shippingCost,
    taxAmount,
    total
  };
}

function calculateDiscount(subtotal, discount) {
  if (!discount) return 0;

  const PERCENTAGE_DISCOUNT = 'percentage';
  const FIXED_DISCOUNT = 'fixed';
  const TIERED_DISCOUNT = 'tiered';

  switch (discount.type) {
    case PERCENTAGE_DISCOUNT:
      return subtotal * (discount.value / 100);

    case FIXED_DISCOUNT:
      return discount.value;

    case TIERED_DISCOUNT:
      const meetsMinimum = discount.minAmount && subtotal > discount.minAmount;
      return meetsMinimum ? subtotal * (discount.value / 100) : 0;

    default:
      return 0;
  }
}

function calculateShipping(subtotal, includeShipping) {
  const SHIPPING_COST = 15;
  const FREE_SHIPPING_THRESHOLD = 100;

  if (!includeShipping) return 0;
  return subtotal > FREE_SHIPPING_THRESHOLD ? 0 : SHIPPING_COST;
}

Improvements:

  • CLEAN CODE: Descriptive function and parameter names
  • CLEAN CODE: Magic numbers extracted to named constants
  • CLEAN CODE: Clear documentation with JSDoc
  • KISS: Complex nested logic extracted to separate functions
  • CLEAN CODE: Return object has meaningful property names
  • CLEAN CODE: Discount types defined as constants instead of magic numbers

Example 3: Data Validation Utilities

Scenario: Form Field Validation

Before: Violates DRY

// validationUtils.js
export function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

export function validatePassword(password) {
  return password && password.length >= 8;
}

export function validatePhone(phone) {
  const regex = /^(\+1)?(\d{3})[-.]?(\d{3})[-.]?(\d{4})$/;
  return regex.test(phone);
}

// userForm.js
function handleUserSubmit(userData) {
  if (!userData.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(userData.email)) {
    return { error: 'Invalid email' };
  }

  if (!userData.password || userData.password.length < 8) {
    return { error: 'Password too short' };
  }

  if (!userData.phone || !/^(\+1)?(\d{3})[-.]?(\d{3})[-.]?(\d{4})$/.test(userData.phone)) {
    return { error: 'Invalid phone' };
  }

  return { success: true };
}

// billingForm.js
function handleBillingSubmit(billingData) {
  if (!billingData.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(billingData.email)) {
    return { error: 'Invalid email' };
  }

  if (!billingData.phone || !/^(\+1)?(\d{3})[-.]?(\d{3})[-.]?(\d{4})$/.test(billingData.phone)) {
    return { error: 'Invalid phone' };
  }

  return { success: true };
}

Issues:

  • Validation utilities exist but aren't used (DRY violation)
  • Regex patterns duplicated in multiple files (DRY violation)
  • Validation logic scattered across components
  • No centralized validation error handling

After: DRY, Reusable Validators

// validationUtils.js
const VALIDATORS = {
  EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
  PHONE: /^(\+1)?(\d{3})[-.]?(\d{3})[-.]?(\d{4})$/,
  STRONG_PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/
};

const MIN_PASSWORD_LENGTH = 8;

export const validators = {
  email: (value) => VALIDATORS.EMAIL.test(value),
  password: (value) => value && value.length >= MIN_PASSWORD_LENGTH,
  phone: (value) => VALIDATORS.PHONE.test(value),
  required: (value) => !!value && value.trim() !== ''
};

export function validateForm(data, rules) {
  const errors = {};

  Object.entries(rules).forEach(([field, validatorFn]) => {
    if (!validatorFn(data[field])) {
      errors[field] = `Invalid ${field}`;
    }
  });

  return Object.keys(errors).length === 0
    ? { success: true }
    : { error: errors };
}

// userForm.js
import { validators, validateForm } from './validationUtils';

function handleUserSubmit(userData) {
  return validateForm(userData, {
    email: validators.email,
    password: validators.password,
    phone: validators.phone
  });
}

// billingForm.js
import { validators, validateForm } from './validationUtils';

function handleBillingSubmit(billingData) {
  return validateForm(billingData, {
    email: validators.email,
    phone: validators.phone
  });
}

Improvements:

  • DRY: Regex patterns defined once in VALIDATORS object
  • DRY: Validation logic centralized in reusable functions
  • CLEAN CODE: Named constants for validation rules
  • KISS: Simple validator functions composed together
  • DRY: Generic validateForm handles all form validation

Example 4: API Response Handling

Scenario: Consistent Error and Success Response Handling

Before: Violates DRY and CLEAN CODE

// userService.js
export async function getUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      console.error('HTTP error', response.status);
      return { data: null, error: 'Failed to fetch user' };
    }
    const data = await response.json();
    return { data, error: null };
  } catch (err) {
    console.error('Request failed', err);
    return { data: null, error: 'Network error' };
  }
}

// productService.js
export async function getProduct(id) {
  try {
    const response = await fetch(`/api/products/${id}`);
    if (!response.ok) {
      console.error('HTTP error', response.status);
      return { data: null, error: 'Failed to fetch product' };
    }
    const data = await response.json();
    return { data, error: null };
  } catch (err) {
    console.error('Request failed', err);
    return { data: null, error: 'Network error' };
  }
}

// orderService.js
export async function createOrder(orderData) {
  try {
    const response = await fetch('/api/orders', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(orderData)
    });
    if (!response.ok) {
      console.error('HTTP error', response.status);
      return { data: null, error: 'Failed to create order' };
    }
    const data = await response.json();
    return { data, error: null };
  } catch (err) {
    console.error('Request failed', err);
    return { data: null, error: 'Network error' };
  }
}

Issues:

  • Error handling pattern repeated in every function (DRY violation)
  • Try-catch-response handling duplicated (DRY violation)
  • Generic error messages ("Network error", "Failed to fetch")
  • No distinction between HTTP errors and network errors

After: DRY with Centralized API Handling

// apiClient.js
/**
 * Centralized API request handler with consistent error handling
 */
export async function apiCall(url, options = {}) {
  try {
    const response = await fetch(url, options);

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new APIError(
        `HTTP ${response.status}: ${response.statusText}`,
        response.status,
        errorData
      );
    }

    return await response.json();
  } catch (error) {
    if (error instanceof APIError) {
      throw error;
    }
    throw new APIError('Network request failed', null, error);
  }
}

export class APIError extends Error {
  constructor(message, statusCode = null, details = null) {
    super(message);
    this.name = 'APIError';
    this.statusCode = statusCode;
    this.details = details;
  }
}

/**
 * Wraps async functions to return {data, error} format
 */
export async function handleAPIRequest(asyncFn) {
  try {
    const data = await asyncFn();
    return { data, error: null };
  } catch (error) {
    const errorMessage = error instanceof APIError
      ? error.message
      : 'Unexpected error';
    return { data: null, error: errorMessage };
  }
}

// userService.js
import { apiCall, handleAPIRequest } from './apiClient';

export const userService = {
  getUser: (id) => handleAPIRequest(() => apiCall(`/api/users/${id}`)),
  updateUser: (id, data) => handleAPIRequest(() =>
    apiCall(`/api/users/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
  )
};

// productService.js
import { apiCall, handleAPIRequest } from './apiClient';

export const productService = {
  getProduct: (id) => handleAPIRequest(() => apiCall(`/api/products/${id}`)),
  listProducts: () => handleAPIRequest(() => apiCall('/api/products'))
};

// orderService.js
import { apiCall, handleAPIRequest } from './apiClient';

export const orderService = {
  createOrder: (orderData) => handleAPIRequest(() =>
    apiCall('/api/orders', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(orderData)
    })
  )
};

Improvements:

  • DRY: API request and error handling centralized in apiClient
  • DRY: All services use the same apiCall and handleAPIRequest
  • CLEAN CODE: APIError class provides clear error structure
  • CLEAN CODE: Service functions are concise and readable
  • KISS: Error handling logic is simple and consistent
  • CLEAN CODE: Consistent {data, error} response format

Summary

These examples demonstrate how applying DRY, KISS, and CLEAN CODE principles makes code:

  • More maintainable - Changes in one place affect all usages
  • More readable - Intent is clear, naming is descriptive
  • Less error-prone - Less duplicated code means fewer places to fix bugs
  • Easier to test - Centralized logic is easier to unit test
  • Easier to extend - Adding new features doesn't require duplicating existing patterns