Clean Code JavaScript: Repo mà dev JS nào cũng nên đọc ít nhất một lần
Review repo clean-code-javascript tổng hợp nguyên tắc viết code JS sạch từ đặt tên biến đến SOLID, giúp codebase dễ maintain và scale hơn hẳn.
Nguyễn Nhật Long
@nguyennhatlong1303
Mình nhớ hồi mới đi làm, có lần submit một PR mà senior review xong comment đúng một câu: "Code chạy đúng rồi, nhưng đọc không hiểu gì cả." Lúc đó mình hơi tự ái, nhưng ngồi lại đọc code của chính mình sau 2 tuần thì... đúng thật, chính mình cũng không hiểu mình viết cái gì. Đó là lúc mình bắt đầu tìm hiểu về clean code, và một trong những tài liệu đầu tiên mình đọc chính là repo clean-code-javascript trên GitHub.
Hôm nay mình muốn review kỹ repo này không phải kiểu liệt kê lại nội dung, mà là chia sẻ góc nhìn sau mấy năm áp dụng thực tế, cái gì hay, cái gì cần lưu ý, và ai nên đọc nó.
Repo này thực chất là gì?
Repo clean-code-javascript là bản dịch tiếng Việt (và cũng có bản gốc tiếng Anh của Ryan McDermott) lấy cảm hứng từ cuốn sách kinh điển Clean Code của Robert C. Martin (Uncle Bob), nhưng được adapt lại hoàn toàn cho JavaScript. Nó không phải là một library hay tool để bạn npm install. Nó là một tập hợp các nguyên tắc, quy ước, và ví dụ cụ thể về cách viết code JS sao cho dễ đọc, dễ maintain, dễ test.
Cấu trúc repo khá đơn giản chỉ là một file README dài, chia thành các section theo chủ đề: đặt tên biến, hàm, object và class, SOLID principles, testing, concurrency, error handling, formatting... Mỗi section đều có cặp ví dụ Bad vs Good rất trực quan.
Theo kinh nghiệm của mình, cái hay nhất của repo này không phải là nó dạy bạn điều gì quá mới mẻ. Phần lớn các nguyên tắc bạn đã "biết" rồi nhưng nó giúp bạn hệ thống hóa lại và có ví dụ cụ thể để tham chiếu khi cần.
Đặt tên biến tưởng dễ mà khó vãi
Phần đầu tiên repo cover là Variables. Nghe thì basic, nhưng mình dám cá là phần lớn bug trong codebase production không phải do logic sai, mà do dev đọc code hiểu nhầm ý nghĩa của biến.
Ví dụ repo đưa ra rất sát thực tế:
1// Bad2const yyyymmdstr = moment().format('YYYY/MM/DD');34// Good5const currentDate = moment().format('YYYY/MM/DD');
Tưởng ai cũng biết, nhưng bạn mở bất kỳ project nào đã sống được 2-3 năm, mình đảm bảo sẽ thấy những biến kiểu d, tmp, data2, result1 nhan nhản. Repo này nhấn mạnh một nguyên tắc mình rất tâm đắc: dùng tên có thể tìm kiếm được (searchable names). Khi codebase lớn, bạn sẽ grep rất nhiều. Một biến tên d thì grep ra cả ngàn kết quả, còn daysSinceCreation thì chỉ ra đúng chỗ cần tìm.
Một điểm nữa mình thấy hay là nguyên tắc đừng thêm context thừa:
1// Bad2const Car = {3 carMake: 'Honda',4 carModel: 'Accord',5 carColor: 'Blue'6};78// Good9const Car = {10 make: 'Honda',11 model: 'Accord',12 color: 'Blue'13};
Đã biết object là Car rồi thì property không cần lặp lại prefix car nữa. Cái này mình thấy rất nhiều anh em mắc phải, đặc biệt khi code backend với Mongoose/Sequelize model.
Functions ít argument, ít side effect, làm một việc duy nhất
Phần Functions là phần dài nhất và cũng là phần mình nghĩ giá trị nhất trong repo. Có vài nguyên tắc mà mình áp dụng hàng ngày:
Tối đa 2 arguments cho một function. Nếu cần nhiều hơn, dùng object destructuring. Cái này thay đổi cách mình viết code hoàn toàn:
1// Bad2function createMenu(title, body, buttonText, cancellable) { ... }34// Good5function createMenu({ title, body, buttonText, cancellable }) { ... }
Lợi ích rõ nhất là khi gọi hàm, bạn không cần nhớ thứ tự argument. Ai từng debug một hàm có 5-6 params mà truyền sai thứ tự sẽ hiểu cái đau này.
Function nên làm đúng một việc. Nghe như câu cửa miệng, nhưng repo đưa ví dụ rất thuyết phục. Khi một function vừa validate, vừa transform, vừa save bạn không thể test từng phần riêng lẻ, không thể reuse, và khi có bug thì phải đọc cả function để tìm.
Mình thấy cái này hay ở chỗ repo không chỉ nói lý thuyết mà show code before/after rất rõ ràng, nên anh em junior đọc xong có thể áp dụng ngay được.
SOLID trong JavaScript không chỉ dành cho Java
Đây là phần mình muốn nói kỹ nhất, vì rất nhiều dev JS nghĩ SOLID là chuyện của Java/C#. Sai. Repo này chứng minh rằng SOLID hoàn toàn applicable cho JS, đặc biệt khi bạn làm việc với class hoặc module pattern.
Để dễ hình dung, mình tóm tắt 5 nguyên tắc và cách repo áp dụng vào JS:
Theo kinh nghiệm của mình, hai nguyên tắc S và D là impactful nhất trong JS day-to-day. Single Responsibility giúp bạn tránh được những file 500+ dòng mà ai cũng sợ đụng vào. Dependency Inversion giúp code testable hơn rất nhiều thay vì import trực tiếp một module, bạn inject nó vào, và khi test thì mock dễ dàng.
| Nguyên tắc | Ý nghĩa | Áp dụng JS thế nào |
|---|---|---|
| **S** Single Responsibility | Mỗi class/module chỉ có một lý do để thay đổi | Tách logic ra nhiều module nhỏ, mỗi module handle một concern |
| **O** Open/Closed | Mở cho mở rộng, đóng cho sửa đổi | Dùng composition, higher-order functions thay vì sửa code cũ |
| **L** Liskov Substitution | Subclass phải thay thế được parent class | Khi extend class, đảm bảo contract không bị phá vỡ |
| **I** Interface Segregation | Client không nên bị ép phụ thuộc vào interface không dùng | Tách interface lớn thành nhiều interface nhỏ (dùng composition) |
| **D** Dependency Inversion | Depend on abstractions, not concretions | Inject dependencies thay vì hardcode |
Repo đưa ví dụ về Dependency Inversion khá hay thay vì một class InventoryTracker tự tạo HTTP request bên trong, nó nhận một requester object từ bên ngoài. Khi cần đổi từ HTTP sang WebSocket hay GraphQL, bạn chỉ cần truyền một requester khác mà không sửa InventoryTracker.
Error Handling đừng nuốt lỗi
Một phần mà mình thấy nhiều dev Việt Nam hay bỏ qua là Error Handling. Repo nhấn mạnh: đừng bao giờ ignore caught errors.
1// Bad2try {3 functionThatMightThrow();4} catch (error) {5 console.log(error);6}78// Good9try {10 functionThatMightThrow();11} catch (error) {12 notifyUserOfError(error);13 reportErrorToService(error);14}
console.log(error) trong production thì coi như mất luôn error đó. Không ai ngồi đọc console log trên server cả. Repo khuyến khích dùng error reporting service (Sentry, Bugsnag,...) và handle error một cách có ý thức.
Mình bổ sung thêm: với Promise, repo cũng nhấn mạnh đừng để unhandled rejection. Cái này từ Node.js 15 trở đi sẽ crash process luôn nếu không handle, nên không phải chuyện nhỏ.
Testing repo nhắc nhẹ nhưng quan trọng nặng
Phần testing trong repo khá ngắn gọn nhưng đúng trọng tâm. Nguyên tắc chính: mỗi test chỉ test một concept duy nhất. Mình thấy rất nhiều test case kiểu "test everything in one it block" khi fail thì không biết fail ở đâu, debug mệt muốn chết.
Repo không đi sâu vào tool (Jest, Mocha, Vitest...) mà focus vào mindset. Cái này mình thấy đúng hướng tool thay đổi liên tục, nhưng nguyên tắc viết test tốt thì không đổi.
Điểm mạnh và điểm cần lưu ý
Sau mấy năm dùng repo này như reference, mình đúc kết lại:
Anh em lưu ý: repo này là guidelines, không phải rules. Đừng biến nó thành dogma. Có những lúc bạn cần viết code "không clean" vì deadline, vì performance, vì context cụ thể của project. Clean code là đích đến, không phải điều kiện tiên quyết để ship feature.
| Điểm mạnh | Điểm cần lưu ý |
|---|---|
| Ví dụ Bad/Good cực kỳ trực quan | Một số ví dụ hơi lý tưởng hóa, thực tế messy hơn nhiều |
| Cover đủ từ basic đến advanced (SOLID) | Không cover các pattern mới như hooks, composables |
| Bản tiếng Việt giúp anh em đọc nhanh | Bản dịch đôi chỗ hơi cứng, nên đọc song song bản gốc |
| Không gắn với framework cụ thể nào | Thiếu ví dụ thực tế từ real-world project |
| Free, open-source, cộng đồng đóng góp | Không cập nhật thường xuyên (ES2022+ features chưa có) |
Ai nên đọc, ai có thể skip?
Nếu bạn là junior dev (0-2 năm kinh nghiệm) đọc ngay, đọc kỹ, bookmark lại. Những nguyên tắc trong này sẽ giúp bạn level up nhanh hơn bất kỳ tutorial nào về framework.
Nếu bạn là mid-level (2-4 năm) đọc lại để hệ thống hóa. Có thể bạn đã biết 70-80% nội dung, nhưng 20% còn lại sẽ fill những gap mà bạn không biết mình có.
Nếu bạn là senior dùng nó như reference khi review code cho team. Thay vì comment "code này không clean", bạn có thể link đến section cụ thể trong repo. Nó giúp feedback khách quan hơn và người nhận feedback cũng dễ chấp nhận hơn.
Mình cũng recommend dùng repo này trong onboarding process cho team. Cho member mới đọc qua trong tuần đầu, rồi thảo luận cùng team xem những nguyên tắc nào team muốn adopt. Không cần áp dụng hết chọn ra 5-10 nguyên tắc quan trọng nhất với context project của bạn là đủ.
So sánh nhanh với các tài liệu tương tự
Nếu chỉ chọn một thứ để đọc, mình sẽ chọn repo clean-code-javascript vì nó cân bằng tốt giữa lý thuyết và thực hành, và không quá dài để đọc hết trong một buổi chiều.
| Tài liệu | Ngôn ngữ target | Độ sâu | Ví dụ code | Phù hợp với |
|---|---|---|---|---|
| clean-code-javascript | JavaScript | Trung bình - Cao | Rất nhiều, Bad/Good | JS/TS dev mọi level |
| Airbnb Style Guide | JavaScript | Cao | Nhiều, chi tiết config ESLint | Team cần coding standard |
| Clean Code (sách) | Java (chủ yếu) | Rất cao | Java-centric | Dev muốn hiểu sâu lý thuyết |
| Google JS Style Guide | JavaScript | Cao | Vừa phải | Team theo Google ecosystem |
Cuối cùng, mình muốn chia sẻ một điều mà mình học được sau nhiều năm: code sạch không phải viết cho compiler, mà viết cho người đọc tiếp theo và người đó rất có thể là chính bạn sau 6 tháng. Repo này là một trong những tài liệu tốt nhất để bắt đầu xây dựng thói quen đó. Star nó trên GitHub đi, rồi mở ra đọc mỗi khi cảm thấy code mình bắt đầu "có mùi".
Nguyễn Nhật Long
@nguyennhatlong1303Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.
Thấy hay? Chia sẻ cho bạn bè!