Những Ngoại Tác Tiêu Cực Của Gen-AI Trong Đội Ngũ Phần Mềm

Nghe bài viết:

gen ai-260222235129

Tôi không có điều gì mới để bổ sung vào cuộc tranh luận lớn về đạo đức của AI tạo sinh, tác động môi trường của nó, hay việc ngành phần mềm đã đến điểm bẻ ngoặt hay chưa. Tôi muốn nói, rất cụ thể, về cách AI tạo sinh đã ảnh hưởng đến công việc của tôi với tư cách là một kỹ sư phần mềm cho đến thời điểm này.

Tôi sẽ không tranh luận rằng việc dùng LLM để sinh mã nguồn năng suất hơn hay kém hơn so với cách làm “truyền thống”. Tôi không đủ tư cách để kết luận điều đó; tôi không dùng LLM/agent để viết code và cũng không nắm rõ các best practice liên quan. Tôi không phản đối về mặt nguyên tắc việc sử dụng coding agent, và tôi thấy việc review code bằng AI hay “rubber-ducking” với chatbot đôi khi rất hữu ích.

Điều tôi muốn lập luận là: dù lợi ích có thể vượt trội hơn hay không, AI tạo sinh đã khiến một số hình thức cộng tác trong đội ngũ phần mềm trở nên kém hiệu quả hơn. Tôi muốn ghi nhận một vài ví dụ cho thấy phát triển phần mềm có sự hỗ trợ của LLM không chỉ có lợi ích mà còn có chi phí, với hy vọng rằng chúng ta có thể cải thiện tình hình bằng cách thừa nhận và tìm cách giảm thiểu những chi phí này.

Tính Súc Tích

Xây dựng phần mềm theo nhóm đòi hỏi kỹ năng giao tiếp không kém gì kỹ năng kỹ thuật. Phần lớn giao tiếp này diễn ra bằng văn bản tiếng Anh. Kỹ sư phần mềm viết proposal kỹ thuật, tài liệu thiết kế, tài liệu hướng dẫn và mô tả PR. Nếu các tài liệu này được viết kém, những kỹ sư khác trong nhóm sẽ khó hiểu hệ thống đang được mô tả.

Theo kinh nghiệm của tôi, các kỹ sư dùng coding agent để triển khai tính năng thường cũng dùng agent để viết mô tả PR. Những mô tả này có xu hướng dài dòng và quá chi tiết. Chúng có thể bao gồm một tiêu đề kiểu “Tổng Quan Nhanh Về Thay Đổi” kèm theo danh sách gạch đầu dòng bảy hoặc tám mục. Sau đó là “Tổng Quan Chi Tiết” mô tả thay đổi trong ba hoặc bốn đoạn văn. Thông tin có thể chính xác, nhưng vấn đề là thiếu ngữ cảnh và sự tổng hợp.

Đến thời điểm một tính năng được mở PR, nhóm thường đã thảo luận về nó nhiều lần, thậm chí họp để đánh giá các phương án triển khai. Coding agent không biết điều này, nên nó không thể viết kiểu: “triển khai tính năng mới bằng cách dùng phương án x như đã thống nhất”, một cách viết ngắn gọn nhưng rất ý nghĩa với nhóm. Có thể một ngày nào đó agent sẽ nghe được mọi cuộc họp, nhưng hiện tại những dạng “viết tắt ngữ cảnh” như vậy vẫn ngoài tầm với. Điều này khiến phần lớn thông tin trong mô tả PR do LLM sinh ra trở nên dư thừa. Thông tin thực sự hữu ích bị chìm trong một bức tường chữ không cần thiết.

Mô tả PR do agent sinh ra cũng thiếu cấu trúc phân cấp. Tức là agent ném rất nhiều thông tin mà không phân biệt đâu là trọng tâm. PR có thể bao gồm bảy hoặc tám thay đổi, nhưng điều gì là quan trọng nhất? Thay đổi nào là hệ quả của thay đổi khác? Một con người có thể viết: “Tôi thêm dropdown mới, nhưng nhận ra không thể lấy dữ liệu từ endpoint hiện tại, nên phải thêm module mới bọc endpoint khác mà Sarah nói là có trong service ‘foo’.” Từ đó ta hiểu ngay rằng mục tiêu chính là dropdown mới; phần backend chỉ là điều kiện cần để đạt được mục tiêu đó. Coding agent có thể trình bày hai thay đổi như ngang hàng nhau, khiến PR khó hiểu và tốn thời gian review hơn.

Tôi mới chỉ nói về mô tả PR, nhưng vấn đề tương tự cũng xuất hiện khi LLM được dùng để sinh bất kỳ tài liệu văn bản nào chia sẻ giữa các kỹ sư.

Sự Cảnh Giác

Dù coding agent có tạo ít bug hơn con người hay không, vấn đề nằm ở chỗ: loại bug mà agent tạo ra khác với loại bug mà con người thường mắc phải.

Chúng ta biết con người hay sai ở đâu: lỗi off-by-one, bỏ sót edge case, race condition. Nhưng theo những gì tôi thấy, coding agent có thể tạo ra mọi loại bug với xác suất gần như ngang nhau. Điều này khiến việc review code khó hơn nhiều.

Gần đây, tôi review một PR thêm một tham số dòng lệnh vào một web service Python để tắt một kiểm tra quyền. Mặc định, chúng tôi muốn kiểm tra này luôn được bật (lựa chọn an toàn nhất), nhưng cho phép override bằng cách truyền tham số cực kỳ rõ ràng --require-x-perm=False. Coding agent sinh ra đoạn code như sau:

# ...
# bunch of other code that might distract you
# ...

parser.add_argument(
    "--require-x-perm",
    action="store_false",
    default=True,
    help="Require x permission (default: True)",
)

# ...
# even more code that also looks important
# ...

Nếu lướt qua, đoạn code trông có vẻ đúng! Có default=True và help string cũng khẳng định điều đó. Nhưng thực tế, vì dùng store_false, khi truyền --require-x-perm thì lại tắt kiểm tra quyền.

Điều đáng sợ là lỗi này không giống lỗi mà con người thường mắc. store_false ít phổ biến hơn store_true, và con người thường sẽ suy nghĩ kỹ khi dùng nó. Hơn nữa, lỗi này “sai một cách hoàn hảo”, kiểu mà con người có thể nhận ra ngay khi gõ.

Việc phải cảnh giác với những lỗi kiểu này đồng nghĩa với việc mỗi dòng code đều phải bị nghi ngờ ngang nhau. Dù là đoạn code tầm thường nhất, reviewer cũng không thể lướt qua. Có thể review code vốn dĩ nên như vậy, nhưng trong thế giới với thời gian và sự chú ý hữu hạn, việc được phép “lướt nhanh” ở vài chỗ từng giúp tiết kiệm công sức. Giờ đây, điều đó khó hơn, khiến review PR tốn thời gian và căng thẳng hơn.

Quyền Tác Giả

Phần mềm rất phức tạp. Trong một đội, hiểu biết về codebase thường phân tán giữa nhiều người. Không có cách tốt để ghi lại ai biết gì, và dù có, thông tin đó cũng nhanh chóng lỗi thời.

Việc biết nên hỏi ai khi có thắc mắc là cực kỳ quan trọng, vì kiến thức “ngầm” nằm ngoài code và chỉ có thể học qua trao đổi trực tiếp.

Trước đây, nếu ai đó merge một PR lớn, phức tạp, đó là tín hiệu mạnh rằng họ hiểu hệ thống. Thấy Joe merge PR 1000+ dòng vào hệ thống caching tuần trước, bạn có thể tin rằng Joe là người phù hợp để hỏi về caching.

Ngày nay, Joe có thể đã dùng LLM để sinh phần lớn PR đó. Có thể anh ấy nắm khái quát, nhưng không trả lời được chi tiết một module nào đó. Thậm chí tệ hơn, có thể anh ấy không thực sự hiểu hệ thống dù đã thay đổi nó rất nhiều. Khi đó, thời gian hỏi anh ấy bị lãng phí. Không còn tín hiệu “tác giả = người hiểu rõ”, bạn phải mất thêm thời gian dò hỏi xem ai thực sự hiểu phần nào của codebase.

Vấn đề này cũng áp dụng với tài liệu kỹ thuật viết bằng LLM. Trước đây, đọc một tài liệu có thể cho bạn biết không chỉ nội dung mà còn hiểu biết và quan điểm của người viết. Ví dụ, một đồng nghiệp đề xuất thư viện A thay vì B vì A cung cấp API thiên về functional programming. Bạn có thể nghĩ: “Có lẽ họ hiểu functional programming, nên hỏi họ về bug trong Clojure.” Nhưng hóa ra đó chỉ là LLM nói thay họ.

Nếu tôi nghi ngờ một tài liệu do LLM viết, tôi cảm thấy thời gian đọc nó kém giá trị hơn, vì nó không cho tôi biết gì về tác giả.

Ngoại Tác Tiêu Cực

Tôi nghĩ gọi những điều này là “ngoại tác tiêu cực” là một ẩn dụ phù hợp. Có thể sự bất đồng về năng suất của coding agent xuất phát từ việc lợi ích tập trung vào một cá nhân, còn chi phí lan sang người khác.

Có thể xét tổng thể, nhóm vẫn có lợi. Nhưng tối thiểu, vì sự tôn trọng đồng nghiệp, kỹ sư dùng agent nên xử lý các vấn đề mà code (hoặc văn bản) do agent tạo ra gây ra cho người khác.

Một số vấn đề có thể được giải quyết bằng công nghệ tốt hơn. Có thể LLM sẽ ngày càng súc tích và ít bug hơn. Nhưng tôi nghĩ giải pháp tốt nhất là văn hóa, không phải kỹ thuật.

Đội ngũ phần mềm nên áp dụng chính sách minh bạch về việc dùng LLM, với hiểu rằng code hoặc văn bản do LLM sinh ra sẽ được tiếp nhận theo một bộ kỳ vọng khác. PR hay tài liệu có sử dụng LLM đáng kể nên được gắn nhãn rõ ràng.

Nếu một mô tả PR dài dòng nhưng được đánh dấu “LLM-generated”, người đọc có thể quyết định mức độ đầu tư thời gian, và bớt áp lực phải đọc kỹ từng câu vì sợ bỏ lỡ ý quan trọng của tác giả. Nếu có hệ quả quan trọng, tác giả cần nêu rõ bên ngoài phần văn bản do LLM sinh ra.

Tương tự, PR gắn nhãn “LLM-generated” có thể được review cẩn trọng hơn, cho phép reviewer điều chỉnh chiến lược review tùy theo nguồn gốc code. Việc gắn nhãn cũng giúp xác định liệu một đóng góp có phản ánh kiến thức thực sự của tác giả hay không.

Tôi không có ý chê bai những người dùng coding agent. Tôi mừng vì mọi người thử nghiệm, và có lẽ agent sẽ giúp đội ngũ làm được những việc trước đây không thể. Nhưng tôi muốn phản biện lại câu chuyện rằng coding agent mang đến “bữa trưa miễn phí” về năng suất. Theo quan sát của tôi, chúng giúp tạo code dễ hơn, nhưng làm cộng tác khó hơn. Hy vọng chúng ta có thể giữ được lợi ích thứ nhất trong khi giảm thiểu tác động của thứ hai.