Bài 4 · Vận dụng · 16 phút
Bình luận: khi nào cần
Biên soạn bởi Nguyễn Anh Tuấn
Bình luận tốt giải thích “vì sao” chứ không “cái gì”; để code tự giải thích bằng tên & hàm; bỏ comment thừa/lỗi thời/bị comment-out; khi nào JSDoc chính đáng.
Có một quan niệm phổ biến: code nhiều comment = code tốt. Thực tế gần ngược lại. Một comment thường là dấu hiệu rằng bạn chưa tìm được cách viết rõ hơn - không phải bằng chứng của code chất lượng.
Khi gặp đoạn cần comment để hiểu, câu hỏi đầu tiên nên là: "có cách nào đặt lại tên, tách hàm nhỏ hơn, để đoạn này tự giải thích không?" Nếu có - làm vậy. Comment chỉ là phương án dự phòng khi code thực sự không thể nói đủ.
Sửa code trước khi thêm comment
// TRUOC: comment giai thich "cai gi" - phai doc ca hai dong moi hieu
function check(u: User): boolean {
// kiem tra nguoi dung con hoat dong va dang ky duoi 30 ngay
return u.active && u.daysRegistered < 30;
}
// SAU: ten ham noi ro y dinh - comment tro nen du thua
function isNewActiveUser(u: User): boolean {
return u.active && u.daysRegistered < 30;
} - ▸Comment nhiều không bằng code rõ - code rõ thì không cần comment.
- ▸Thấy cần comment → hỏi "tên/hàm có thể rõ hơn không?" trước khi gõ comment.
- ▸Comment là chi phí bảo trì: phải cập nhật khi code thay đổi; nếu quên, nó thành bẫy.
Không phải mọi comment đều trung lập. Một số comment tích cực gây hại - làm code khó đọc hơn, hoặc tệ hơn, dẫn người đọc đi sai hướng.
Các loại comment xấu thường gặp
// 1. DU THUA - noi dung y chang code, khong them gi
let count = 0; // khoi tao bien count bang 0
// 2. LOI THOI - code da doi nhung comment chua cap nhat (nguy hiem nhat)
// Tinh thue VAT 10% theo luat cu
function calcTax(amount: number): number {
return amount * 0.08; // thuc te da doi sang 8% tu thang truoc
}
// 3. CODE BI COMMENT-OUT - khong ai biet co the xoa chua; dung git thay vi day
// function oldValidate(x: string) { ... }
// const legacyFlag = true;
// 4. CHU KY / NHAT KY - git log lam viec nay roi
// Nguyen - 2024-01-15: them truong email
// Minh - 2024-02-03: sua bug validate
// 5. GAY NHIEU - comment that dai, khong them thong tin gi
// Ham nay nhan mot mang cac phan tu kieu so va tra ve tong
// cua tat ca cac phan tu trong mang do
function sum(nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
} Comment lỗi thời là loại nguy hiểm nhất
- ▸Dư thừa (redundant): lặp lại điều code đã nói - gây nhiễu, không thêm giá trị.
- ▸Lỗi thời (stale): code đã đổi nhưng comment chưa cập nhật - dễ gây hiểu sai.
- ▸Comment-out code: đã có git; code bị comment-out tích tụ làm file rối và không ai dám xoá.
- ▸Chữ ký/nhật ký: đây là việc của `git log` - giữ trong code chỉ tạo tiếng ồn.
Comment thật sự hữu ích là comment nói điều mà code không thể nói: lý do đằng sau một quyết định, cảnh báo hậu quả ẩn, hoặc context mà người đọc không thể suy ra từ code đơn thuần.
Các loại comment tốt - giải thích ý định, cảnh báo, TODO rõ ràng
// 1. GIAI THICH VI SAO - ly do kinh doanh khong the doc tu code
// Dung setTimeout de tranh race condition voi animation CSS:
// neu cap nhat DOM ngay, transition chua chay xong se bi reset.
setTimeout(() => updateDOM(), 50);
// 2. CANH BAO HAU QUA - nguoi doc can biet truoc khi sua
// CANH BAO: ham nay khong thread-safe; chi goi tu worker thread chinh.
// Goi tu nhieu thread cung luc se gay data race tren sharedBuffer.
function writeToSharedBuffer(data: Uint8Array): void { /* ... */ }
// 3. TODO RO RANG - viec gi, tai sao chua lam, ai chiu trach nhiem
// TODO(nguyen): chuyen sang crypto.randomUUID() sau khi drop support Node 14
const id = Math.random().toString(36).slice(2);
// 4. LAM RO BIEU THUC KHO - khi bien/ham khong the ro hon duoc
// Kiem tra ngay thu trong tuan theo UTC, khong phai local time
// vi server va client co the o cac timezone khac nhau.
const isWeekend = [0, 6].includes(new Date(ts).getUTCDay()); - ▸"Vì sao" thay vì "cái gì": giải thích quyết định, ràng buộc, hoặc side effect ẩn.
- ▸Cảnh báo hậu quả: nếu sửa đoạn này sai cách thì điều gì tệ xảy ra?
- ▸TODO rõ ràng: việc gì, lý do chưa làm, ai/khi nào sẽ làm - không phải "fix this later".
- ▸Làm rõ biểu thức: khi logic thực sự không thể đặt tên rõ hơn (regex, bitwise, timing).
Nhiều comment giải thích kiểu tham số, giá trị trả về, hoặc trường trong object tồn tại vì JavaScript không có cách khác để nói điều đó. TypeScript cung cấp kênh diễn đạt trực tiếp hơn: kiểu chính là tài liệu sống, được trình biên dịch kiểm tra và không bao giờ lỗi thời khi bạn sửa code.
Kiểu thay thế được nhiều comment mô tả
// TRUOC: comment mo ta tham so va gia tri tra ve
// @param userId - ID cua nguoi dung (so nguyen duong)
// @param role - vai tro: 'admin', 'editor', hoac 'viewer'
// @returns true neu cap quyen thanh cong
function grantAccess(userId: any, role: any): any { /* ... */ }
// SAU: kieu noi thay - comment mo ta tro thanh du thua
type UserId = number;
type Role = 'admin' | 'editor' | 'viewer';
function grantAccess(userId: UserId, role: Role): boolean { /* ... */ }
// Goi sai kieu: grantAccess('abc', 'superuser') -> loi bien dich ngay Khi nào JSDoc/TSDoc vẫn chính đáng
JSDoc hợp lý cho hàm public - nói điều kiểu không nói được
/**
* Chuyen doi so tien VND sang chuoi hien thi co phan cach hang nghin.
*
* @param amount - So tien (phai >= 0; am -> nem RangeError)
* @param currency - Ma tien te ISO 4217, mac dinh 'VND'
* @returns Chuoi dang "1.500.000 VND"
* @throws {RangeError} Neu amount < 0
*
* @example
* formatMoney(1500000) // => "1.500.000 VND"
* formatMoney(250, 'USD') // => "250 USD"
*/
function formatMoney(amount: number, currency = 'VND'): string {
if (amount < 0) throw new RangeError('amount phai >= 0');
return amount.toLocaleString('vi-VN') + ' ' + currency;
} - ▸Kiểu (type) thay thế được phần lớn comment mô tả tham số và kết quả trả về.
- ▸JSDoc/TSDoc phù hợp cho API public: ràng buộc, side effect, ví dụ, throws.
- ▸Kiểu luôn đồng bộ với code; comment có thể lỗi thời - ưu tiên kiểu trước.
Mỗi khi muốn thêm comment vì đoạn code khó hiểu, hãy thử quy trình này trước:
- ▸Bước 1: đặt lại tên - biến, hàm, kiểu. Thường là đủ để xoá comment.
- ▸Bước 2: tách hàm nhỏ - đặt tên hàm = comment ngầm. Xem <a href="/khoa-hoc/clean-code/ham-nho" class="text-purple hover:underline">Bài 3 về hàm nhỏ</a> để làm thành thục.
- ▸Bước 3: nếu vẫn cần comment, viết "vì sao" - lý do, ràng buộc, cảnh báo.
- ▸Bước 4: nếu là API public, thêm JSDoc/TSDoc cho ràng buộc và ví dụ.
Bài tiếp theo: Định dạng & cấu trúc
Câu hỏi thường gặp
Tuỳ quy ước đội. Điều quan trọng hơn là nhất quán - cả đội dùng một ngôn ngữ, không trộn lẫn. Nếu repo có contributor quốc tế, tiếng Anh an toàn hơn. Nhưng dù ngôn ngữ nào, comment nên giải thích "vì sao", không giải thích "cái gì" vì code đã nói "cái gì" rồi.
Không nhất thiết. Hàm public trong thư viện hoặc module dùng chung thì JSDoc/TSDoc có giá trị (IDE hiện tooltip, TypeDoc sinh docs). Nhưng hàm trong code ứng dụng mà tên đã rõ ý định thì comment chỉ tạo gánh nặng bảo trì. Thước đo: liệu ai gọi hàm này có cần thêm thông tin mà tên + kiểu không cung cấp được không?
Đó chính xác là việc của git. Xoá code bạn không dùng, nếu sau này cần, git log và git show sẽ cho lại. Code comment-out không có ngày, không có lý do, không có context - tệ hơn nhiều so với lấy lại từ lịch sử git với commit message rõ ràng.
Được, nhưng có kỳ hạn. TODO rõ ràng (ghi rõ việc gì, tại sao chưa làm, ai chịu trách nhiệm) tốt hơn TODO "để đó". Tốt hơn nữa: tạo một issue/ticket tracker thay vì chôn TODO trong code - ít bị bỏ quên hơn. Dù sao, TODO không nên sống mãi - review định kỳ và xoá những TODO đã hết thời.
Kiểu (type) trả lời "tham số/kết quả là gì". JSDoc trả lời những thứ kiểu không nói được: tại sao tham số có ràng buộc đặc biệt, side effect nào, ví dụ sử dụng. Với hàm public trong thư viện hoặc module dùng chung, kết hợp cả hai cho trải nghiệm IDE tốt nhất. Với code nội bộ, tên rõ + kiểu đủ là thường đủ.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Khi gặp một đoạn code khó hiểu, bước đầu tiên nên làm là gì?
- 1
Săn comment dư thừa
Mở một file bất kỳ trong dự án của mèo con (hoặc mã nguồn mở). Tìm ≥2 comment chỉ lặp lại điều code đã nói rõ.
Hoàn thành khi: Liệt kê comment tìm được + giải thích một câu tại sao chúng dư thừa (code đã nói gì rồi).
- 2
Đổi comment thành code rõ
Lấy một đoạn có comment giải thích "cái gì" (vd
// kiem tra tuoi). Đặt lại tên biến/hàm để comment đó trở nên không cần thiết, rồi xoá comment.Hoàn thành khi: Đoạn code sau khi sửa không còn comment đó nhưng vẫn rõ ý định - bất kỳ ai đọc cũng hiểu.
- 3
Phân loại comment trong một file
Đọc một file dài ≥50 dòng có nhiều comment. Phân loại từng comment: dư thừa / giải thích vì sao / cảnh báo / TODO / lỗi thời / code bị comment-out.
Hoàn thành khi: Bảng phân loại với ≥6 comment; ít nhất nhận diện được mỗi loại một lần.
- 4
Viết một JSDoc đúng
Chọn một hàm public trong dự án TypeScript của mèo con mà chưa có JSDoc. Viết JSDoc đầy đủ: mô tả ngắn,
@param,@returns, và (nếu có) một@throwshoặc ràng buộc đặc biệt.Hoàn thành khi: JSDoc giải thích được "vì sao/ràng buộc" mà chữ ký và kiểu không nói - IDE hiện đúng tooltip khi hover.
- 5
Cảnh báo hậu quả thật
Nghĩ đến một đoạn code trong dự án của mèo con mà nếu ai đó sửa bừa sẽ gây lỗi nghiêm trọng (race condition, gọi theo đúng thứ tự, giới hạn không hiển nhiên). Viết một comment cảnh báo ngắn gọn cho chỗ đó.
Hoàn thành khi: Comment nêu được HẬU QUẢ cụ thể nếu vi phạm, không chỉ nói "cẩn thận" chung chung.
- 6
Dọn code comment-out
Tìm trong dự án của mèo con (hoặc một repo mã nguồn mở) ≥1 khối code bị comment-out. Xác minh git log cho thấy code đó vẫn còn trong lịch sử, rồi xoá nó khỏi file.
Hoàn thành khi: File sau khi xoá gọn hơn;
git log -S <ten_ham_hoac_bien>tìm lại được commit có đoạn code đó.