22 điểm bởi GN⁺ 2024-09-04 | 5 bình luận | Chia sẻ qua WhatsApp
  • Khi bảo trì một codebase xa lạ, ta tốn rất nhiều thời gian để tìm kiếm chuỗi
  • Ngay cả trong dự án do chính mình viết, vẫn phải tìm nhiều thứ như tên hàm, thông báo lỗi, tên lớp
  • Nếu khả năng tìm kiếm kém, bạn có thể không tìm thấy tham chiếu trong codebase và có nguy cơ coi chúng là không cần thiết
  • Từ tình huống đó, tác giả rút ra một số quy tắc có thể áp dụng để duy trì Greppability của codebase

Đừng chia tách định danh

  • Việc chia tách định danh hoặc xây dựng chúng động là một ý tưởng không hay
  • Giả sử có hai bảng cơ sở dữ liệu là shipping_addressesbilling_addresses, việc xây dựng tên bảng một cách động theo loại đơn hàng có thể trông hợp lý
const getTableName = (addressType: 'shipping' | 'billing') => {  
    return `${addressType}_addresses`  
}  
  • Cách này có vẻ DRY nhưng không tốt cho bảo trì. Khi ai đó tìm kiếm tên bảng shipping_addresses trong codebase, họ có thể bỏ sót đoạn này
  • Hardcode định danh là cách tốt hơn
  • Đoạn mã đã được refactor để tăng khả năng tìm kiếm:
const getTableName = (addressType: 'shipping' | 'billing') => {  
    if (addressType === 'shipping') {  
        return 'shipping_addresses'  
    }  
    if (addressType === 'billing') {  
        return 'billing_addresses'  
    }  
    throw new TypeError('addressType must be billing or shipping')  
}  
  • Điều này cũng áp dụng tương tự cho tên cột, trường đối tượng, tên phương thức/hàm (trong JavaScript, việc xây dựng tên phương thức động là rất dễ)

Dùng cùng một tên xuyên suốt toàn bộ stack

  • Đừng đổi tên trường ở ranh giới ứng dụng chỉ để khớp với một quy ước đặt tên nào đó
  • Một ví dụ điển hình là lấy định danh kiểu snake_case của PostgreSQL vào JavaScript rồi chuyển thành camelCase, điều này không tốt
  • Nó làm việc tìm kiếm trở nên khó hơn. Để tìm mọi chỗ, bạn phải tìm hai chuỗi thay vì một
const getAddress = async (id: string) => {  
    const address = await getAddressById(id)  
    return {  
        streetName: address.street_name,  
        zipCode: address.zip_code,  
    }  
}  
  • Tốt hơn là trả về trực tiếp đối tượng đó
const getAddress = async (id: string) => {  
    return await getAddressById(id)  
}  

Phẳng thường tốt hơn lồng sâu

  • Lấy cảm hứng từ Zen của Python, khi xử lý namespace thì trong đa số trường hợp, cấu trúc phẳng sẽ tốt hơn việc lồng nhiều tầng trong thư mục/đối tượng
  • Nếu có hai cách tổ chức cho tệp cấu hình bản dịch:
{  
    "auth": {  
        "login": {  
            "title": "Login",  
            "emailLabel": "Email",  
            "passwordLabel": "Password",  
        },  
        "register": {  
            "title": "Register",  
            "emailLabel": "Email",  
            "passwordLabel": "Password",  
        }  
    }  
}  
{  
    "auth.login.title": "Login",  
    "auth.login.emailLabel": "Email",   
    "auth.login.passwordLabel": "Password",  
    "auth.register.title": "Login",  
    "auth.register.emailLabel": "Email",  
    "auth.register.passwordLabel": "Password",  
}  
  • Nên chọn phương án thứ hai. Bạn có thể dễ dàng tìm khóa và tham chiếu chúng như t('auth.login.title')
  • Khi xét đến cấu trúc component React:
./components/AttributeFilterCombobox.tsx  
./components/AttributeFilterDialog.tsx  
./components/AttributeFilterRating.tsx  
./components/AttributeFilterSelect.tsx  
  • Cách này được ưu tiên hơn cấu trúc sau
./components/attribute/filter/Combobox.tsx  
./components/attribute/filter/Dialog.tsx  
./components/attribute/filter/Rating.tsx  
./components/attribute/filter/Select.tsx  
  • Xét từ góc độ tìm kiếm, bạn có thể tìm toàn bộ component có kèm namespace như AttributeFilterCombobox thay vì những tên quá chung chung như Dialog

Ý kiến của GN⁺

  • Bài viết blog này giải thích rất rõ tầm quan trọng của việc tìm kiếm định danh khi bảo trì codebase
  • Việc xây dựng định danh động hoặc đổi tên ở ranh giới ứng dụng khiến việc bảo trì mã trở nên khó khăn hơn. Định danh cần nhất quán và rõ ràng
  • Thay vào đó, hardcode định danh và giữ namespace phẳng sẽ tốt hơn về mặt tìm kiếm
  • Nên áp dụng các nguyên tắc này vào dự án để tăng tính dễ đọc và khả năng bảo trì của mã
  • Ngoài các quy tắc mà tác giả đề xuất, vẫn còn nhiều cách khác để nâng cao chất lượng mã như viết Self-Documenting Code, dùng chú thích có ý nghĩa, v.v.

5 bình luận

 
nowpark 2024-09-06

Hãy chuyển thành full path của JSON, mình cũng để lại một công cụ giúp biến nó thành dạng dễ grep!

https://vi.news.hada.io/topic?id=3159

 
botplaysdice 2024-09-05

Hay đấy... greppability cơ à...

 
ahwjdekf 2024-09-04

Có vẻ như việc cố gắng ghi thông tin hữu ích vào cùng một dòng càng nhiều càng tốt cũng khá hữu ích.

 
roxie 2024-09-09

Hay đấy

 
GN⁺ 2024-09-04
Ý kiến Hacker News
  • Việc tìm kiếm các ký hiệu như tên hàm và tên lớp kém hiệu quả hơn so với việc dùng công cụ hiểu được cú pháp của mã

    • Chỉ riêng các tính năng "đi tới định nghĩa" và "tìm nơi sử dụng" cũng có thể làm giảm đáng kể nhu cầu tìm kiếm văn bản
    • Trong 10 năm qua, hầu như chỉ tìm kiếm các chuỗi hiển thị cho người dùng
    • Những bài đăng như thế này cho thấy tác giả nên đầu tư thời gian để học các công cụ tốt hơn phù hợp với ngôn ngữ của mình
    • Chỉ cần một IDE tốt cũng có thể tiết kiệm rất nhiều thời gian
  • Sẽ rất hữu ích nếu công cụ grep có chế độ "siêu không phân biệt hoa thường"

    • Ví dụ, khi tìm kiếm "FooBar|first_name", nó sẽ được mở rộng để khớp với mọi kiểu viết hoa thường
    • Khó mà nghĩ ra tình huống tính năng này không nên là mặc định
  • Ủng hộ tính grep được

    • Trong tiếng Thụy Điển thực sự có những từ như "grep-bar" hoặc "grep-barhet"
    • "greppbar" có nghĩa là "có thể hiểu được", còn "greppbarhet" có nghĩa là "khả năng có thể hiểu được"
  • Khi thiết kế Hamilton, mục tiêu là giúp dễ grep các định nghĩa hàm và các phần sử dụng phụ thuộc vào chúng

    • Trong thế giới biến đổi dữ liệu bằng Python, rất dễ tạo ra các codebase mà grep không giúp ích được nhiều
  • 'greppable' không phải là một từ/khái niệm thường được dùng riêng lẻ

    • Đã dùng nó làm nguyên tắc tổ chức trong thời gian dài
    • Đây là một trong những cách tốt nhất để cấu trúc mã
  • Từng thấy một ví dụ phức tạp dùng nội suy chuỗi có điều kiện

    • Khi mới tham gia dự án, đã mất quá nhiều thời gian để tìm ba từ nhìn thấy trên UI
    • Sau đó đã đổi toàn bộ thành các chuỗi có thể grep dễ dàng trong đoạn mã này
  • Nhiều phong cách lập trình và công cụ giữ hằng chuỗi trên một dòng bất kể độ dài dòng

    • Mục đích là để khi thấy chuỗi trong đầu ra của chương trình, có thể tìm đúng chuỗi đó trong mã
  • Rust, Javascript, Lisp đặt từ khóa trước định nghĩa hàm nên dễ tìm kiếm

    • C không có các từ khóa như vậy nên chỉ có thể tìm theo tên hàm
    • Một số quy tắc lập trình C tách định nghĩa thành hai dòng để việc tìm kiếm dễ hơn
  • Đồng ý với khả năng tìm kiếm trong mã, nhưng phản đối việc giữ nguyên cùng một tên khi đi qua các ranh giới

    • Mỗi ký hiệu chỉ nên tồn tại trong một miền để giảm tải nhận thức
  • Khả năng tìm kiếm trong mã là tốt, nhưng ví dụ này cố tình làm tăng khả năng phát sinh lỗi

    • Thêm điều kiện vào chuỗi sẽ tạo ra khả năng không khớp giữa đầu vào và đầu ra
    • Làm phẳng dictionary sẽ làm tăng khả năng phát sinh lỗi gõ sai
    • Lỗi gõ sai xảy ra rất thường xuyên, và nếu bị sao chép sang nhiều codebase thì sẽ khó xử lý