4 điểm bởi GN⁺ 5 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Chi phí viết code đã giảm mạnh nhờ sự phổ biến của các công cụ coding AI, nhưng thực tế là chi phí để hiểu phần code được tạo ra lại lớn hơn
  • LLM là phi quyết định, không lưu giữ mã nguồn gốc và phạm vi đầu ra mở rộng ra toàn bộ phần mềm nói chung, nên không thể xem tương đương với đầu ra của trình biên dịch
  • LLM tạo code nhanh hơn rất nhiều so với tốc độ con người có thể hiểu, vì vậy nên dùng theo cách tăng dần (incremental) để tránh những thay đổi quy mô lớn mà không ai hiểu nổi
  • Rủi ro cốt lõi khi code trở nên rẻ là độ phức tạp (complexity); nó tăng ít nhất theo cấp số nhân theo quy mô hệ thống, trong khi LLM là một lập trình viên năng suất cao không hề sợ độ phức tạp
  • Giải pháp được đề xuất là một kỹ sư trừ bớt (subtractive engineer), người loại bỏ và đơn giản hóa thay vì thêm code, đồng thời nhấn mạnh việc duy trì kỹ nghệ lập trình máy tính

Thời đại code trở nên rẻ

  • Trong 1 năm qua, AI đã tạo ra lượng lớn code với chất lượng khá tốt và tốc độ rất nhanh, khiến chi phí tạo code giảm đáng kể
  • Đối với lập luận rằng "việc coding vốn dĩ chưa bao giờ là vấn đề", tác giả phản biện rằng coding cũng là một phần của vấn đề, và phần đó đã giảm đi rất nhiều nhờ các công cụ coding AI
  • Tác giả đặt câu hỏi: với những lập trình viên từng tự hào về năng lực coding của mình, việc tầm quan trọng của coding thuần túy suy giảm có ý nghĩa gì

Chi phí hiểu tăng lên (Understanding is Expensive(er))

  • Khi code không còn được viết ra một cách vất vả từ tay lập trình viên mà được tạo hàng loạt, thì bản thân sự hiểu biết về code đó không còn tồn tại
    • Nếu cần hiểu, thì sau khi code được viết xong phải đọc lại code để có được sự hiểu đó
    • Theo quan niệm phổ biến, đọc code do người khác viết còn khó hơn tự viết code của mình
  • Tác giả xem lập luận ủng hộ AI kiểu "chẳng phải chúng ta cũng không hiểu đầu ra của trình biên dịch sao" là một lỗi phân loại (category error)
    • Trình biên dịch là quyết định được, còn LLM thì theo thiết kế là phi quyết định
    • Quy trình làm việc với trình biên dịch giữ lại mã nguồn gốc, còn quy trình với LLM thường thì không
    • Đầu ra của trình biên dịch chỉ giới hạn trong miền hẹp là mã máy, còn đầu ra của LLM không bị giới hạn như vậy mà có thể là phần mềm tổng quát
  • Trong đa số trường hợp, đặc biệt là với phần mềm tối quan trọng (mission-critical software), ngay cả code do LLM tạo ra thì lập trình viên vẫn phải hiểu phần code nền tảng
  • LLM có thể tạo code với tốc độ mà không ai theo kịp, nên tồn tại rủi ro vượt quá tốc độ hiểu
    • Vì vậy, nên sử dụng tăng dần thay vì tạo ra một danh sách thay đổi khổng lồ trong một lần
    • Có thể phù hợp với các đợt refactor mang tính cơ học, nhưng cực kỳ nguy hiểm khi đưa ngữ nghĩa mới (semantics) vào codebase

Cái bẫy người học việc của phù thủy (The Sorcerer's Apprentice Trap)

  • Tác giả đưa cảnh "The Sorcerer's Apprentice" trong phim Disney Fantasia làm ẩn dụ phù hợp cho thời đại AI
    • Để bớt khổ vì việc lau dọn, người học việc niệm phép lên cây chổi; rồi cây chổi quét dọn ngày càng dữ dội khiến tình hình mất kiểm soát
    • Cuối cùng, phù thủy (Sorcerer) quay lại, giành quyền kiểm soát tình hình, quở trách sự ngu ngốc của người học việc và dọn dẹp hỗn loạn
  • Bài học của ẩn dụ này là phải trở thành phù thủy, không phải người học việc, và phù thủy thì phải hiểu code

Độ phức tạp vẫn là điều tệ hại (Complexity: Still Bad)

  • Con người không giỏi nắm bắt các đường cong lũy thừa hay hàm mũ, và thường tin vào những thứ như lãi kép (compound interest) như thể chuyện cổ tích
  • Rủi ro cốt lõi khi code trở nên rẻ là độ phức tạp (complexity); tác giả cho rằng nó tăng ít nhất theo cấp số nhân, và thường theo hàm mũ, theo quy mô hệ thống, dù không đưa ra chứng minh
  • Ngay cả trước thời LLM cũng đã có lập trình viên năng suất cao (prolific coder)
    • Một prolific coder không hề sợ độ phức tạp sẽ tiếp tục chất thêm code lên trên các vấn đề sẵn có, khiến hệ thống sụp đổ vào trạng thái đình trệ không thể sửa nổi, nơi mỗi thay đổi tạo ra số bug tương đương số bug được sửa
  • LLM vừa là một prolific coder vừa không sợ độ phức tạp, nên rất nguy hiểm

Kỹ sư trừ bớt, áp đặt ràng buộc (The Subtractive, Constraining Engineer)

  • Để đối phó với rủi ro của code do LLM tạo ra, tác giả đề xuất hình mẫu kỹ sư trừ bớt, áp đặt ràng buộc (subtractive, constraining engineer)
    • Người kỹ sư này biết nói "không", rà soát kỹ đầu ra của LLM, đề xuất đơn giản hóa và kiên quyết giữ quyền kiểm soát
    • Họ không tự hào về phần code mình tạo ra, mà tự hào về phần code đã loại bỏ hoặc ngăn không cho đi vào hệ thống (cùng các lớp liên quan)
    • Thái độ này gần với nhà điêu khắc (sculptor) hơn là người xây dựng (builder)
  • Tư duy kiểu builder vẫn còn có giá trị ở cấp độ thiết kế hệ thống
    • Một kỹ sư giỏi vẫn phải biết cách kết hợp các thành phần hiệu quả để xây dựng hệ thống
    • Nhưng ngay cả ở cấp độ này, tư duy trừ bớt — loại bỏ các thành phần không cần thiết và các ranh giới hệ thống để đơn giản hóa triển khai và tương tác — vẫn rất hữu ích
  • Kỹ sư trừ bớt là một kiểu người khác với phần lớn coder trước đây; kiểu người này hợp với việc biết nói "không" và thích gọt giũa hệ thống hiện có hơn là tái viết anh hùng ca
  • Cách tiếp cận này thừa nhận thực tế kép rằng code đã rẻ hơn còn độ phức tạp vẫn là kẻ săn mồi đỉnh chuỗi (apex predator), đồng thời là cách hiệu quả để gìn giữ kỹ nghệ lập trình máy tính

1 bình luận

 
Ý kiến trên Lobste.rs
  • Bài viết năm 1985 của Peter Naur, Programming as Theory Building, có vẻ đáng đọc lại thêm vài lần vào thời điểm này

  • Tôi cho rằng cách nói “LLM không biết sợ độ phức tạp” gần như là cường điệu
    Kiểu thất bại đó thực sự có tồn tại. Nếu đưa chỉ dẫn quá rộng, LLM sẽ thêm tầng lớp, tạo ra các mức trừu tượng, và thường viết ra lượng mã quá mức so với bài toán. Nhưng hành vi đó lại dễ quan sát, dễ review, và ngạc nhiên là cũng khá dễ đổi hướng. Chỉ cần căn chỉnh phong cách phần mềm mong muốn bằng các file AGENTS/CLAUDE.md
    Chỉ cần yêu cầu thay đổi nhỏ nhất, hỏi có thể xóa bớt gì, và hỏi liệu lớp trừu tượng đó có thực sự đáng giá không. Cũng có thể hỏi về bất biến, các điểm kết nối, gánh nặng nhận thức, rồi yêu cầu một lượt pass thứ hai để loại bỏ sự “khôn lỏi”, thì nhìn chung nó sẽ đi theo áp lực đó. Nếu đưa vào prompt, còn có thể khiến nó tránh từ đầu
    Rủi ro không nằm ở chỗ LLM hoàn toàn không thể tôn trọng độ phức tạp, mà ở chỗ nó sẵn sàng tạo ra kiểu mã mà quy trình xung quanh đang tưởng thưởng. Đội ngũ thưởng cho số lượng sẽ nhận được số lượng, còn đội ngũ thưởng cho sự đơn giản thì phần lớn cũng sẽ nhận được điều đó
    Các model hiện nay tốt hơn đa số con người, và rồi sẽ còn tốt hơn nữa. Nếu mã không tốt, cần gây áp lực lên các phòng thí nghiệm AI bằng feedback. Ví dụ, tôi thấy dòng GPT viết tài liệu tốt thì cực tệ

    • Vừa đúng vừa không đúng. Cốt lõi có vẻ gần với cách huấn luyện và mục tiêu hơn là năng lực. Thứ đang thịnh hành hiện nay là kiểu “tôi vibe coding cả dự án”, và LLM rất yếu trong việc sửa mã sẵn có so với tạo từ đầu. Rất có thể là do cách huấn luyện, và cũng có vẻ có thiên lệch thống kê. Mã trong các codebase lớn và phức tạp chắc chắn nhiều hơn rất nhiều so với các dự án nhỏ và đơn giản, nên có lẽ đã chiếm tỷ trọng lớn hơn trong quá trình học
      Vì vậy, nói là “bất khả thi” có thể không phải cách diễn đạt tốt nhất, nhưng tôi cho rằng có nhiều thiên lệch khiến mặc định của nó nghiêng về phía phức tạp
      Ngoài ra còn có thiên lệch làm một điều gì đó thay vì không làm gì. Lập trình viên giỏi dùng nhiều ngữ cảnh hơn rất nhiều so với một quản lý chỉ ném ra yêu cầu cụ thể, và cũng sẽ đề xuất phương án thay thế. LLM có lẽ cũng làm được, nhưng hiện tại ngay cả khi có cơ chế “đặt câu hỏi trong lúc lập kế hoạch” hay code phát hiện vòng lặp, nó vẫn không giỏi đặt ra đúng câu hỏi. Có lẽ loại dữ liệu huấn luyện đó cũng không phổ biến
      Ở một khía cạnh nào đó, prompt engineering gần như là một kiểu hack khủng khiếp xoay quanh cả bó vấn đề này
    • Tôi không chắc mình hoàn toàn đồng ý với cách nói đó, nhưng rõ ràng là có điều gì đó ở đây. Một mặt, tôi từng thấy kiểu “độ phức tạp phổ biến” như việc nó mặc định tạo ra một dự án CMake khổng lồ dù chỉ cần một Makefile cho một mục tiêu duy nhất là đủ
      Mặt khác, tôi từng phải thuyết phục và trấn an LLM sau khi nhận được câu trả lời kiểu “Tôi không khuyến nghị việc này cho một dự án một tháng. Hay là cứ commit ở trạng thái này và để nó như một bản demo?”. Dù tôi biết cả hai việc đều sẽ xong trong chưa tới một giờ. Nhưng nếu nói “Ừ, cứ làm hẳn một thứ hoàn toàn mới đi. Tự tìm cách nhé”, thì nó vẫn thử làm, nên theo nghĩa đó cũng có thể nói là nó không biết sợ
    • Một cuộc bàn luận có sắc thái về “vibe coding” trên Lobsters, chắc địa ngục đóng băng mất rồi /s
      Bỏ đùa sang một bên thì tôi đồng ý. Điều này cũng khớp với trải nghiệm của tôi
  • Nói chung tôi không mấy đồng ý với câu “chi phí để hiểu code đã trở nên đắt hơn”
    Với mã LLM sinh ra một cách vô thức thì có thể đúng, nhưng nếu được dẫn dắt đúng cách thì LLM cũng có thể tạo ra mã dễ đọc và được phân tầng tốt. Dĩ nhiên trong trường hợp đó, phần lớn các lựa chọn kiến trúc vẫn do con người quyết định, và theo tôi vốn dĩ nên như vậy
    Ngược lại, tôi thấy LLM cực kỳ giỏi trong việc giúp hiểu mã do người lạ viết. Bạn có thể bảo Claude Code phân tích sâu một đoạn mã cụ thể, tạo tài liệu Markdown giải thích từng phần, rồi còn đề xuất nên bắt đầu review hoặc tìm hiểu từ đâu
    Điều này thực sự rất có giá trị

    • Trải nghiệm dùng LLM với code lạ nhìn chung là tốt, nhưng ở những chỗ lệch khỏi thông lệ thì nó có lúc bịa đặt đầy sáng tạo và nói dối. Vấn đề là vì code vốn đã lạ nên rất khó phân biệt hai chuyện đó
  • “Con người nhìn chung không hiểu tốt các đường cong hàm mũ. Vì thế họ tin vào những câu chuyện cổ tích như lãi kép” — ý là lãi kép là chuyện cổ tích à? Hay là tôi đang hiểu nhầm điều gì đó?