36 điểm bởi GN⁺ 2026-03-19 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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

 
GN⁺ 2026-03-19
Ý 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

    • Game xử lý lặp đi lặp lại vô số đối tượng tương tự nhau ở hơn 60 khung hình mỗi giây, nên cấu trúc dựa trên mảng đơn giản là mặc định hợp lý
    • Trong phát triển game, khung hình phải được render trong 16ms nên bảng kích thước cố định và tìm kiếm tuyến tính là mẫu rất phổ biến. Đây là lựa chọn mang tính cấu trúc để tránh sự khó lường của cấp phát động
    • Góc nhìn này cũng đúng với phát triển phần mềm nói chung ngoài game. Tất cả chúng ta đều làm việc dưới áp lực deadline và giới hạn chi phí, nên thời gian kỹ sư bản thân nó đã là một loại chi phí
    • Tuy vậy, cần cẩn thận khi mở rộng nguyên xi bài học từ game sang lập trình nói chung. Game thiên về mã chuyên dụng hơn là tái sử dụng, còn phần mềm phổ thông thì thiên về dữ liệu
    • Còn tôi thì là kiểu người bắt đầu bằng hash map thay vì mả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ì

    • Trong nhóm, chúng tôi hay trích câu “trừu tượng hóa phải tự nhiên xuất hiện, chứ không phải được thiết kế trước”
    • Trừu tượng hóa sớm làm lãng phí thời gian lập trình viên, tăng nợ kỹ thuật, và làm xác suất phát sinh bug cao hơn. Nó thường xuất hiện dưới danh nghĩa “chuẩn bị cho vấn đề tương lai”
    • Có thể tham khảo bài viết của Philip Wadler như một tài liệu liên quan
    • Tôi coi trọng tính dễ đọc và khả năng bảo trì hơn hiệu năng. Vì vậy với tôi, Rule 4 mới là nền tảng, còn Rule 1 là hệ quả của nó
    • Tôi từng gặp trường hợp tách cấu hình phức tạp quá mức. Cuối cùng chỉ cần 8 file YAML đơn giản là đủ
  • 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ể

    • Với n nhỏ, tìm kiếm tuyến tính thậm chí có thể nhanh hơn
    • Nhưng thuật toán O(n²) có rủi ro “vẫn deploy được, nhưng rồi cuối cùng sẽ nổ tung khi vận hành
  • 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

    • Theo kinh nghiệm của tôi, Rule 5 trên thực tế gần như chính là Rule 1. Có câu rằng “nhìn code thì thấy rối, nhưng chỉ cần xem schema cơ sở dữ liệu là mọi thứ trở nên rõ ràng”
    • Trong các dịch vụ phân tán, Rule 5 thường bị bỏ qua. Thay vì chia nhỏ thành nhiều lần gọi HTTP·DB, một cấu trúc có thể xử lý chỉ bằng một lần gọi sẽ hiệu quả hơn
  • 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

    • Tôi phản đối Rule 3 ở một mức độ nào đó. Với đầu vào nhỏ thì không sao, nhưng với đầu vào lớn thì hiệu năng Big-O rất quan trọng.
      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
    • Cần hiểu nghĩa của “fancy” theo đúng miền bài toán. Nếu n nhỏ thì cách tiếp cận đơn giản tốt hơn, nhưng nếu n lớn thì thuật toán nâng cao là bắt buộc
      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
    • Bố tôi từ thời Fortran đã rất thích dùng lookup table. Đó là một kiểu tối ưu hóa cổ điển nhằm giảm tính toán lặp lại
  • 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

    • Điều quan trọng là phải giữ được mối liên kết của tư duy lịch sử
    • Cụm “clickbait tinh thần của các châm ngôn cổ điển” nghe như một phép ví von dí dỏm
  • 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

    • Mã thực sự nhanh là mã dùng cả hai cách tiếp cận. Ở giai đoạn thiết kế thì cân nhắc hiệu quả bộ nhớ đệm, sau đó loại bỏ điểm nghẽn bằng profiling
    • Các con số về độ trễ chỉ cho biết giới hạn lý thuyết của thuật toán; hiệu năng thực tế còn phụ thuộc vào cách triển khai và môi trường runtime
    • Điều mà ‘tối ưu hóa sớm’ cấm là kiểu tinh chỉnh cục bộ ở mức hack. Còn việc cân nhắc tốc độ trong thiết kế tổng thể là điều hiển nhiên
  • 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ế

    • Bản thân tôi cũng dự đoán khá chính xác điểm nghẽn trong đa số trường hợp. Tôi cũng nhiều lần thay đổi cách tiếp cận nhờ test hiệu năng trước
    • Nhưng theo kinh nghiệm 30 năm của tôi, cảm giác rằng sẽ có điểm nghẽn thì thường đúng, còn vị trí hay thời điểm chính xác thì không thể dự đoán
    • Cũng từng có câu đùa kiểu “Rob Pike thì biết gì chứ”, nhưng ông ấy là người tạo ra Unix và Go. Các quy tắc của ông ấy có lý do tồn tại