Skip to content

Code Quality + Refactoring

Mở đầu

Code viết xong chạy được là OK? Bạn có thể viết code như sau: function OK, nhưng 2 tuần sau tự mình cũng không hiểu. Hoặc team có người nghỉ, để lại đống "chỉ chúa với họ hiểu" code.

Chương này: hiểu code tốt là gì, identify bad code, refactor an toàn.

Bạn sẽ học:

ChươngNội dung
1Code smells
2Refactoring techniques
3Code review
4Quality metrics

0. Toàn cảnh: code lifecycle

Software dev có 1 sự thật hay bị bỏ qua: code bị đọc nhiều hơn nhiều lần được viết.

Đời code

  • Write: dev viết version đầu, function chạy được, test pass
  • Review: team đọc, suggest improvement
  • Maintain: fix bug, add feature, adapt new req — chiếm 80%+ lifecycle
  • Refactor: code khó maintain → improve internal structure mà không đổi external behavior
  • Retire: tech iterate, code cũ replace

Martin Fowler trong "Refactoring": "Bất kỳ thằng ngốc nào cũng viết được code máy hiểu. Chỉ programmer tốt viết được code người hiểu."


1. Code smells

1.1 Code smell là gì?

"Code smell" (Kent Beck) = feature trong code, không phải bug, nhưng hint design issue sâu hơn. Như mùi lạ trong phòng — không bệnh ngay, nhưng cần clean.

代码坏味道识别器 ── 点击切换不同示例
问题代码
function processOrder(order) {
  // 验证订单... (20行)
  // 计算价格... (15行)
  // 检查库存... (10行)
  // 发送通知... (15行)
  // 更新数据库... (10行)
  // 生成报表... (10行)
  // 总计 80+ 行!
}

📏 过长函数

一个函数超过 50 行,做了太多事情,难以理解和测试。

改进建议:将大函数拆分为多个职责单一的小函数:validateOrder()、calculatePrice()、checkInventory() 等。

1.2 Common smells

SmellSymptomHại
Long functionFunction >50 dòngKhó hiểu, test, reuse
Magic numberCode viết 86400000 trực tiếpMeaning unclear, sửa hay sót
Duplicate codeLogic giống ở nhiều chỗSửa phải sync nhiều chỗ
Deep nesting>3 layer if/forLogic như mê cung
Long parameter listParam >4Call khó, dễ sai order
God class1 class/module làm nhiều việcTrách nhiệm không rõ, sửa 1 break nhiều

Core insight

Smell không phải "error", mà "signal". Cho biết: design ở đây có thể cần improve. Không phải mọi smell cần fix ngay, nhưng phải có khả năng identify.


2. Refactoring techniques

2.1 Refactoring là gì?

Definition chính xác: không đổi external behavior, improve internal structure.

Key: "không đổi external behavior". Refactor không phải rewrite, không add feature, không fix bug. Là "organize" code internal.

重构手法对比演示 ── 选择一种手法查看前后对比
Extract Function:将一段代码从大函数中提取出来,放入一个命名清晰的新函数中。
重构前
function printReport(invoice) {
  console.log("=== 账单 ===")
  // 计算总额
  let total = 0
  for (let item of invoice.items) {
    total += item.price * item.qty
  }
  console.log(`总计: ${total}`)
}
重构后
function printReport(invoice) {
  console.log("=== 账单 ===")
  const total = calcTotal(invoice.items)
  console.log(`总计: ${total}`)
}

function calcTotal(items) {
  return items.reduce(
    (s, i) => s + i.price * i.qty, 0
  )
}
要点:提炼函数是最常用的重构手法。好的函数名就是最好的注释——如果你需要写注释解释一段代码在做什么,那它就该被提炼成函数。

2.2 Common techniques

Extract function

Technique dùng nhất. Khi 1 đoạn code có thể tóm tắt bằng tên có nghĩa → extract thành function.

javascript
// Before
function printReport(data) {
  // Calc total
  let total = 0
  for (const item of data.items) {
    total += item.price * item.qty
  }
  // Print...
}

// After
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0)
}

function printReport(data) {
  const total = calculateTotal(data.items)
  // Print...
}

Rename

Naming tốt = doc rẻ nhất + hiệu quả nhất. Khi cần comment giải thích variable/function = name không đủ tốt.

javascript
// Before
const d = new Date() - startTime
const arr = users.filter(u => u.a)

// After
const elapsedMs = new Date() - startTime
const activeUsers = users.filter(user => user.isActive)

Replace nested conditional với guard clauses

javascript
// Before
function getPayAmount(employee) {
  if (employee.isSeparated) {
    return { amount: 0 }
  } else {
    if (employee.isRetired) {
      return { amount: employee.pension }
    } else {
      return { amount: employee.salary }
    }
  }
}

// After
function getPayAmount(employee) {
  if (employee.isSeparated) return { amount: 0 }
  if (employee.isRetired) return { amount: employee.pension }
  return { amount: employee.salary }
}

Refactor safety net

Risk lớn nhất refactor = "sửa sửa sửa thành bug". Prerequisite refactor = test coverage. Mỗi small step refactor → run test, đảm bảo behavior không đổi. Code không test → bổ sung test trước → refactor sau.


3. Code review

3.1 Sao cần?

CR = 1 trong cách hiệu quả nhất đảm bảo quality. Giá trị không chỉ bug detection:

  • Knowledge share: team biết code nhau, giảm "bus factor"
  • Unify style: convention dần thành standard team
  • Phát hiện design issue sớm: khó fix hơn bug = bad architecture
  • Mutual learning: đọc code người khác = shortcut improve

3.2 Review gì?

DimFocus
CorrectnessLogic đúng? Edge case handle?
ReadabilityNaming rõ? Structure dễ hiểu?
SecurityInjection risk? Sensitive data expose?
PerformanceIssue rõ ràng? N+1 query?
TestCó test tương ứng? Cover critical path?

3.3 Etiquette

CR tốt = bàn về code, không phải critize người:

  • "Chúng ta" thay vì "bạn": "Bạn sai đây" → "Đây có thể guard clause"
  • Ask thay vì command: "Đổi const" → "Variable này sau sẽ reassign? Nếu không, const an toàn hơn"
  • Give reason: không chỉ "không tốt", mà "sao không tốt" + "thế nào tốt hơn"

4. Code quality metrics

4.1 Cyclomatic complexity

Cyclomatic complexity = số path độc lập trong code. Mỗi if, for, case, &&, || tăng complexity.

ComplexityEvaluationRecommend
1-10SimpleDễ hiểu + test
11-20MediumCân nhắc tách
21-50ComplexPhải refactor
50+UnmaintainableRefactor khẩn

4.2 Code coverage

TypeNote
Line coverage% code line được execute
Branch coverage% branch được execute

Coverage trap

80% coverage ≠ code tốt. Coverage chỉ cho biết "code nào không test", không cho biết "test có nghĩa không". Test expect(true).toBe(true) tăng coverage nhưng vô giá trị.

4.3 Practical tools

ToolUse
ESLintJS/TS static analysis
PrettierCode format, unify style
SonarQubeComprehensive quality platform
HuskyGit hooks, pre-commit check

5. AI assist code quality

LLM ở code quality cực practical, có thể làm "24/7 code reviewer".

5.1 Identify code smells

Prompt:

Review code, identify code smell, bao gồm:
long function, magic number, duplicate code, deep nesting, long param list.
Mỗi issue: location + description + improvement.

[paste code]

5.2 Auto refactor

Prompt:

Refactor code:
1. Không đổi external behavior
2. Dùng extract function, guard clause
3. Improve naming, loại magic number
4. Giải thích mỗi step refactor

[paste code]

5.3 Mock code review

Prompt:

Review code as senior dev, feedback từ:
- Correctness: logic có bug? Edge case?
- Readability: naming rõ? Structure dễ?
- Performance: issue rõ?
- Security: injection / data leak risk?
Tone "suggest" not "command", give improvement.

[paste code]

AI caveat

AI refactor suggestion phải self-verify — run test confirm behavior unchanged. Treat AI as "colleague suggesting", không "authority trust unconditionally".


6. Tổng kết

  1. Identify: smell code, biết chỗ cần improve
  2. Refactor: safe techniques, small step under test protection
  3. Collaborate: CR, team guard quality together
  4. Measure: objective metric track code health

Insight cuối

Code quality không one-time, mà ongoing habit. Như giữ phòng sạch — không phải đợi lộn xộn mới big clean, mà daily tidy. Boy Scout Rule: leave code cleaner than you found it.

2026 cho VN dev

  • Modern stack 2026:
    • ESLint flat config: format mới
    • Biome: thay ESLint + Prettier, faster (Rust)
    • Knip: tìm dead code TypeScript
    • CodeRabbit / Greptile: AI-powered code review
  • VN context:
    • Startup: ESLint + Prettier + Husky đủ
    • Enterprise: SonarQube + strict review process
  • AI-era:
    • Copilot, Cursor catch many bug + smell tự động
    • GPT-4 / Claude review PR efficient
    • Codium AI auto-gen test
  • Bài tập: configure ESLint + Prettier + Husky cho 1 project

Tài liệu