5 quy tắc lập trình của Rob Pike (1989)
(cs.unc.edu)- Quy tắc 1: Không thể dự đoán chương trình sẽ tiêu tốn thời gian ở đâu. Nút thắt cổ chai xuất hiện ở những chỗ không ngờ tới, vì vậy đừng cố cải thiện tốc độ cho đến khi thực sự chứng minh được đó là nút thắt cổ chai
- Quy tắc 2: Đo đạc là ưu tiên trước hết. Chỉ tinh chỉnh hiệu năng sau khi đã đo đạc, và chỉ cân nhắc tối ưu hóa khi một phần của mã thực sự áp đảo toàn bộ hệ thống
- Quy tắc 3: Thuật toán phức tạp thì chậm với n nhỏ. Thuật toán phức tạp có hằng số lớn, nên nếu n không thường xuyên tăng lớn thì hãy dùng cách đơn giản. Ngay cả khi n có lớn lên, trước tiên vẫn phải áp dụng quy tắc 2
- Quy tắc 4: Thuật toán phức tạp có nhiều lỗi và khó triển khai. Nên ưu tiên dùng thuật toán đơn giản và cấu trúc dữ liệu đơn giản
- Quy tắc 5: Dữ liệu là cốt lõi. Nếu chọn đúng cấu trúc dữ liệu và tổ chức chúng tốt, thì thuật toán gần như sẽ tự lộ ra một cách hiển nhiên. Trọng tâm của lập trình không phải là thuật toán mà là cấu trúc dữ liệu
Triết lý và trích dẫn liên quan
- Quy tắc 1 và 2 mang cùng ý nghĩa với câu châm ngôn của Tony Hoare: “tối ưu hóa sớm là cội nguồn của mọi điều xấu”
- Ken Thompson diễn giải lại quy tắc 3 và 4 thành “khi còn nghi ngờ, hãy dùng cách đơn giản (Brute Force)”
- Quy tắc 3 và 4 là ví dụ cho triết lý thiết kế KISS(Keep It Simple, Stupid)
- Quy tắc 5 là điều Fred Brooks đã nhắc tới trong 『The Mythical Man-Month』,
thường được tóm gọn thành “viết mã đơn giản bằng cách dùng các đối tượng thông minh”
1 bình luận
Ý kiến trên Hacker News
Làm tôi nhớ đến bài nói chuyện của Jonathan Blow
Ông ấy tiếp cận vấn đề từ góc độ năng suất. Ở giai đoạn đầu phát triển Braid, gần như mọi thứ đều được triển khai bằng các mảng đơn giản, và chỉ chỉnh sửa khi xuất hiện điểm nghẽn
Câu nói “quan trọng hơn tốc độ hay bộ nhớ là thời gian đời người cần để hiện thực một chương trình” thật sự rất ấn tượng
Nếu thật sự tiếp nhận Rule 1 một cách nghiêm túc, thì Rule 3~5 sẽ tự nhiên kéo theo
Khi thừa nhận rằng không thể dự đoán điểm nghẽn, chiến lược hợp lý duy nhất là viết mã đơn giản và đo đạc
Trên thực tế, thứ thất bại thường xuyên nhất không phải tối ưu hóa sớm mà là trừu tượng hóa sớm. Người ta tạo ra những tầng phức tạp để có sự linh hoạt chưa cần thiết, và điều đó lại làm tăng chi phí bảo trì
Tôi từng có trải nghiệm vào lúc 2 giờ sáng trong thập niên 90 khi phải triển khai tính năng tìm kiếm tập dữ liệu
Vì quá mệt nên tôi cứ làm bằng tìm kiếm tuyến tính trước rồi tính sửa sau, nhưng thực tế trong một bài test kéo dài 4 tiếng thì chênh lệch chỉ có 6 giây
Cuối cùng tôi vẫn sửa, nhưng không có khác biệt đáng kể
Tôi hoàn toàn đồng cảm với Rule 5
Càng là bài toán khó thì càng được giải quyết bằng việc lặp đi lặp lại để cải thiện cấu trúc dữ liệu và API. Khi cấu trúc đã vững, luồng điều khiển sẽ trở nên tự nhiên
LLM khá yếu trong kiểu tư duy cấu trúc này. Chúng đề xuất luồng điều khiển phức tạp khá tốt, nhưng lại không giỏi thiết kế cấu trúc dữ liệu có thể kết hợp
Tôi xuất thân từ ngành điện tử (E.E.), và nhờ Rule 3 mà đầu sự nghiệp không gặp vấn đề lớn
Nhưng về sau khi chuyển sang các hệ thống quy mô lớn hơn, n tăng lên và độ phức tạp thật sự trở nên quan trọng
“Big iron” ở thời Rob Pike nói đến khá giống với môi trường nhúng ngày nay
Nếu hai thuật toán có độ khó triển khai tương đương nhau, tôi sẽ chọn cái nhanh hơn với đầu vào lớn
Bài liên quan: Less Than Quadratic
Không ít lần người ta chọn cách O(n²) quá đơn giản rồi trải nghiệm cảnh nổ tung khi vận hành
Tôi nghĩ các quy tắc của Pike tốt hơn những châm ngôn cũ
Những câu như “tối ưu hóa sớm là cội nguồn của mọi điều xấu” rất dễ bị hiểu sai khi mất đi ngữ cảnh
Phiên bản của Pike thì rõ ràng và khó bị lạm dụng hơn
Từng có cách diễn đạt cũ rằng “Rule 5 có thể được tóm lại là ‘hãy để mã ngốc dùng các đối tượng thông minh’”,
nhưng trong thời đại hướng đối tượng, nó lại bị biến chất thành một niềm tin sai lầm che giấu độ phức tạp
Sau hơn 10 năm vận hành cùng một codebase, tôi đã hoàn toàn thấm nhuần các quy tắc của Pike
Việc giữ vững nguyên tắc KISS/DRY và duy trì công nghệ đã được kiểm chứng thay vì chạy theo mốt đã cho thấy sự ổn định dài hạn
Trên thực tế, tài liệu nguyên tắc phát triển của đội chúng tôi gần như giống hệt các quy tắc của Pike
Vào đầu những năm 1990, thời kỳ C++, tôi từng thay danh sách liên kết kép bằng việc cấp phát lại mảng đơn giản
Không chỉ bug biến mất mà nó còn nhanh hơn
Tôi đã học được rằng thực hiện các phép toán đắt đỏ ít thường xuyên hơn là một chiến lược tốt
So sánh quy tắc “đừng tinh chỉnh nếu chưa đo đạc” với Latency Numbers Every Programmer Should Know của Jeff Dean thì khá thú vị
Dean nói rằng có thể dự đoán hiệu năng bằng kiến thức có sẵn
Cuối cùng hai lập trường này có thể dung hòa — ở giai đoạn thiết kế thì chọn cấu trúc có cảm giác nhanh, còn sau khi triển khai thì tinh chỉnh dựa trên đo đạc
Rule 1 và 2 chỉ mang tính tuyệt đối khi xử lý những bài toán mới
Khi liên tục xây dựng hệ thống trong cùng một domain, ta có thể dự đoán trước điểm nghẽn
Một lập trình viên giàu kinh nghiệm có thể có cảm giác tương đối chính xác về hiệu năng ngay từ trước khi thiết kế