Lời khuyên “thẩm mỹ tốt” của Brian Kernighan cho thấy mã dễ đọc tiết kiệm thời gian, giảm lỗi và giúp đội thật sự chạy nhanh hơn các mánh khoé tinh vi.

Tên Brian Kernighan xuất hiện ở nhiều nơi mà nhiều dev dùng mà không cần nghĩ: công cụ Unix kinh điển, hệ sinh thái C, và hàng thập kỷ bài viết giúp người ta giải thích chương trình rõ ràng. Dù bạn nhớ The C Programming Language (với Dennis Ritchie), The Unix Programming Environment, hay các bài luận và bài nói của ông, sợi chỉ chung là nhấn mạnh những ý tưởng đơn giản được diễn đạt gọn gàng.
Lời khuyên hay nhất của Kernighan không phụ thuộc vào cú pháp C hay quy ước Unix. Nó nói về cách con người đọc: ta quét để tìm cấu trúc, phụ thuộc vào tên, suy đoán ý định, và bối rối khi mã giấu ý nghĩa phía sau những mẹo. Đó là lý do “thẩm mỹ” trong khả đọc vẫn quan trọng khi bạn viết TypeScript, Python, Go, Java hay Rust.
Ngôn ngữ thay đổi. Tooling cải thiện. Đội vẫn phải giao tính năng dưới áp lực thời gian, và phần lớn mã được bảo trì bởi người khác ngoài tác giả gốc (thường là bạn trong tương lai). Sự rõ ràng là hệ số nhân giúp tất cả điều đó tồn tại được.
Đây không phải là bài tán dương “hero coding” hay yêu cầu ghi nhớ luật cổ điển. Đây là hướng dẫn thực tiễn cho những thói quen giúp mã hàng ngày dễ làm việc hơn:
Ảnh hưởng của Kernighan quan trọng vì nó chỉ ra mục tiêu đơn giản, phù hợp đội: viết mã biết giao tiếp. Khi mã đọc như một lời giải thích rõ ràng, bạn mất ít thời gian giải mã và nhiều thời gian cải thiện nó hơn.
“Thẩm mỹ tốt” không phải phong cách cá nhân, pattern hào nhoáng, hay ép giải pháp vào ít dòng nhất. Nó là thói quen chọn phương án đơn giản và rõ ràng nhất để truyền đạt ý định.
Một giải pháp có thẩm mỹ tốt trả lời câu hỏi cơ bản cho người đọc tiếp theo: Mã này cố gắng làm gì, và vì sao làm như vậy? Nếu câu trả lời cần thao tác tâm lý, giả định ẩn, hoặc giải mã mẹo, thì mã đang phí thời gian của đội.
Phần lớn mã được đọc nhiều hơn là viết. “Thẩm mỹ tốt” coi đọc là hoạt động chính:
Đó là lý do khả đọc không chỉ là thẩm mỹ (không gian thụt lề, độ dài dòng, hay bạn thích snake_case hay không). Những thứ đó hữu ích, nhưng “thẩm mỹ tốt” chủ yếu là làm cho suy luận dễ dàng: tên rõ ràng, luồng điều khiển hiển nhiên, cấu trúc dự đoán được.
Sai lầm phổ biến là tối ưu cho ngắn gọn thay vì rõ ràng. Đôi khi mã rõ ràng nhất dài hơn một chút vì nó làm các bước rõ ràng.
Ví dụ, so sánh hai cách:
Phiên bản thứ hai có thể thêm dòng, nhưng giảm tải nhận thức để kiểm chứng độ đúng. Nó cũng làm bug dễ cô lập và thay đổi an toàn hơn.
Thẩm mỹ tốt là biết khi nào nên ngừng “cải tiến” bằng sự tinh tế và thay vào đó làm ý định rõ ràng. Nếu đồng đội có thể hiểu mã mà không cần bạn giới thiệu, bạn đã chọn đúng.
Mã tinh ranh thường cho cảm giác thắng lợi tại thời điểm: ít dòng hơn, mẹo đẹp, một yếu tố “wow” trong diff. Trong đội thực tế, sự tinh ranh đó biến thành hóa đơn lặp lại—trả bằng thời gian onboarding, thời gian review, và sự do dự mỗi lần ai đó phải chạm vào mã.
Onboarding chậm lại. Người mới không chỉ phải học sản phẩm; họ còn phải học phương ngữ riêng của bạn. Nếu hiểu một hàm đòi hỏi giải mã toán tử tinh tế hoặc quy ước ngầm, người ta sẽ tránh sửa nó—hoặc sửa với sợ hãi.
Review dài hơn và ít tin cậy hơn. Reviewer tiêu tốn năng lượng để chứng minh mẹo đúng thay vì đánh giá hành vi có khớp ý định không. Tệ hơn, mã tinh ranh khó mô phỏng bằng tư duy, nên reviewer dễ bỏ sót các trường hợp biên mà họ sẽ bắt được trong phiên bản thẳng thắn.
Sự tinh ranh tích tụ trong:
Một vài kẻ lặp lại:
17, 0.618, -1) mã hóa quy tắc mà không ai nhớ.\u0026\u0026 / ||) dựa trên việc người đọc biết luật đánh giá tinh vi.Ý của Kernighan về “thẩm mỹ” xuất hiện ở đây: rõ ràng không phải viết ít hơn; là làm ý định hiển nhiên. Nếu phiên bản “thông minh” tiết kiệm 20 giây hôm nay nhưng tốn 20 phút cho mỗi người đọc trong tương lai, thì đó không phải thông minh—đó là đắt đỏ.
“Thẩm mỹ” của Kernighan thường xuất hiện trong các quyết định nhỏ lặp lại. Bạn không cần rewrite lớn để làm mã dễ sống hơn—những chiến thắng nhỏ cộng dồn mỗi khi ai đó quét file, tìm hành vi hoặc sửa bug dưới áp lực.
Một tên tốt giảm nhu cầu comment và làm cho lỗi khó ẩn.
Hướng tới tên tiết lộ ý định phù hợp cách đội bạn nói:
invoiceTotalCents thay vì sum.Nếu một tên bắt bạn phải giải mã, nó đang làm ngược lại nhiệm vụ.
Hầu hết việc đọc là quét. Khoảng trắng và cấu trúc nhất quán giúp mắt tìm điều quan trọng: ranh giới hàm, điều kiện và “happy path.”
Một vài thói quen thực tiễn:
Khi logic rắc rối, readability thường cải thiện bằng cách làm quyết định rõ ràng.
So sánh hai phong cách:
// Harder to scan
if (user \u0026\u0026 user.active \u0026\u0026 !user.isBanned \u0026\u0026 (role === 'admin' || role === 'owner')) {
allow();
}
// Clearer
if (!user) return deny('missing user');
if (!user.active) return deny('inactive');
if (user.isBanned) return deny('banned');
if (role !== 'admin' \u0026\u0026 role !== 'owner') return deny('insufficient role');
allow();
Phiên bản thứ hai dài hơn, nhưng đọc như một checklist—và dễ mở rộng mà không phá vỡ.
Những lựa chọn “nhỏ” này là nghề hàng ngày của mã bảo trì: tên trung thực, format dẫn dắt người đọc, và luồng điều khiển không bắt bạn phải tập thể dục trí óc.
Phong cách rõ ràng của Kernighan hiện rõ nhất ở cách bạn chia công việc thành hàm và module. Người đọc nên có thể lướt qua cấu trúc, đoán phần nào làm gì, và đúng phần lớn trước khi đọc chi tiết.
Hướng tới hàm chỉ làm một việc ở một “mức zoom” nhất định. Khi một hàm trộn validation, business rule, formatting, và I/O, người đọc phải giữ nhiều luồng trong đầu.
Một bài kiểm tra nhanh: nếu bạn thấy mình viết comment như “// now do X” trong hàm, thì X thường là ứng viên tốt để tách thành hàm riêng có tên rõ ràng.
Danh sách tham số dài là thuế phức tạp ẩn: mỗi call site trở thành một file cấu hình nhỏ.
Nếu vài tham số luôn đi cùng nhau, hãy nhóm chúng. Objects options (hoặc struct nhỏ) có thể làm call site tự giải thích—nếu bạn giữ nhóm đó nhất quán và tránh nhét mọi thứ vào một túi “misc”.
Ngoài ra, ưu tiên truyền khái niệm miền hơn là primitives. UserId tốt hơn string, và DateRange tốt hơn (start, end) khi các giá trị đó có quy tắc.
Module là lời hứa: “Mọi thứ bạn cần cho khái niệm này ở đây, phần còn lại ở nơi khác.” Giữ module nhỏ đủ để bạn nắm mục đích của nó trong đầu, và thiết kế ranh giới giảm side effect.
Thói quen thực tiễn:
Khi cần shared state, đặt tên trung thực và ghi invariants. Rõ ràng không phải tránh phức tạp—mà đặt nó ở nơi người đọc mong đợi. For more on maintaining these boundaries during changes, see /blog/refactoring-as-a-habit.
Thẩm mỹ của Kernighan thể hiện ở cách bạn comment: mục tiêu không phải chú thích mọi dòng, mà giảm nhầm lẫn trong tương lai. Comment tốt nhất là cái ngăn một giả định sai—đặc biệt khi code đúng nhưng bất ngờ.
Comment nhắc lại code (“tăng i”) chỉ tạo nhiễu và làm người đọc bỏ comment. Comment hữu ích giải thích ý định, đánh đổi, hoặc ràng buộc không rõ ràng từ cú pháp.
# Bad: says what the code already says
retry_count += 1
# Good: explains why the retry is bounded
retry_count += 1 # Avoids throttling bans on repeated failures
Nếu bạn muốn viết comment “làm gì”, đó thường là dấu hiệu code nên rõ hơn (tên tốt hơn, hàm nhỏ hơn, luồng đơn giản hơn). Hãy để code mang dữ kiện; comment mang lý do.
Không gì làm mất lòng tin nhanh hơn comment lỗi thời. Nếu comment là tùy chọn, nó sẽ trôi dần; nếu sai, nó trở thành nguồn bug. Một thói quen thực dụng: coi cập nhật comment là một phần của thay đổi, không phải “nice to have”. Trong review, có quyền hỏi: Comment này còn đúng với hành vi không? Nếu không, hãy cập nhật hoặc xóa. “Không có comment” tốt hơn “comment sai.”
Inline comment dành cho bất ngờ cục bộ. Hướng dẫn rộng hơn thuộc về docstring, README, hoặc ghi chú dev—đặc biệt cho:
Docstring tốt nói cho người dùng cách dùng hàm đúng và lỗi gì có thể gặp, không kể từng bước cài đặt. Một ghi chú ngắn trong /docs hoặc /README có thể lưu lại câu chuyện “tại sao ta làm như thế” để sống sót qua refactor.
Lợi ích im lặng: ít comment hơn, nhưng mỗi comment đều xứng đáng.
Phần lớn mã trông “ổn” trên happy path. Thử thách thực sự của thẩm mỹ là khi input thiếu, service chậm, hoặc người dùng làm điều bất ngờ. Khi áp lực, mã tinh ranh thường giấu sự thật. Mã rõ ràng làm lỗi hiển nhiên—và có thể phục hồi.
Message lỗi là một phần sản phẩm và workflow debug. Viết như người đọc mệt mỏi đang trực.
Bao gồm:
Nếu bạn có logging, thêm context có cấu trúc (như requestId, userId, hoặc invoiceId) để message hành động được mà không phải lục thêm dữ liệu không liên quan.
Có xu hướng “xử lý mọi thứ” bằng one-liner tinh tế hoặc catch-all generic. Thẩm mỹ tốt là chọn vài trường hợp biên quan trọng và làm chúng hiển nhiên.
Ví dụ, branch rõ ràng cho “input rỗng” hoặc “không tìm thấy” thường đọc tốt hơn một chuỗi biến đổi tạo ra null ở giữa. Khi trường hợp đặc biệt quan trọng, đặt tên cho nó và đưa ra trước.
Trộn hình dạng trả về (đôi khi object, đôi khi string, đôi khi false) buộc người đọc giữ cây quyết định. Ưu tiên pattern nhất quán:
Xử lý thất bại rõ ràng giảm ngạc nhiên—và ngạc nhiên là nơi bug và trang đêm sinh sôi.
Rõ ràng không chỉ về những gì bạn muốn khi viết mã. Làm sao người tiếp theo mong thấy khi mở file lúc 4:55pm. Nhất quán biến “đọc mã” thành nhận dạng pattern—ít bất ngờ, ít hiểu lầm, ít tranh luận lặp lại.
Một style guide tốt ngắn, cụ thể và thực tế. Nó không mã hóa mọi sở thích; nó quyết định các câu hỏi lặp lại: quy ước đặt tên, cấu trúc file, mẫu xử lý lỗi, và định nghĩa “done” cho test.
Giá trị thực sự là xã hội: nó ngăn cuộc bàn luận lặp lại mỗi PR. Khi có văn bản, review chuyển từ “tôi thích X” sang “chúng ta đã đồng ý X (và đây là lý do)”. Giữ tài liệu sống và dễ tìm—nhiều đội ghim nó trong repo (ví dụ, /docs/style-guide.md) để gần code.
Dùng formatter và linter cho mọi thứ đo được và nhàm chán:
Điều này giải phóng con người để tập trung vào ý nghĩa: đặt tên, dạng API, trường hợp biên và liệu mã có khớp ý định.
Luật thủ công vẫn quan trọng khi mô tả quyết định thiết kế—ví dụ, “ưu tiên early returns để giảm lồng” hay “mỗi module chỉ một entry point public.” Công cụ không thể phán đoán hoàn toàn những thứ đó.
Đôi khi phức tạp được biện minh: ngân sách hiệu năng chặt, ràng buộc nhúng, concurrency khó, hay hành vi nền tảng đặc thù. Thỏa thuận nên là: ngoại lệ được cho phép, nhưng phải rõ ràng.
Tiêu chuẩn đơn giản: ghi ngắn gọn đánh đổi trong comment, thêm micro-benchmark hoặc số đo khi viện dẫn hiệu năng, và cô lập mã phức tạp sau một interface rõ để phần lớn codebase vẫn dễ đọc.
Một review tốt nên là bài học ngắn, tập trung về “thẩm mỹ tốt”. Kernighan không nói mã tinh ranh là xấu—mà nói tinh ranh đắt khi người khác phải sống với nó. Review là nơi đội làm rõ đánh đổi đó và chọn rõ ràng có chủ đích.
Bắt đầu bằng câu hỏi: “Một đồng đội có hiểu trong một lần đọc không?” Thường nghĩa là kiểm tra tên, cấu trúc, test và hành vi trước khi đi vào tối ưu vi mô.
Nếu mã đúng nhưng khó đọc, coi khả đọc như một lỗi thật sự. Gợi ý đổi tên để phản ánh ý định, tách hàm dài, đơn giản hóa luồng, hoặc thêm test nhỏ minh họa hành vi dự kiến. Một review bắt được “nó chạy nhưng tôi không biết vì sao” ngăn hàng tuần bối rối sau này.
Một thứ tự thực dụng:
Review vỡ khi phản hồi mang tính chấm điểm. Thay vì “Tại sao lại làm thế?”, thử:
Câu hỏi mời hợp tác và thường làm hiện các ràng buộc bạn chưa biết. Đề xuất truyền đạt hướng mà không ngụ ý kém cỏi. Giai điệu này giúp “thẩm mỹ” lan trong đội.
Muốn khả đọc nhất quán, đừng phụ thuộc vào tâm trạng reviewer. Thêm vài “kiểm tra khả đọc” vào template review và định nghĩa xong:
Qua thời gian, review chuyển từ kiểm soát phong cách sang dạy phán đoán—đúng kiểu kỷ luật hằng ngày Kernighan khuyến nghị.
Công cụ LLM có thể sinh mã chạy nhanh, nhưng “chạy” không phải tiêu chuẩn Kernighan nhắm tới—giao tiếp mới là. Nếu đội dùng workflow vibe-coding (ví dụ xây tính năng qua chat và lặp trên mã sinh), đáng để coi khả đọc là tiêu chí chấp nhận hàng đầu.
Trên nền tảng như Koder.ai, nơi bạn có thể sinh frontend React, backend Go và app Flutter từ prompt chat (và xuất mã nguồn sau đó), cùng thói quen thẩm mỹ áp dụng:
Tốc độ có giá trị nhất khi sản phẩm vẫn dễ cho con người review, duy trì và mở rộng.
Rõ ràng không phải thứ đạt được một lần. Mã giữ được khả đọc chỉ nếu bạn tiếp tục nắn nó về cách nói rõ ràng khi yêu cầu thay đổi. Tinh thần Kernighan phù hợp ở đây: ưu tiên cải thiện nhỏ, dễ hiểu thay vì rewrite anh hùng hay one-liner “thông minh” gây ấn tượng hôm nay nhưng rối rắm tháng tới.
Refactor an toàn nhất là nhàm chán: thay đổi tí một và giữ hành vi như trước. Nếu có test, chạy chúng sau mỗi bước. Nếu không, thêm vài kiểm tra tập trung quanh vùng bạn chạm—xem như rào tạm để cải thiện cấu trúc mà không sợ.
Nhịp thực dụng:
Commit nhỏ cũng giúp review dễ hơn: đồng đội đánh giá ý định thay vì săn tác dụng phụ.
Bạn không cần loại bỏ mọi cấu trúc tinh ranh một lần. Khi chạm mã để làm tính năng hay sửa bug, đổi lối tắt bằng các tương đương thẳng thắn:
Đây là cách rõ ràng thắng trong đội: cải thiện từng “hotspot” khi người ta đã làm việc ở đó.
Không phải mọi dọn dẹp đều cấp bách. Quy tắc hữu dụng: refactor ngay khi mã đang thay đổi, thường bị hiểu sai, hoặc có khả gây bug. Lên lịch khi mã ổn định và cô lập.
Làm cho nợ refactor hiển nhiên: để TODO ngắn có ngữ cảnh, hoặc thêm ticket mô tả đau điểm (“khó thêm phương thức thanh toán mới; hàm làm 5 việc”). Khi đó bạn quyết định có chủ đích—thay vì để mã khó hiểu trở thành thuế thường trực của đội.
Nếu muốn “thẩm mỹ tốt” xuất hiện liên tục, hãy làm nó dễ thực hành. Đây là checklist nhẹ bạn có thể dùng khi lập kế hoạch, code và review—ngắn để nhớ, đủ cụ thể để hành động.
Trước: process(data) làm validation, parsing, save và logging trong một chỗ.
Sau: Tách validateInput, parseOrder, saveOrder, logResult. Hàm chính trở thành dàn mục lục dễ đọc.
Trước: if not valid then return false lặp nhiều lần.
Sau: Một section guard upfront (hoặc một hàm validate) trả về danh sách vấn đề rõ ràng.
Trước: x, tmp, flag2, doThing().
Sau: retryCount, draftInvoice, isEligibleForRefund, sendReminderEmail().
Trước: Vòng lặp với ba trường hợp đặc biệt ẩn giữa.
Sau: Xử lý trường hợp đặc biệt trước (hoặc tách helper), rồi chạy vòng lặp thẳng.
Chọn một cải tiến để áp dụng tuần này: “không viết thêm viết tắt”, “happy path trước”, “tách một helper mỗi PR” hay “mỗi thông báo lỗi có bước tiếp theo”. Theo dõi bảy ngày, rồi giữ lại những gì thực sự giúp đọc dễ hơn.
Ảnh hưởng của Kernighan không phải nằm ở C mà ở một nguyên tắc bền bỉ: mã là phương tiện giao tiếp.
Ngôn ngữ và framework thay đổi, nhưng các đội vẫn cần mã dễ scan, lý giải, review và debug—đặc biệt là vài tháng sau và khi áp lực thời gian cao.
“Thẩm mỹ tốt” nghĩa là luôn chọn phương án đơn giản và rõ ràng nhất để truyền đạt ý định.
Một kiểm tra đơn giản: liệu đồng đội có trả lời được “mã này làm gì và vì sao làm như vậy?” mà không phải giải mã các mẹo hay dựa vào giả định ẩn không?
Bởi vì phần lớn mã được đọc nhiều hơn là được viết.
Tối ưu cho người đọc giảm thời gian onboard, xoa dịu friction khi review và giảm nguy cơ sai sót khi sửa—đặc biệt khi người bảo trì là “tôi trong tương lai” với ít ngữ cảnh hơn.
“Thuế sự tinh ranh” xuất hiện như:
Nếu phiên bản tinh tế tiết kiệm vài giây hôm nay nhưng tốn vài phút mỗi lần chạm vào sau này, thì đó là tổn thất ròng.
Những thủ phạm phổ biến:
Những mẫu này che giấu trạng thái trung gian và khiến các trường hợp biên dễ bị bỏ sót khi review.
Khi nó giảm tải nhận thức.
Làm các bước rõ ràng với biến trung gian có tên (ví dụ: validate → normalize → compute) giúp kiểm chứng độ đúng dễ hơn, đơn giản hóa debug và làm thay đổi trong tương lai an toàn hơn—dù có thể thêm vài dòng.
Những thói quen đặt tên hiệu quả:
invoiceTotalCents thay vì sum)Nếu bạn phải giải mã một tên, nghĩa là nó không hoàn thành nhiệm vụ; tên nên giảm nhu cầu comment.
Ưu tiên nhánh đơn giản và giữ “happy path” hiển nhiên.
Một vài mẹo:
Comment cho tại sao, không phải làm gì.
Comment tốt ghi lại ý định, đánh đổi, hoặc ràng buộc không rõ ràng từ cú pháp. Tránh ghi lại những gì code đã nói sẵn, và coi việc cập nhật comment là một phần của thay đổi—comment sai còn tệ hơn không có comment.
Dùng công cụ cho các quy tắc cơ học (formatting, imports, lỗi rõ ràng) và để con người xử lý ý nghĩa.
Một style guide nhẹ giúp giải quyết các quyết định lặp lại (đặt tên, cấu trúc, mẫu xử lý lỗi). Khi cần ngoại lệ vì hiệu năng hay ràng buộc nền tảng, hãy ghi rõ đánh đổi và cô lập sự phức tạp sau một giao diện sạch.