Một số cách để viết mã dễ đọc hơn trong JavaScript/Node.js

Một số cách để viết mã dễ đọc hơn trong JavaScript/Node.js

Những mẩu tin ngắn hàng ngày dành cho bạn
  • Hẳn là nhiều người ở đây đã nghe đến kiểu tấn công bảo mật Clickjacking rồi nhỉ. Kẻ tấn công thường nhúng một website (thường là mục tiêu) vào trong một iframe trên website của chúng, sau đó làm mờ hoặc ẩn nó đi rồi đặt vào vị trí các nút bấm trên web, ví dụ "Bấm vào để nhận quà". Đâu ai ngờ rằng phía trên nút bấm đó là một nút bấm khác trong iframe. Khá nguy hiểm!

    Nhưng trình duyệt đã có cách ngăn chặn kiểu tấn công này bằng các quy tắc như tiêu đề X-Frame-Options, frame-ancestors của CSP và SameSite: Lax/Strict của Cookies...

    Mới đây, đã xuất hiện thêm kiểu tấn công mới - "DoubleClickjacking" 😨. Đại ý là "hắn" lợi dụng hành động double click để lừa người dùng bấm vào một nút mà hắn muốn. Chi tiết hơn trong bài viết này: DoubleClickjacking: A New Era of UI Redressing.

    » Xem thêm
  • Mọi người đã nghe nói đến Jujutsu - jj - một dạng quản lý phiên bản cho mã nguồn (version control system) chưa? Có vẻ như nó đang nhận được nhiều sự quan tâm.

    Chờ xíu! Chẳng phải git đã quá tốt rồi sao? Thế thì chế ra thằng jj để làm gì nữa? Cũng hơi khó trả lời nhỉ? Mỗi công cụ sinh ra chắc chắn phải cải thiện hoặc khắc phục được nhược điểm của cái trước. Cho nên jj ắt hẳn phải làm được điều gì đó mà git chưa làm được nên mới nổi lên như vậy.

    Thật ra mình đã nghe nói đến jj từ vài tháng trước rồi, nhưng vào đọc thì toàn kiến thức cao siêu. Hoặc là đang mang nặng cái lối suy nghĩ của git vào trong đầu rồi nên chưa lĩnh hội ra được điều gì cả.

    Mình hay có kiểu cái gì đọc lần 1 mà không hiểu thì đọc tiếp lần 2, lần 2 không hiểu thì đọc tiếp lần 3... đến lần thứ n mà vẫn không hiểu thì bỏ. Cơ mà không phải là từ bỏ mà một thời gian sau đó quay lại đọc tiếp. Đến một lúc nào đó khả năng mình sẽ hiểu ra một ít vấn đề, thế mới tài 😆.

    Thì cái jj này có vẻ như nó đang mở ra được tính linh hoạt trong việc "cam kết" mã. Tưởng tượng bạn đang làm việc trên một dự án, đang ở nhánh này, muốn sang nhánh khác để sửa, nhưng mà lại đang viết dở ở nhánh này, thế là phải stash, rồi checkout, rồi commit, rồi merge hoặc rebase lại vào nhánh cũ... nhìn chung quá trình làm việc với git nghiêm ngặt đến mức cứng nhắc, cần nhiều thao tác để giải quyết một vấn đề, chưa kể cái cây commit (commit-tree) nữa thì ôi thôi, khỏi xem cho đỡ nhức mắt. Thế nên ông jj này đang làm cách nào đó để bạn khỏi cần phải quan tâm đến các nhánh luôn, sửa trực tiếp vào commit. Nghe ảo nhỉ 😂.

    Đấy mới lĩnh hội được đến đấy, hy vọng sau n lần đọc lại nữa mình sẽ viết được một bài chi tiết hơn về công cụ này.

    » Xem thêm
  • Gòi gòi tới công chiện gòi 🤤🤤🤤

    » Xem thêm

Vấn đề

Điều mà tôi tin rằng rất nhiều lập trình viên mong muốn tiến tới là việc viết mã sao cho dễ đọc dễ hiểu. Bằng chứng là có rất nhiều Design Pattern được đưa ra để hướng dẫn mọi người giải quyết vấn đề theo cách mà nhiều người vẫn làm. Nhưng đó chưa phải là tất cả, việc viết mã hoàn chỉnh thì lại phụ thuộc vào từng lập trình viên.

Chúng ta có nhiều công cụ hỗ trợ soạn thảo mã nguồn theo nhiều cách. Như định dạng, màu sắc, giao diện hiển thị, công cụ hỗ trợ gỡ lỗi... thoải mái lựa chọn theo sở thích hoặc cùng làm việc nhóm. Bên cạnh đó, vẫn có những quy tắc được đặt ra để các thành viên tham gia phát triển phải tuân theo. Tạm bỏ qua vấn đề định dạng (format), ngày hôm nay chúng ta hãy cùng nhau tìm hiểu xem có cách nào để viết mã dễ đọc hơn không nhé!

Một số cách làm mã dễ đọc hơn

Thông thường, những người viết mã khó bảo trì thường mang trong mình phong cách Ninja mà tôi có một bài viết chi tiết tại Nhắc đến Ninja Code - họ là ai mà khiến cho nhiều người phải "khiếp sợ"?. Để viết mã dễ đọc, ngoài cách "rửa tay gác kiếm" khỏi giới nhẫn giả, thì dưới đây là một số cách bạn đọc có thể tham khảo từ chính tôi.

Tên biến và tên hàm

Biến và hàm là hai thành phần cơ bản và quan trọng nhất của bất kì chương trình nào. Việc đặt cho chúng những cái tên dễ hiểu là điều hết sức quan trọng trong việc đọc hiểu mã.

Có nhiều nguyên tắc đặt tên biến và hàm mà bạn có thể tìm thấy trên mạng. Ví dụ tôi thường đặt tên biến theo kiểu camelCase, là danh từ, gắn liền với nội dung mà nó nắm giữ, các biến thuộc dạng hằng số hoặc chỉ thay đổi theo biến môi trường thì sẽ viết hoa và theo định dạng UPPER_CASE... Tên hàm gắn liền với một hành động, nghĩa là động từ, theo kiểu camelCase. Chức năng của hàm gắn liền với cái tên của nó, chỉ làm một công việc còn nếu nhiều hơn, tách chúng thành các hàm nhỏ hơn.

Tránh Magic Number hay Hardcoded Value

Thi thoảng trong chương trình, chúng ta hay bắt gặp việc so sánh biến với một con số nào đó ví dụ như:

if (user.lastSeen < 4321)
...

4321 lúc này được gọi là Magic Number hay Hardcoded Value, chúng ta không biết chính xác con số đó biểu thị điều gì đơn giản vì nó không có tên. Điều này khiến người đọc mã phải bối rối về sự xuất hiện của chúng. Thay vào đó, hãy sử dụng một biến để biểu thị cho ý nghĩ của những con số này.

const timeActive = 4321;
if (timeExpired < timeActive)
...

Lệnh điều kiện

Có một sự thật là đoạn mã nào thụt lề càng nhiều thì logic sẽ càng nhiều và càng phức tạp. Hãy xem xét một ví dụ dưới đây.

async function main() {
  const user = await getUser(id);

  if (user) {
    if (user.lastSeen) {
      const userSeen = dayjs(user.lastSeen);
    } else {
      const userSeen = dayjs();
      if (userSeen.isAfter(dayjs().subtract(1, "day"))) {
        showUserOnline();
      } else {
        showUserOffline();
      }
    }
  }
}

Thật ra không có một quy tắc cụ thể nào cho việc sử dụng if...else, nhưng hãy nhìn vào ví dụ trên có phải if...else càng nhiều logic sẽ càng phức tạp, sau này có bảo trì, sửa lại mã cũng trở nên khó khăn hơn vì có nhiều logic cần được làm sáng tỏ trước khi bắt tay vào sửa mã.

Nếu có thể, hãy hạn chế các lệnh điều kiện if...else lồng nhau. Sử dụng switch...case thay thế trong những trường hợp phức tạp hơn, và tốt nhất hãy làm "phẳng" cây logic nhất có thể.

Hạn chế Side-Effect

Có thể nói Side Effect đã làm thay đổi hoàn toàn cách viết mã của tôi từ khi biết đến. Để nói về vấn đề này, tôi có một bài viết chi tiết là Pure Function trong Javascript. Tại sao chúng ta nên biết càng sớm càng tốt?.

Nhìn chung, việc hạn chế Side-Effect giúp chúng ta có một cấu trúc chương trình rành mạch hơn, tránh việc gọi một hàm thôi mà làm thay đổi hết trạng thái của chương trình.

Áp dụng functional

Bất cứ chỗ nào xử lý logic, hãy tách thành những hàm nhỏ hơn và đặt tên cho chúng. Hãy xem xét một ví dụ dưới đây:

const userRoleMap = {
  0: 'admin',
  1: 'moderator',
  2: 'user',
};

fetch('https://api.example.com/users')
  .then(response => response.json())
  .then((user) => {
    return user.filter(user => user.active)
  })
  .then((user) => {
    return user.map(user => {
      return {
        id: user.id,
        name: user.name,
        roleName: userRoleMap[user.role],
      }
    })
  });

Đoạn mã trên lấy danh sách users từ một endpoint về, lọc ra danh sách users đang kích hoạt và sau đó thêm thuộc tính roleName dựa vào role của mỗi users.

Có thể mất một thời gian để hiểu được ý nghĩa của đoạn mã trên, vì chúng ta đang phải tập trung vào logic để hiểu. Thay vào đó, đoạn mã trên có thể được viết lại bằng cách tách các đoạn mã xử lý logic ra thành các hàm nhỏ hơn.

const userRoleMap = {
  0: 'admin',
  1: 'moderator',
  2: 'user',
};

const filterActiveUsers = (users) => {
  return users.filter(user => user.active)
};

const mapUsersRole = (users) => {
  return users.map(user => {
    return {
      id: user.id,
      name: user.name,
      role: userRoleMap[user.role],
    }
  })
};

fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(filterActiveUsers)
  .then(mapUsersRole);

Hãy nhìn vào phần thân chương trình, toàn bộ logic đã hoàn toàn lộ rõ là filterActiveUsersmapUsersRole. Điều này giúp chúng ta nhanh chóng nắm bắt được luồng xử lý dữ liệu, sau này nếu muốn xem chi tiết chỉ cần nhảy đến các hàm liên quan.

Sử dụng comment hợp lý

Comment là một vấn đề gây nhiều tranh cãi, có người nói ai càng comment chứng tỏ càng thiếu kinh nghiệm vì họ không biết cách viết mã sao cho dễ đọc!? Quan điểm của tôi là hãy sử dụng comment đúng lúc và không quá phụ thuộc vào nó để giải thích chi tiết cách mã của bạn hoạt động.

Kết hợp nhiều yếu tố như đặt tên và các hàm functional để gián tiếp giải thích cách mã của bạn hoạt động, trừ khi gặp phải logic quá phức tạp hoặc cần mô tả ngắn gọn luồng dữ liệu thì hãy để lại comment. Các thay đổi quan trọng có liên kết đến tài liệu bên ngoài...

Ví dụ:


# 02/09/2023: Thay đổi logic theo tài liệu DOC-2945
function getRandomUser() {
...

Ngoài ra, còn nhiều trường hợp khác để bạn khéo léo vận dụng cách comment ví dụ như sử dụng một tính năng nào đó của thư viện ngoài.

# Limits Environment variables
# https://developers.cloudflare.com/workers/platform/limits/#environment-variables
function setEnv() {
...

Như trong ví dụ trên, tôi đang sử dụng một tính năng biến môi trường của Cloudflare và cần tuân thủ một số quy tắc liên quan đến giới hạn, việc đính kèm tài liệu sẽ giúp cho đồng nghiệp dễ dàng truy cứu lại tài liệu và hiểu được cách tôi viết mã hơn.

Tài liệu hóa mã mà bạn viết

Dĩ nhiên một chương trình phức tạp không thể nào chỉ dựa vào mỗi việc đọc mã thôi mà có thể hiểu hết được. Bên cạnh đó, chúng ta cần có một hệ thống tài liệu mô tả hầu hết luồng hoạt động của chương trình. Nó vừa giúp cho người mới có tài liệu để nghiên cứu và giúp cho bản thân phát huy khả năng hệ thống hoá.

Hãy rèn luyện cho mình kỹ năng viết và truyền đạt lại những thứ mình làm. Đây cũng là một trong những kỹ năng cần thiết để giúp bạn tiến xa hơn trên con đường sự nghiệp.

Cuối cùng, vẫn là không ngừng học hỏi

Học, học nữa, học mãi... Học không bao giờ là đủ và cũng không bao giờ thừa. Điều tôi nói trên đây chỉ là quan điểm cá nhân, ngoài ra tôi biết còn nhiều điều cần phải học hỏi thêm từ người khác. Bạn có thể học hỏi thêm từ đồng nghiệp hoặc thông qua các dự án mã nguồn mở.

Tổng kết

Trên đây là một số các quy tắc mà tôi vẫn đang áp dụng trong công việc hàng ngày, tuy không đánh giá được mức độ tốt đến đâu nhưng ít nhất sau vài ba tuần đọc lại mã của mình viết ra, tôi vẫn có thể hiểu được chúng :D. Còn bạn, bạn có đang áp dụng thêm phương pháp nào để giúp cho mã dễ đọc hơn không? Hãy để lại bình luận nhé!

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Trịnh Cường1 năm trước
hay lắm bạn. nhưng mà nếu cứ chia nhỏ logic ra thì code phình rất to và cồng kềnh, tuy dễ đọc hơn thật nhưng mà lúc view sẽ bị phân tán các function, gây hơi mất tập trung khi đọc code, vì cứ phải vào hàm chia nhỏ xem, xong lại quay về hàm chính xem có gì, cứ thế cứ thế
Trả lời
Avatar
Xuân Hoài Tống1 năm trước
Bạn Cường có thắc mắc mà trước kia mình cũng từng bị, việc chia nhỏ các hàm và tổ chức sao cho hợp lý thì lại phụ thuộc nhiều vào cách tổ chức mã của bạn. Ví dụ mình thường viết các hàm functional trong một tệp riêng biệt bên cạnh các tệp xử lý logic chính. Nếu đặt tên cho hàm đủ tốt, bạn sẽ biết chính xác vị trí hàm cần sửa ở đâu mà không cần phải xem xét từng functional một. Việc các hàm không có Side-Effect, tập trung vào làm nhiệm vụ đúng như tên của nó cũng góp phần giúp mã của bạn dễ đọc hơn.
Bấm hoặc cuộn mạnh để sang bài mới