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

Threads
  • Có bạn nào trong lúc code mà bị "ngẫn tò te" hoặc "lú lẫn" trong mớ hỗn độn của mình viết ra chưa. Những lúc như thế tôi thường hay ngừng lại, thư giãn đầu óc, hoặc để mai tính tiếp. Ấy vậy mà rất hiệu quả nhé! Lúc sau xem lại cái tự nhiên thấy vấn đề ở đâu ngay.

    Hoặc cũng có thể thử áp dụng phương pháp "chú vịt vàng" - thử giải thích vấn đề đang gặp phải với vịt xem, nó sẽ cho thấy mâu thuẫn trong lời nói ra.

    Còn không có tiền mua vịt to thì xe túi mù ra vịt bé giống mình nè 😅

    » Xem thêm
  • Ơ buồn cười thật. Ai cũng biết GIF là định dạng ảnh động thường được dùng thay cho video clip để hiển thị các nội dung ngắn trên nền tảng web. GIF tiện hơn các nội dung dạng video là vì nó được hiển thị như một bức ảnh và được hỗ trợ rộng rãi. Cơ mà GIF có dung lượng nặng quá.

    Nói thật nhiều lúc mình có vài cái hành động muốn hiển thị lên web cho mọi người xem, cơ mà định dạng GIF nó nặng với cả cũng không biết cách tối ưu cho nhẹ xuống. Hôm nay lên mạng tìm hiểu xem định dạng nào có khả năng thay thế GIF trong tương lai thì mọi người biết đó là gì không? Là WEBP (webp)!!! Đúng vậy, là định dạng ảnh mà mình đang dùng trên blog lâu nay luôn á, mà giờ mới biết là nó hiển thị được cả ảnh động nữa, hơi quê 😆

    Kết hợp với ffmpeg nữa là chuyển được tất tần tật video clip thành webp được ngay. Để vài nửa ngồi chế lại cái cli một tí là dùng ngon luôn mọi người ạ 🤪

    » Xem thêm
  • Cảm giác như Github Copilot đang cố gắng mở rộng thị trường cho anh em developer á. Mới trước họ ra mắt Github Open Copilot Chat thì mới đây lại thêm cái Using GitHub Copilot in the command line dùng để giải thích hoặc gợi ý lệnh trong terminal.

    Đây, cách dùng rất đơn giản thôi, ví dụ muốn nó giải thích câu lệnh sudo apt-get để làm gì, thì:

    $ gh copilot explain "sudo apt-get"

    Hoặc nhờ nó gợi ý lệnh mong muốn, sử dụng tiếng Việt được luôn nhé (kể cả tiếng Việt không dấu vẫn hiểu 😳)

    $ gh copilot suggest "xoá commit chưa push"

    Mình đã kiểm tra và thấy lệnh ra rất đúng, xịn thật 🤓

    » 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é!

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.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

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.