36 điểm bởi GN⁺ 2025-02-17 | 6 bình luận | Chia sẻ qua WhatsApp
  • Phân tích mang tính phản biện về 10 quy tắc phát triển phần mềm của NASA
    • Các quy tắc này được thiết kế cho những hệ thống nhúng cực kỳ quan trọng (ví dụ: phần mềm tàu vũ trụ)
    • Tuy nhiên, cần thảo luận xem các quy tắc này có phù hợp trong những môi trường phát triển khác hay không, hoặc liệu chúng có áp dụng được cho các ngôn ngữ khác (không phải C) hay không

1. Duy trì luồng điều khiển đơn giản (goto, setjmp/longjmp, cấm đệ quy)

  • Quy tắc này cấm xử lý ngoại lệ (setjmp()/longjmp()) và đệ quy.
  • Đệ quy không nhất thiết là không hiệu quả. Nếu dùng phương pháp phù hợp, đệ quy vẫn có thể bảo đảm kết thúc.
  • Việc ép buộc chuyển đệ quy thành vòng lặp có thể tạo ra mã khó bảo trì.

Phê bình:

  • Bảo đảm kết thúc là quan trọng, nhưng các hạn chế cực đoan có thể làm giảm tính dễ đọc và khả năng bảo trì.
  • Việc cấm đệ quy một cách tuyệt đối rất dễ dẫn đến sự phức tạp không cần thiết.

2. Mọi vòng lặp phải có giới hạn trên rõ ràng

  • Trình biên dịch phải có khả năng phân tích tĩnh số lần lặp của vòng lặp.
  • Tuy nhiên, chỉ đặt giới hạn trên thôi thì vẫn khó bảo đảm thời gian thực thi thực tế.
  • Đặt giới hạn độ sâu đệ quy có thể an toàn tương đương với việc đặt giới hạn trên cho vòng lặp.

Phê bình:

  • Chỉ đặt giới hạn trên không thể bảo đảm thời gian chạy khả thi trong thực tế.
  • Ngay cả khi có giới hạn trên, nếu giá trị đó quá lớn thì trên thực tế cũng không khác mấy so với vòng lặp vô hạn.

3. Cấm cấp phát bộ nhớ động sau khi khởi tạo

  • Trong hệ thống nhúng, bộ nhớ bị giới hạn nên mục tiêu là ngăn sự cố do cạn bộ nhớ.
  • Tuy nhiên, cấp phát động có thể dự đoán được đôi khi còn an toàn hơn quản lý bộ nhớ thủ công.
  • Ví dụ, dùng garbage collector thời gian thực (RTGC) có thể khiến cấp phát động trở nên có thể dự đoán.

Phê bình:

  • Thay vì cấm hẳn cấp phát động, có thể tốt hơn nếu phân tích mẫu sử dụng bộ nhớ để bảo đảm an toàn.
  • Có thể tận dụng các công cụ phân tích tĩnh hiện đại (như SPlint) để phát hiện sớm lỗi liên quan đến bộ nhớ động.

4. Giới hạn kích thước hàm trong một trang A4 (khoảng 60 dòng)

  • Lập luận là hàm quá dài sẽ làm giảm khả năng đọc hiểu.
  • Nhưng trong môi trường phát triển hiện đại, có tính năng code folding nên kích thước theo đơn vị logic quan trọng hơn độ dài của hàm.

Phê bình:

  • Nên lấy độ phức tạp logic làm tiêu chí thay vì kích thước vật lý (số dòng).
  • Việc chia nhỏ hàm không nên trở thành mục tiêu tự thân → ngược lại còn có thể làm việc bảo trì khó hơn.

5. Mỗi hàm phải có ít nhất hai câu lệnh assert

  • assert rất hữu ích cho việc gỡ lỗi và tài liệu hóa.
  • Tuy nhiên, áp đặt số lượng cứng nhắc có thể kém hiệu quả.

Phê bình:

  • Điều quan trọng không phải là số lượng assert, mà là xác định rõ những vị trí cần kiểm tra tính hợp lệ của dữ liệu.
  • Việc kiểm tra mọi tham số và mọi đầu vào bên ngoài thực tế hơn.

6. Giảm phạm vi của đối tượng dữ liệu xuống mức tối thiểu

  • Đây là nguyên tắc tốt, khuyến khích dùng biến cục bộ.
  • Tuy nhiên, không chỉ phạm vi của hàm mà cả phạm vi của kiểu và của hàm cũng nên được thu hẹp tối đa.

Phê bình:

  • Trong Ada, Pascal, JavaScript và các ngôn ngữ hàm, kiểu và hàm cũng có thể được khai báo cục bộ → cách tiếp cận này tốt hơn cả quy tắc của NASA.

7. Bắt buộc kiểm tra tính hợp lệ của giá trị trả về và tham số hàm

  • Giá trị trả về phải luôn được kiểm tra.
  • Tuy nhiên, việc kiểm tra mọi trường hợp là rất khó trong thực tế.

Phê bình:

  • Để ngăn lỗi khi chạy, cần càng nhiều kiểm tra càng tốt, nhưng cũng phải cân nhắc giới hạn thực tiễn.
  • Đặc biệt trong C, việc kiểm tra giá trị trả về là quan trọng, nhưng trong các ngôn ngữ hiện đại (Java, Rust, v.v.) có thể xử lý an toàn hơn nhờ hệ thống kiểu.

8. Hạn chế sử dụng preprocessor (chỉ cho phép include header và macro đơn giản)

  • Cấm macro phức tạp, token pasting, macro biến số đối số (...).
  • Tuy nhiên, macro biến số đối số có thể hữu ích cho công cụ gỡ lỗi.

Phê bình:

  • Thay vì chỉ hạn chế dùng preprocessor, nên khuyến khích phong cách macro dễ đọc.
  • Nếu chặn biên dịch có điều kiện như #ifdef, việc viết mã độc lập với nền tảng có thể trở nên khó khăn.

9. Hạn chế sử dụng con trỏ (cấm con trỏ kép, cấm con trỏ hàm)

  • Cấm dùng con trỏ hàm → mục tiêu là độ ổn định cao.
  • Tuy nhiên, con trỏ hàm là thiết yếu cho callback, strategy pattern, trình điều khiển thiết bị, v.v.

Phê bình:

  • Nếu không có con trỏ hàm mà buộc phải chọn hàm bằng switch-case, mã sẽ kém dễ đọc và khó bảo trì hơn.
  • Trong phát triển hệ điều hành, network stack và driver, con trỏ hàm là bắt buộc.
  • Thay vì hạn chế con trỏ, các cách bảo đảm sử dụng con trỏ an toàn hơn (như smart pointer của C++, Rust, v.v.) là lời giải tốt hơn.

10. Với mọi mã nguồn, đặt cảnh báo trình biên dịch ở mức tối đa và dùng công cụ phân tích tĩnh

  • Đây là một khuyến nghị rất tốt.
  • Loại bỏ cảnh báo trình biên dịch + dùng công cụ phân tích tĩnh = tăng độ ổn định.

Phê bình:

  • Một số quy tắc khác của NASA (ví dụ: cấm con trỏ, giới hạn kích thước hàm) thực chất nhằm khắc phục giới hạn của công cụ phân tích tĩnh.
  • Tuy nhiên, công cụ phân tích tĩnh hiện đại đã tiến bộ rất nhiều, nên thay vì áp đặt hạn chế quá mức, việc tận dụng các kỹ thuật phân tích tinh vi hơn sẽ hữu ích hơn.

6 bình luận

 
regentag 2025-02-18

Nhìn hoàn toàn từ góc độ thời gian thực và hệ thống nhúng thì đây đều là những quy tắc dễ hiểu và cần thiết. Liệu trình phân tích tĩnh có thể thay thế các quy tắc này không?

Ví dụ, nếu cho phép cấp phát động, liệu có thể đảm bảo rằng việc cấp phát bộ nhớ sẽ thành công trong mọi kịch bản sử dụng không?

Khi học về kiểm thử phần mềm, luôn có những mệnh đề được nhắc đến ngay từ buổi đầu tiên. Một trong số đó là "không thể có kiểm thử hoàn hảo".

 
smboy86 2025-02-18

Có vẻ phần phản đối lại thu hút mắt tôi hơn
nên chắc đây là những quy tắc không hợp với tôi rồi haha

 
rtyu1120 2025-02-17

Có vẻ không chỉ NASA mà trong các ngành gắn trực tiếp với tính mạng như hàng không/ô tô cũng thường áp dụng những quy tắc lập trình tương tự vậy haha

 
ssssut 2025-02-17

https://github.com/kubernetes/kubernetes/…
Nhìn vào mã nguồn Kubernetes, tôi chợt nhớ đến đoạn mã theo kiểu "space shuttle style" được cho là viết theo cách xây dựng mã nguồn ứng dụng của tàu con thoi NASA.
Thread HN liên quan: https://news.ycombinator.com/item?id=18772873

 
GN⁺ 2025-02-17
Ý kiến trên Hacker News
  • Nếu đọc bài gốc thì sẽ thấy mục đích của từng mục đều được giải thích
  • Bài gốc chủ yếu nhắm đến ngôn ngữ C và dường như muốn tối ưu để có thể kiểm tra độ tin cậy của các ứng dụng quan trọng viết bằng C một cách kỹ lưỡng hơn
  • Tác giả gốc rõ ràng hiểu chính xác mình đang làm gì và giải thích nhiều phương pháp để kiểm chứng mã C
  • Toàn bộ lập luận trong bài gốc đều hoàn toàn dễ hiểu
    • Có lẽ là vì tôi đã học C trên các hệ thống nhỏ
    • Tôi đã học C cho phần cứng dùng trong thiết bị y tế cấy ghép, và trong phòng thí nghiệm chúng tôi cũng tuân theo các hướng dẫn tương tự
  • Đoạn cuối rất hay
    • Ban đầu các quy tắc có thể tạo cảm giác gò bó, nhưng cần cân nhắc những trường hợp mà tính đúng đắn của mã có thể liên quan trực tiếp đến sinh mạng
    • Giống như dây an toàn trên ô tô, lúc đầu có thể thấy bất tiện nhưng theo thời gian bạn sẽ dùng nó một cách tự nhiên
  • Sự phê bình của tôi đối với các quy tắc này sẽ rất khác với OP
    • Ngay từ đầu tôi đã khó có thể nghiêm túc với một bài viết bênh vực setjmp/longjmp
    • Với bất kỳ ai từng tiếp cận mẫu này, vấn đề của nó là quá rõ ràng
    • Bài viết cho rằng setjmp/longjmp là xử lý ngoại lệ
    • Và cho rằng xử lý ngoại lệ là tốt
    • Tôi thấy tiền đề thứ hai có vấn đề rất nghiêm trọng
  • Ý ở đây là phải đặt số lần lặp tối đa cho vòng lặp
    • 10^90 thì ngớ ngẩn và không liên quan
    • Từ điểm đó trở đi tôi không đọc tiếp bài nữa
  • Nếu phê bình các quy tắc thì tôi sẽ tập trung vào những điểm sau
    • Độ dài phần thân hàm không có tương quan với sự đơn giản trong việc hiểu, thậm chí có thể ngược lại với điều mà quy tắc này hàm ý
    • Con số 2 assertion là hoàn toàn tùy tiện; mọi điều có thể khẳng định thì nên được khẳng định
    • Những người dùng Ada, Pascal (Delphi), JavaScript, hoặc các ngôn ngữ hàm nên khai báo kiểu và hàm càng cục bộ càng tốt
  • Cách tiếp cận cá nhân của tôi trong JavaScript là không định nghĩa hàm theo kiểu lồng nhau, trừ khi tôi thực sự muốn capture giá trị một cách tường minh
    • Có thể là do mô hình tư duy cũ
    • Trong quá trình profiling hiệu năng, trước đây tôi từng thấy nó bị định nghĩa lại mỗi khi hàm được gọi
    • Tôi không nghĩ các trình thông dịch JavaScript hiện đại còn hoạt động như vậy
    • Chắc hẳn đã có những tối ưu hóa sâu kể từ khi arrow function được giới thiệu
    • Thói quen cũ không dễ mất đi
    • Các hàm có tên mà không capture biến cục bộ thì tôi giữ ở phạm vi file/module
  • Nhiều ghi chú khác cũng thú vị và rất chi tiết
    • Theo kiểu “chính xác về mặt kỹ thuật là kiểu chính xác tốt nhất” mà các kỹ sư kỳ cựu ưa thích
    • Tôi nghĩ tông giọng thận trọng chung mà các quy tắc của NASA muốn truyền tải là rất tốt, và tôi ủng hộ phần lớn trong số đó
  • Xét theo ngữ cảnh, đây giống các thực hành được đề xuất hơn là “quy tắc”
    • Những “quy tắc” chính thức nằm trong các tài liệu có tên như “NPR”
    • Lập trình viên không có nghĩa vụ phải tuân theo hay bỏ qua các “quy tắc” này
  • GCC có thể cho biết mức sử dụng stack và quan hệ caller-callee sau khi biên dịch
    • setjmp()longjmp() là cách tệ để xử lý ngoại lệ
    • Mã dọn dẹp sẽ không được chạy
    • Nếu theo tinh thần của các quy tắc này thì lẽ ra không nên có tài nguyên nào cần dọn dẹp
  • Vấn đề chính sẽ biểu hiện khác nhau ở từng ứng dụng
    • Phải làm gì khi vượt quá giới hạn lặp, hoặc khi các tài nguyên cố định được cấp phát lúc khởi động không đủ
  • Không rõ vì sao ngày nay, khi lập trình viên đọc mã trên màn hình, kích thước giấy lại không còn liên quan
    • Đã có sự nhắc lại về trang giấy tiêu chuẩn và cỡ chữ
    • Không chỉ vì giới hạn của giấy mà còn vì giới hạn của con người
  • Quy tắc về đệ quy nhằm bảo đảm một giới hạn có thể biết tĩnh cho không gian stack cần thiết
    • Phê bình rằng điều này phụ thuộc vào compiler là đúng, nhưng đó là điều kiện tiên quyết để suy ra giới hạn trên của thời gian chạy
    • Trong các hệ thống an toàn nghiêm ngặt cần có thời gian phản hồi được bảo đảm
  • Tiêu đề nên thể hiện rằng đây là một bài phê bình các quy tắc
  • Khuyến khích sử dụng kiểu nghiêm ngặt
    • Sử dụng kiểu nghiêm ngặt cho mọi kiểu scalar
    • Không trộn lẫn đơn vị đo hệ Anh với hệ mét
 
roxie 2025-02-21

> Tiêu đề phải cho thấy đây là một lời phê bình đối với các quy tắc đó

222