Buổi 07: Refactoring Mastery — Clean Code Có Hệ Thống 🔧
Thành quả: Refactor 1 legacy module: extract functions, rename, decouple, add tests
🎯 Mục Tiêu
- Nhận diện 10 code smells phổ biến nhất
- Master 8 refactoring techniques an toàn
- Dùng cm-clean-code + cm-debugging workflow
- Refactor có safety net: test trước → refactor → verify
- Hiểu khi nào NÊN và KHÔNG NÊN refactor
📖 Phần 1: 10 Code Smells
| # | Smell | Ví dụ | Severity |
|---|---|---|---|
| 1 | God Function | Function > 50 lines | 🔴 Critical |
| 2 | Magic Numbers | if (status === 3) | 🟡 Medium |
| 3 | Dead Code | Functions never called | 🟡 Medium |
| 4 | Duplicated Logic | Same code in 3+ places | 🔴 Critical |
| 5 | Deep Nesting | if inside if inside if (>3 levels) | 🟡 Medium |
| 6 | Long Parameter List | function(a, b, c, d, e, f, g) | 🟡 Medium |
| 7 | Feature Envy | Class A dùng data của Class B quá nhiều | 🟠 High |
| 8 | Shotgun Surgery | 1 change affects 10+ files | 🔴 Critical |
| 9 | Primitive Obsession | Dùng string thay vì enum/type | 🟡 Medium |
| 10 | Inconsistent Naming | getUserData vs fetchUser vs loadUserInfo | 🟡 Medium |
Phát hiện bằng AI
bash
# Scan for code smells
cat src/services/order.service.ts | gemini "Analyze this code for:
1. Code smells (God function, duplication, deep nesting)
2. Naming inconsistencies
3. Missing error handling
4. Performance issues
5. Security vulnerabilities
Rate each issue: 🔴 Critical, 🟠 High, 🟡 Medium, 🟢 Low"📖 Phần 2: 8 Refactoring Techniques
1. Extract Function
javascript
// ❌ BEFORE: God function
async function processOrder(order) {
// 50 lines of validation, calculation, notification, logging...
const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);
const tax = total * 0.1;
const shipping = total > 100 ? 0 : 10;
// ... 30 more lines
}
// ✅ AFTER: Small, focused functions
async function processOrder(order) {
const total = calculateTotal(order.items);
const tax = calculateTax(total);
const shipping = calculateShipping(total);
await saveOrder({ ...order, total, tax, shipping });
await notifyCustomer(order.customerId);
}
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.qty, 0);
}
function calculateTax(total) { return total * 0.1; }
function calculateShipping(total) { return total > 100 ? 0 : 10; }2. Replace Magic Numbers
javascript
// ❌ BEFORE
if (user.role === 3) { /* admin logic */ }
if (order.status === 5) { /* completed */ }
// ✅ AFTER
const UserRole = { ADMIN: 3, USER: 1, MODERATOR: 2 } as const;
const OrderStatus = { PENDING: 1, PROCESSING: 2, SHIPPED: 4, COMPLETED: 5 } as const;
if (user.role === UserRole.ADMIN) { /* admin logic */ }
if (order.status === OrderStatus.COMPLETED) { /* completed */ }3. Guard Clause (Flatten Nesting)
javascript
// ❌ BEFORE: Deep nesting
function getDiscount(user, order) {
if (user) {
if (user.isPremium) {
if (order.total > 100) {
return 0.2;
} else {
return 0.1;
}
} else {
return 0;
}
}
return 0;
}
// ✅ AFTER: Guard clauses (early return)
function getDiscount(user, order) {
if (!user) return 0;
if (!user.isPremium) return 0;
if (order.total > 100) return 0.2;
return 0.1;
}4. Extract Interface/Type
typescript
// ❌ BEFORE: Primitive obsession
function createUser(name: string, email: string, age: number, role: string)
// ✅ AFTER: Type-safe
interface CreateUserInput {
name: string;
email: string;
age: number;
role: 'admin' | 'user' | 'moderator';
}
function createUser(input: CreateUserInput)5-8: More Techniques
| Technique | When | Before → After |
|---|---|---|
| Replace Conditional with Polymorphism | switch/if-else on type | Strategy Pattern |
| Introduce Parameter Object | 4+ params | { config } object |
| Move Logic to Service | Fat controller | Controller → Service → Repository |
| Decompose Conditional | Complex if conditions | Named boolean helpers |
📖 Phần 3: Safe Refactoring Workflow
The Golden Rule: Test → Refactor → Verify
Step 1: CHARACTERIZE
→ Run existing tests (if any)
→ Add characterization tests cho code cần refactor
→ Tests capture CURRENT behavior (even if buggy)
Step 2: REFACTOR
→ Apply 1 technique at a time
→ Small changes, frequent commits
→ "Make the change easy, then make the easy change"
Step 3: VERIFY
→ Run ALL tests after each change
→ If tests fail → revert immediately
→ git diff → review changes are correct
Step 4: COMMIT
→ Small, atomic commits
→ refactor(scope): description of what changedcm-clean-code + cm-debugging Combo
cm-clean-code:
1. Detect dead code, duplicates, naming mess
2. TRIZ-powered suggestions
3. Run AFTER features, BEFORE PRs
cm-debugging:
1. If refactoring breaks something
2. 5-phase investigation: Reproduce → Isolate → Root cause → Fix → Verify
3. Never guess — measure📖 Phần 4: Khi Nào KHÔNG Refactor
| Situation | Decision |
|---|---|
| Code sắp bị thay thế | ❌ Don't refactor |
| Không có tests và không có time viết | ⚠️ Risky — viết test trước |
| Deadline ngày mai | ❌ Ship first, refactor ticket later |
| "Code xấu nhưng đang hoạt động tốt" | 🤔 Add to tech debt backlog |
| "Tôi muốn code đẹp hơn" → no user value | ❌ Focus on features |
| Code gây bugs lặp lại | ✅ MUST refactor |
| Code block new features | ✅ MUST refactor |
| Code violates security | ✅ MUST refactor immediately |
🧪 Lab: Refactor Legacy Module
Task: Refactor God Function (45 min)
Cho file legacy-order.js:
javascript
// legacy-order.js — THE GOD FUNCTION
async function handleOrder(req, res) {
try {
if (!req.body.items || req.body.items.length === 0) {
return res.status(400).json({ error: 'No items' });
}
if (!req.body.customerId) {
return res.status(400).json({ error: 'No customer' });
}
let total = 0;
for (let i = 0; i < req.body.items.length; i++) {
if (req.body.items[i].quantity < 1) {
return res.status(400).json({ error: 'Bad quantity' });
}
total += req.body.items[i].price * req.body.items[i].quantity;
}
if (total > 500) {
total = total * 0.9; // 10% discount
}
const tax = total * 0.1;
const shipping = total > 100 ? 0 : 15;
const finalTotal = total + tax + shipping;
// Save to database...
// Send email...
// Log analytics...
res.json({ orderId: '123', total: finalTotal });
} catch (err) {
res.status(500).json({ error: 'Server error' });
}
}Steps:
- Write characterization tests
- Extract:
validateOrder(),calculateTotal(),calculateDiscount(),calculateTax(),calculateShipping() - Replace magic numbers
- Run tests after EACH extraction
- Commit with proper messages
🎓 Tóm Tắt
| Concept | Key Takeaway |
|---|---|
| Code Smells | God function, magic numbers, dead code, duplication |
| Safe Refactoring | Test → Refactor → Verify → Commit |
| 8 Techniques | Extract, guard clause, constants, type, decompose |
| When NOT to | No tests, deadline, code being replaced |
⏭️ Buổi tiếp theo
Buổi 08: Test-Driven Development — Code Tự Tin 100% 🧪