24 điểm bởi xguru 2020-12-28 | 3 bình luận | Chia sẻ qua WhatsApp

Hướng dẫn mã nguồn mở được cập nhật theo hướng hiện đại trong khi vẫn tuân theo các nguyên tắc Unix truyền thống

  • Triết lý thiết kế CLI

    → con người là ưu tiên hàng đầu

    → những thành phần đơn giản hoạt động cùng nhau

    → duy trì tính nhất quán giữa các chương trình

    → chỉ nói vừa đủ khi cần (đầu ra không quá ít cũng không quá nhiều)

    → giúp dễ khám phá (trợ giúp toàn diện, ví dụ, gợi ý lệnh nên chạy tiếp theo, gợi ý việc cần làm khi có lỗi)

    → như một cuộc hội thoại thông thường

    → mạnh mẽ, bền vững

    → đồng cảm với người dùng

    → hỗn loạn: nếu phải phá vỡ quy tắc, hãy làm rõ ý định và mục đích

  • Hướng dẫn CLI

    → Cơ bản

    ✓ hãy dùng thư viện phân tích cú pháp dòng lệnh: Go(Cobra,cli), Node(oclif), Python (Click,Typer), Ruby(TTY)

    ✓ trả về mã 0 khi thành công, mã khác 0 khi lỗi

    ✓ đầu ra dùng stdout

    ✓ log, lỗi và các thông báo khác dùng stderr

    → Trợ giúp

    ✓ khi chạy không kèm tùy chọn, hãy hiển thị trợ giúp (-h, --help)

    ✓ mặc định chỉ hiển thị phần trợ giúp ngắn gọn

      · chương trình này làm gì
    
      · một hoặc hai ví dụ gọi lệnh
    
      · giải thích các cờ nếu không quá nhiều
    
      · `--help` để xem giải thích thêm
    

    ✓ khi dùng tùy chọn -h, --help, hãy hiển thị đầy đủ trợ giúp

    ✓ cung cấp kênh để nhận phản hồi/vấn đề

    ✓ trong phần trợ giúp, hãy cung cấp liên kết tới tài liệu bản web

    ✓ giải thích bằng ví dụ

    ✓ nếu có nhiều ví dụ, hãy đăng chúng ở nơi khác (cheat sheet hoặc trang web)

    ✓ đừng quá bận tâm đến trang man (ít người dùng, và cũng không chạy trên Windows)

    ✓ nếu phần trợ giúp dài, hãy pipe qua pager

    ✓ hiển thị các cờ và lệnh được dùng nhiều nhất ở đầu phần trợ giúp

    ✓ dùng định dạng trong phần trợ giúp (chữ đậm)

    ✓ nếu người dùng làm sai điều gì đó và bạn có thể đoán được, hãy gợi ý

    ✓ nếu lệnh của bạn mong đợi nhận thứ gì đó qua pipe nhưng stdin là terminal tương tác, hãy hiển thị trợ giúp rồi thoát ngay

    → Đầu ra

    ✓ đầu ra có thể đọc được bởi con người là quan trọng nhất

    ✓ nếu không ảnh hưởng đến khả năng sử dụng, hãy cung cấp đầu ra có thể đọc được bởi máy

    ✓ nếu vì đầu ra cho con người mà đầu ra cho máy trở nên bất khả thi, hãy cung cấp tùy chọn --plain để có thể kết hợp với grep / awk v.v.

    ✓ nếu nhận --json, hãy xuất theo định dạng JSON

    ✓ tốt nhất là không có đầu ra khi thành công, nhưng nếu cần thì hãy ngắn gọn. Hỗ trợ tùy chọn -q để bỏ các đầu ra không cần thiết

    ✓ nếu thay đổi trạng thái, hãy nói cho người dùng biết (tham khảo đầu ra của git push)

    ✓ hãy làm cho trạng thái hiện tại của hệ thống dễ nhìn, dễ hiểu

    ✓ hãy gợi ý các lệnh mà người dùng có thể chạy tiếp (như git status hiển thị git add / restore)

    ✓ các hành vi vượt ra ngoài nội bộ chương trình phải là tường minh. Ví dụ đọc/ghi tệp mà người dùng không chỉ định (cache), hoặc kết nối tới máy chủ từ xa (tải tệp)

    ✓ tăng mật độ thông tin bằng ASCII art

    ✓ dùng màu sắc có chủ đích, đừng lạm dụng

    ✓ tắt màu nếu không phải terminal hoặc khi người dùng yêu cầu

    ✓ nếu stdout không phải terminal tương tác, đừng hiển thị hoạt ảnh

    ✓ chỉ dùng ký hiệu/emoji khi chúng thực sự làm rõ điều gì đó

    ✓ theo mặc định, đừng in ra những thông tin chỉ người tạo ra chương trình mới hiểu

    ✓ đừng dùng stderr như một tệp log (ít nhất đừng đặt vậy làm mặc định; chỉ ở chế độ chi tiết mới in các mức log như ERR, WARN)

    ✓ nếu in ra nhiều văn bản, hãy dùng công cụ phân trang như less

    → Lỗi

    ✓ hãy catch lỗi và viết lại thông điệp theo cách dễ hiểu cho con người

    ✓ tỷ lệ tín hiệu trên nhiễu là quan trọng. Nếu cùng một lỗi xảy ra nhiều lần, hãy gom lại và in kèm tiêu đề giải thích

    ✓ hãy cân nhắc nơi mà người dùng sẽ nhìn vào đầu tiên

    ✓ nếu xảy ra lỗi bất ngờ/không thể giải thích, hãy cung cấp thông tin debug/trace và giải thích cách gửi lỗi này cho nhà phát triển

    ✓ hãy để việc gửi báo cáo lỗi có thể thực hiện mà không cần nỗ lực thêm. (ví dụ tạo URL chứa đầy đủ thông tin để chỉ cần mở trong trình duyệt là gửi được)

    → Argument & Flags : đối số và cờ

    ✓ đối số: tham số theo vị trí. Thứ tự rất quan trọng. cp bar foocp foo bar là khác nhau

    ✓ cờ: tham số có tên. Một ký tự như -r hoặc nhiều ký tự như --recursive. Thứ tự thường không quan trọng.

      cũng có thể bao gồm giá trị người dùng nhập. `--file foo.txt` hoặc `--file=foo.txt`
    

    ✓ hãy ưu tiên cờ hơn đối số. Gõ sẽ nhiều hơn nhưng rõ ràng hơn. Nếu có quá nhiều đối số thì sau này sẽ khó mở rộng tính năng

    ✓ hãy có cả bản ngắn và bản đầy đủ cho cờ. Khi dùng bản đầy đủ trong script thì không cần giải thích thêm

    ✓ chỉ dùng cờ một ký tự cho những cờ được dùng thường xuyên

    ✓ cũng có thể nhận nhiều đối số để phục vụ một thao tác đơn giản

    ✓ nếu cần từ hai đối số khác nhau trở lên thì có thể bạn đang làm gì đó chưa đúng

    ✓ cờ nên dùng tên chuẩn nếu đã có chuẩn phổ biến

      `-a --all`, `-d --debug`, `-f --force`, `-h --help`, `-o --output`, `-p --port`, `-q --quiet`, `-u --user`
    

    ✓ mặc định nên là giá trị phù hợp với đa số người dùng

    ✓ nếu người dùng cung cấp đối số/cờ cần giá trị đầu vào mà chưa có giá trị, hãy yêu cầu họ nhập

    ✓ luôn cung cấp cách truyền giá trị bằng đối số/cờ, đừng bắt buộc phải nhập qua prompt

    ✓ luôn yêu cầu xác nhận trước khi làm việc nguy hiểm

    ✓ nếu đầu vào hoặc đầu ra là tệp, hãy hỗ trợ - để nhận từ stdin hoặc xuất ra stdout

      $ curl https://example.com/something.tar.gz | tar xvf -
    

    ✓ nếu cờ có thể nhận giá trị bổ sung, hãy cho phép các từ đặc biệt như none. ssh -F none

    ✓ nếu có thể, hãy thiết kế đối số, cờ và subcommand để hoạt động không phụ thuộc thứ tự

    ✓ các giá trị đối số nhạy cảm (như mật khẩu) nên có thể được nhập từ tệp

    → Tính tương tác

    ✓ chỉ dùng prompt hoặc tính năng tương tác khi stdin là terminal tương tác

    ✓ nếu truyền --no-input thì không dùng prompt hay bất kỳ tính năng tương tác nào

    ✓ khi nhập mật khẩu, đừng hiển thị giá trị người dùng gõ

    ✓ hãy để người dùng có thể thoát dễ dàng (đừng như vim). Hãy để Ctrl-C hoạt động. Nếu do liên quan tới việc chạy chương trình như ssh, tmux mà không thể dùng Ctrl-C, hãy hiển thị rõ rằng có chuỗi thoát bắt đầu bằng ~ như SSH

    → Subcommand

    ✓ công cụ phức tạp có thể cung cấp subcommand để giảm độ phức tạp

    ✓ ngoài ra, nếu có nhiều công cụ liên quan chặt chẽ, có thể gom chúng vào một lệnh để dùng thuận tiện hơn

    ✓ hãy nhất quán giữa các subcommand. Cùng một cờ thì cùng một ý nghĩa, và định dạng đầu ra tương tự nhau

    ✓ dùng tên nhất quán giữa các tầng subcommand khác nhau

    ✓ đừng thêm những lệnh có tên dễ nhầm hoặc quá giống nhau, như updateupgrade

    → Độ bền vững

    ✓ hãy kiểm tra, xác thực mọi đầu vào của người dùng. Kiểm tra sớm và hiển thị lỗi dễ hiểu

    ✓ khả năng phản hồi quan trọng hơn tốc độ tuyệt đối

    ✓ nếu mất nhiều thời gian, hãy hiển thị tiến độ

    ✓ nếu có thể, hãy xử lý song song. Nhưng hãy suy nghĩ kỹ trước khi làm

    ✓ hãy đặt timeout

    ✓ hãy làm cho chương trình có tính idempotent (chạy lại không làm thay đổi kết quả). Khi có lỗi, người dùng có thể nhấn mũi tên lên trong shell để chạy lại và tiếp tục

    ✓ hãy thiết kế theo kiểu crash-only. Đây là bước tiếp theo của idempotence. Nếu không cần dọn dẹp sau khi làm việc, hoặc có thể hoãn dọn dẹp đến lần chạy sau, chương trình có thể thoát ngay khi thất bại hoặc bị ngắt

    ✓ con người sẽ sử dụng sai chương trình của bạn

    → Khả năng thích ứng tương lai

    ✓ nếu có thể, hãy thay đổi theo hướng bổ sung (additive). Đừng phá tương thích bằng cách đổi hành vi cũ, hãy thêm cờ mới

    ✓ nếu không phải thay đổi theo hướng bổ sung thì hãy cảnh báo trước

    ✓ thay đổi đầu ra dành cho con người phần lớn là chấp nhận được

    ✓ đừng vì có một subcommand mà mọi người hay dùng mà tạo ra catch-all subcommand để tự động chạy nó khi không chỉ định rõ

    ✓ đừng cho phép viết tắt tùy ý cho subcommand

    ✓ đừng tạo ra những “quả bom hẹn giờ” rồi một ngày nào đó sẽ ngừng hoạt động đúng

    → Tín hiệu và ký tự điều khiển

    ✓ khi người dùng nhấn Ctrl-C (tín hiệu INT), hãy dừng càng nhanh càng tốt

    ✓ nếu người dùng nhấn Ctrl-C trong lúc đang dọn dẹp lâu, hãy bỏ qua lần đầu. Nếu nhấn lại thì cho phép buộc thoát

      ^CGracefully stopping... (press Ctrl+C again to force)
    

    → Cấu hình

    ✓ hãy tuân theo đặc tả XDG(X Desktop Group)

    ✓ nếu sửa cấu hình không phải của chương trình bạn, hãy xác nhận với người dùng và nói rõ bạn sẽ làm gì

    ✓ tham số cấu hình nên được áp dụng theo thứ tự ưu tiên

      cờ > biến môi trường shell > cấu hình cấp dự án (`.env`) > cấu hình người dùng > cấu hình hệ thống
    

    → Biến môi trường

    ✓ biến môi trường dùng cho hành vi thay đổi theo ngữ cảnh nơi lệnh được chạy

    ✓ để tối đa tính di động, biến môi trường chỉ nên gồm chữ hoa/số/gạch dưới và không được bắt đầu bằng số

    ✓ nếu có thể, hãy dùng giá trị một dòng (single-line) cho biến môi trường

    ✓ đừng dùng những tên đã được dùng rộng rãi

    ✓ nếu có thể, hãy kiểm tra và sử dụng các biến môi trường phổ biến

      `NO_COLOR`, `DEBUG`, `EDITOR`, `HTTP_PROXY`, `SHELL`, `TERM`, `TERMINFO`, `HOME`, `TMPDIR`, `PAGER`, `LINES` ..
    

    ✓ nếu cần, hãy tải biến môi trường từ .env

    ✓ đừng dùng phần mở rộng .env cho tệp cấu hình

    → Đặt tên

    ✓ tên nên là từ ngắn gọn, dễ nhớ

    ✓ chỉ dùng chữ thường, và chỉ dùng - (dash) khi thực sự cần

    ✓ nếu có thể, hãy ngắn

    ✓ dễ gõ trên bàn phím

    → Phân phối

    ✓ nếu có thể, hãy phân phối dưới dạng một binary duy nhất

    ✓ dễ gỡ cài đặt

    → Phân tích dữ liệu

    ✓ đừng gửi dữ liệu sử dụng công cụ hoặc dữ liệu crash về cho bạn mà không có sự đồng ý của người dùng

3 bình luận

 
jonnung 2021-01-09

Cảm ơn vì nội dung hay.

 
xguru 2020-12-28

Có vẻ như nhờ Rust và Go, vốn rất phù hợp để tạo ra các single binary, nên ngày càng có nhiều công cụ command line hay hơn.

Việc tạo ra chúng cũng đang ngày càng dễ dàng và mạnh mẽ hơn.

 
xguru 2020-12-28

Tôi vừa dịch sơ lược mà cũng học được rất nhiều. Làm xong rồi mới thấy... có lẽ dịch cả repo thì tốt hơn nhỉ. ^^;;