Files
gh-devstefancho-claude-plug…/skills/code-quality-reviewer/EXAMPLES.md
2025-11-29 18:19:48 +08:00

563 lines
15 KiB
Markdown

# 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
```javascript
// 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
```javascript
// 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
```javascript
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
```javascript
/**
* 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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