ByteByteGo
88

Thủ thuật

Tại sao xác thực mã nguồn lại trở nên quan trọng hơn bao giờ hết trong kỷ nguyên AI?

(giờ Việt Nam)

Tóm tắt AI

Sự bùng nổ của mã nguồn do AI tạo ra đặt ra thách thức lớn cho khâu kiểm chứng. Bài viết phân tích tầm quan trọng ngày càng tăng của việc xác thực mã nguồn và những định hướng tương lai trong quy trình phát triển phần mềm.

Bản dịch AI

Why Code Verification Matters More Than Ever in the Age of AI

Khi bạn cấp cho một agent một access token, nó sẽ lan truyền: vào cửa sổ ngữ cảnh (context window), vào nhật ký gọi công cụ (tool call logs), và vào các ghi chú mà nó lưu giữ giữa các bước. Mỗi bản sao hoạt động từ bất cứ đâu, rất lâu sau khi sự việc đã xảy ra.

Relay giữ thông tin xác thực tại WorkOS. Agent của bạn gọi tên người dùng, WorkOS đính kèm token đó, làm mới nó và chỉ giải phóng nó cho các máy chủ nằm trong danh sách cho phép (allowlisted hosts). Một phiên làm việc của agent bị chiếm quyền điều khiển là một tiến trình đang chạy mà bạn có thể tiêu diệt.

Tìm hiểu cách thức hoạt động →

Khoảng cách giữa mã nguồn thực thi ổn thỏa và mã nguồn thực sự an toàn để tin cậy đang ngày càng nới rộng khá nhanh. Trong nhiều năm, viết mã là bước chậm chạp, tốn kém, trong khi đánh giá mã lại là một nhiệm vụ nhỏ hơn ở giai đoạn cuối. Với sự trỗi dậy của lập trình có sự hỗ trợ của AI, sự cân bằng này đang thay đổi.

Các công cụ AI hiện có thể tạo ra một hàm hoạt động trong vài giây và một tính năng hoàn chỉnh trong vài phút. Các đội ngũ có thể viết nhiều mã do máy tạo ra hơn mỗi tháng. Nói cách khác, việc sản xuất mã hiện nay nhanh và tương đối dễ dàng, trong khi xác minh mã mới là phần khó hơn. Người đánh giá vẫn phải đọc thay đổi, hiểu nó và quyết định xem nó có phù hợp để đưa vào môi trường production hay không. Thực tế, việc viết nhiều mã hơn đơn giản có nghĩa là có nhiều mã cần phải được xác minh hơn.

Gần đây, chúng tôi đã có cơ hội trò chuyện với Andrea Malagodi, CTO của Sonar (công ty đã xây dựng một số phần mềm xác minh mã được sử dụng nhiều nhất). Ông đã cung cấp những hiểu biết sâu sắc về xác minh mã, đặc biệt là trong bối cảnh AI và cách Sonar đang thích nghi với những thay đổi gần đây.

Trong bài viết này, chúng ta sẽ xem xét cách thức hoạt động của xác minh mã, lý do tại sao sự gia tăng của mã do AI tạo ra lại gây thêm áp lực lên nó, cùng với những hiểu biết cực kỳ hữu ích từ Andrea về tương lai của lĩnh vực này.

Sự thay đổi liên quan đến việc tạo và xác minh mã khá rõ ràng khi chúng ta nhìn vào dữ liệu. Một trong những tín hiệu rõ ràng nhất đến từ nghiên cứu DORA của Google, một nghiên cứu dài hạn về cách hàng ngàn đội ngũ xây dựng và vận chuyển phần mềm. Công trình gần đây của họ cho thấy khi các đội ngũ áp dụng AI nhiều hơn, độ ổn định khi bàn giao sản phẩm đã giảm xuống. Niềm tin vào mã do AI tạo ra vẫn ở mức thấp, với hơn một phần ba số lập trình viên báo cáo rằng họ không mấy tin tưởng vào những gì các công cụ này tạo ra [2]. Nói cách khác, tốc độ viết mã nhanh hơn đã mang lại nhiều áp lực hơn ở các giai đoạn sau.

Một thử nghiệm có kiểm soát từ nhóm nghiên cứu METR đưa ra dấu hiệu tương tự. Những người tham gia là các lập trình viên nguồn mở giàu kinh nghiệm đang làm việc trên các dự án hoàn thiện của riêng họ, và mỗi nhiệm vụ được chỉ định ngẫu nhiên là cho phép hoặc không cho phép sử dụng công cụ AI. Các lập trình viên kỳ vọng AI sẽ giúp họ tăng tốc khoảng một phần tư thời gian.

Tuy nhiên, kết quả cho thấy một bức tranh hoàn toàn khác. Các nhiệm vụ có sự hỗ trợ của AI mất nhiều thời gian hơn khoảng 19% [3]. Hơn nữa, điều này xảy ra sau khi các lập trình viên tin rằng AI đã giúp họ làm việc hiệu quả hơn. Hóa ra, rất nhiều thời gian dư thừa đã đổ dồn vào việc viết prompt, chờ đợi, đọc kết quả đầu ra và sửa lỗi. Công bằng mà nói, cùng nhóm đó sau đó đã báo cáo một tín hiệu tiếp theo gây bối rối hơn. Điều này một phần là do các lập trình viên muốn tiếp tục sử dụng các công cụ AI của họ [4].

Tuy nhiên, nếu xem xét các kết quả này cùng nhau, rõ ràng là trong khi AI chắc chắn làm tăng lượng mã được viết ra, nó cũng dẫn đến nhiều công việc xác minh hơn ở các giai đoạn sau.

Vậy trước tiên hãy hiểu xác minh mã thực sự có nghĩa là gì.

Xác minh mã là thuật ngữ bao quát cho mọi kiểm tra đảm bảo rằng một đoạn mã là đúng, an toàn và đủ khả năng bảo trì để đưa vào môi trường production. Nói cách khác, đó là công việc tạo dựng đủ niềm tin để đưa một thay đổi đến trước người dùng thực. Thuật ngữ chính cần lưu ý ở đây là "tạo dựng" (earning). Điều này là do niềm tin đến theo từng mức độ. Nó được xây dựng qua từng bước kiểm tra một, thay vì được trao ngay lập tức.

Hãy nghĩ về nhiệm vụ soạn thảo một hợp đồng. Viết các từ ngữ chỉ là một phần của nhiệm vụ này. Tuy nhiên, việc đánh giá, kiểm tra pháp lý và chữ ký mới là những thứ biến những từ ngữ đó thành thứ mà mọi người thực sự có thể dựa vào. Viết mã cũng hoạt động theo cách tương tự. Khoảnh khắc một đoạn mã rời khỏi trình soạn thảo và được commit vào kho lưu trữ mã (code repository), nó mang theo một tuyên bố ngầm định về chức năng. Xác minh mã là quá trình mà tuyên bố đó được kiểm tra cho đến khi một đội ngũ cảm thấy an toàn để sử dụng mã đó trong môi trường production thực tế.

Một số lĩnh vực đẩy giai đoạn này đến giới hạn thông qua xác minh hình thức nghiêm ngặt (rigorous formal verification). Trong các lĩnh vực đó, các kỹ sư phải chứng minh bằng toán học rằng mã được triển khai khớp với một đặc tả chính xác. Quy trình như vậy là tiêu chuẩn cho những thứ quan trọng như hệ thống điều khiển chuyến bay và nhân hệ điều hành (kernels), nơi một lỗi duy nhất có thể đe dọa tính mạng. Tuy nhiên, đối với hầu hết phần mềm, việc áp dụng cách tiếp cận tương tự có thể tốn kém hơn nhiều so với lợi ích mang lại. Do đó, các đội ngũ chọn các kiểm tra nhẹ nhàng hơn được sắp xếp theo từng lớp.

Tiếp tục với phép ẩn dụ về các lớp, chúng ta có thể hình dung xác minh mã như một chồng bộ lọc. Như bạn có thể thấy, mỗi bộ lọc bắt được một loại vấn đề nhất định.

Ở trên cùng của chồng bộ lọc, chúng ta có các kiểm tra rẻ nhất. Ví dụ:

Type Checker: Nó xác nhận liệu các giá trị di chuyển qua mã của bạn có đúng kiểu dữ liệu mà mỗi thao tác mong đợi hay không. Bằng cách này, nó có thể bắt được cả một nhóm lỗi trước khi mã kịp chạy.

Linter: Nó quét các mẫu đáng ngờ và các vấn đề về phong cách viết mã.

Những loại kiểm tra này có thể chạy ngay lập tức và gần như không tốn kém gì. Bên dưới chồng bộ lọc này, chúng ta có các bài kiểm tra (tests).

Một unit test chạy một đoạn mã nhỏ với các đầu vào đã biết và xác nhận xem nó có trả về đầu ra mong đợi hay không. Các bài kiểm tra có thể bắt được các lỗi hành vi trong mã mà type checker không thể phát hiện. Điều này là do một đoạn mã có thể có kiểu dữ liệu hoàn toàn hợp lệ nhưng vẫn tính toán ra kết quả sai. Ví dụ, hãy xem xét một hàm đơn giản được cho là cộng hai số, nhưng thay vào đó lại nhân chúng. Trong trường hợp như vậy, type checker và linter sẽ không chỉ ra bất kỳ vấn đề nào. Chỉ có một unit test so sánh kết quả của thao tác với một đáp án đã biết mới tiết lộ được lỗi đó.

Bên dưới lớp kiểm tra là bộ lọc đánh giá của con người (human review). Đây về cơ bản là trường hợp một lập trình viên khác xem xét thay đổi và đánh giá xem nó có phù hợp với hệ thống, giải quyết đúng vấn đề và có dễ đọc hay không. Lớp này bắt được những gì máy móc có thể bỏ lỡ, chẳng hạn như một giải pháp hoạt động tốt nhưng lại sử dụng cách tiếp cận mà tiêu chuẩn của nhóm không khuyến khích.

Bên dưới tất cả các lớp này là thiết lập giám sát production (production monitoring). Công việc của hệ thống này là quan sát mã dưới lưu lượng truy cập thực tế và gắn cờ các vấn đề có thể đã vượt qua mọi lớp trước đó.

Các chồng bộ lọc trong thực tế cũng có thể chứa nhiều lớp hơn thế này. Điều này có thể bao gồm các trình quét bảo mật và kiểm tra sự phụ thuộc (dependency checks). Tuy nhiên, điểm mấu chốt là mỗi bộ lọc bao phủ một điểm yếu cụ thể của bộ lọc phía trên nó. Đây là lý do tại sao các đội ngũ chuyên nghiệp chạy một vài lớp như vậy theo một thứ tự cụ thể trước khi phát hành bất kỳ mã nào vào môi trường production.

Các bộ lọc trong chồng đó thuộc về hai nhóm:

Phân tích tĩnh (Static Analysis): Các bộ lọc trong nhóm này kiểm tra mã nguồn mà không thực thi nó, điều này làm cho loại phân tích này nhanh và rộng. Với phân tích tĩnh, chúng ta có thể quét toàn bộ cơ sở mã trong một lần chạy. Type checker và linter thuộc nhóm này. Sự đánh đổi là hành vi thực tế tại thời điểm chạy (runtime) vẫn nằm ngoài tầm quan sát. Do đó, phân tích tĩnh đôi khi có thể báo động về một vấn đề có thể không tồn tại trong điều kiện thực tế.

Phân tích động (Dynamic Analysis): Các bộ lọc trong nhóm này chạy mã với các đầu vào thực tế và quan sát kết quả. Các bài kiểm tra thuộc nhóm này. Nhóm này dựa vào việc kiểm tra hành vi thực tế, nhưng bị giới hạn bởi các đường dẫn được thực thi. Một bộ kiểm tra chỉ chạy trên "happy path" (đường dẫn lý tưởng) không thể phát hiện ra lỗi treo máy có thể xảy ra khi gặp đầu vào trống.

Mặc dù có độ bao phủ rộng rãi, một bản quét sạch và một bộ kiểm tra xanh (thành công) vẫn có thể để lại những lỗ hổng. Đây là lý do tại sao xác minh mã dựa vào nhiều bộ lọc hoạt động cùng nhau để đạt hiệu quả.

Một kết luận hấp dẫn mà chúng ta có thể đưa ra là kiểm tra càng nhiều càng tốt. Tuy nhiên, có một sự đánh đổi cốt lõi trong xác minh mã. Mỗi bộ lọc có thể mắc hai loại sai lầm:

Dương tính giả (False Positive): Nghĩa là gắn cờ một vấn đề khi mã thực tế vẫn ổn.

Âm tính giả (False Negative): Nghĩa là giữ im lặng trong khi một lỗi thực sự vẫn lọt qua.

Nếu chúng ta tinh chỉnh một công cụ để bắt mọi vấn đề có thể xảy ra, nó có thể làm ngập các lập trình viên bằng các báo động giả. Tuy nhiên, nếu chúng ta tinh chỉnh nó để giữ im lặng trừ khi chắc chắn, nó có thể bắt đầu bỏ lỡ các lỗi thực sự. Hai khía cạnh này đối nghịch với nhau.

Các báo động giả gây ra chi phí khá lớn. Khi một công cụ thường xuyên đưa ra báo động giả, các lập trình viên bắt đầu phớt lờ nó. Tuy nhiên, thói quen này có thể gây ra hậu quả thảm khốc. Ngay cả một cảnh báo thực sự thỉnh thoảng xuất hiện cũng có thể bị gạt đi cùng với những cảnh báo khác. Nghiên cứu về các công cụ phân tích tĩnh mô tả mô hình này, nơi tỷ lệ dương tính giả cao làm xói mòn niềm tin cho đến khi các đội ngũ tắt công cụ hoặc không chú ý đến các cảnh báo của nó. Tuy nhiên, làm như vậy lại mở đường cho chính những lỗi mà công cụ đó được tạo ra để ngăn chặn [7].

Đây là lý do tại sao một thiết lập xác minh mã tốt quan tâm đến chất lượng tín hiệu cũng nhiều như độ bao phủ. Andrea từ Sonar mô tả sự cân bằng này gần giống như định lý CAP dành cho xác minh mã. Điều này được xây dựng từ ý tưởng cổ điển rằng bạn chỉ có thể thúc đẩy một số thuộc tính bằng cách đánh đổi các thuộc tính khác. Ba ưu tiên cạnh tranh trong trường hợp xác minh mã là tốc độ, độ chính xác và độ bao phủ, và không có công cụ nào thắng tuyệt đối cả ba. Nhóm của Andrea cố gắng giữ trọng tâm vào con người với một quy tắc đơn giản: một phát hiện mà lập trình viên có thể hành động dựa trên đó thì mới đáng để đưa ra cảnh báo.

Lập trìnhAIKiểm thử phần mềmPhát triển phần mềmKỹ thuật
Đọc bài gốc

Bài viết được AI dịch và tổng hợp tự động từ ByteByteGo. Liên kết bài gốc ở phía trên. Dữ liệu đồng bộ qua API công khai được ghi nguồn tại AI HOT (canonical) ↗. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.