Thông số kỹ thuật đủ chi tiết là mã
A sufficiently detailed spec is code
Nội dung bài viết xoay quanh quan điểm "spec đủ chi tiết chính là code", nhằm bác bỏ ý nghĩ rằng AI agentic coding có thể giúp chúng ta "lách luật" khỏi sự phức tạp của việc viết code thông qua các bản đặc tả (spec). Bài viết chỉ ra sự hiểu lầm phổ biến rằng spec đơn giản hơn code, lấy ví dụ là file `SPEC.md` của dự án Symphony từ OpenAI, vốn có cấu trúc và nội dung rất giống pseudocode, mô tả chi tiết cấu trúc database (database schemas) và logic code. Lời khuyên gửi đến các lập trình viên là dù bạn dùng định dạng spec nào đi nữa, việc tạo ra code có chức năng, dễ bảo trì (maintainable) vẫn đòi hỏi một quá trình tư duy sâu sắc, thường là lặp đi lặp lại, vốn dĩ đã là bản chất của việc code truyền thống.
Bài đăng này thực chất là một đoạn truyện tranh được mở rộng thành một bài viết dài: Đã lâu rồi tôi không cần một bài đăng như bài tôi sắp viết. Nếu ai đó nảy ra ý tưởng tạo mã từ...
Bài đăng này về cơ bản là bộ truyện tranh này đã mở rộng thành một bài viết dài:

Đã lâu rồi tôi không cần một bài viết như bài tôi sắp viết. Nếu ai đó đã nảy ra ý tưởng tạo mã từ các thông số kỹ thuật mà tôi chia sẻ hình ảnh trên với họ và điều đó thường có tác dụng.
Tuy nhiên, những người ủng hộ mã hóa tác nhân tuyên bố đã tìm ra cách thách thức trọng lực và tạo mã hoàn toàn từ các tài liệu đặc tả. Hơn nữa, họ cũng đã làm bùn đủ để tôi tin rằng những điều đảm bảo trong truyện tranh ở trên bình luận bổ sung về lý do tại sao tuyên bố của họ gây hiểu nhầm.
Theo kinh nghiệm của tôi, sự ủng hộ của họ bắt nguồn từ hai quan niệm sai lầm phổ biến:
-
Quan niệm sai lầm thứ 1: tài liệu đặc tả đơn giản hơn tài liệu mã tương ứng
Họ dựa vào quan niệm sai lầm này khi tiếp thị mã hóa đại lý cho các tín đồ những người coi mã hóa tác nhân là thế hệ gia công phần mềm tiếp theo. Họ ước mơ của các kỹ sư được trở thành nhà quản lý, người viết đặc tả các tài liệu mà họ cung cấp cho một nhóm đại lý để thực hiện công việc, chỉ sẽ hiệu quả nếu chỉ định công việc rẻ hơn là thực hiện công việc.
-
Quan niệm sai lầm thứ 2: công việc đặc tả phải chu đáo hơn công việc viết mã làm việc
Họ dựa vào quan niệm sai lầm này khi tiếp thị mã hóa đại lý cho những người hoài nghi lo ngại rằng mã hóa tác nhân sẽ tạo ra độ dốc không thể bảo trì được. Đối số là việc lọc công việc thông qua tài liệu đặc tả sẽ cải thiện chất lượng và thúc đẩy thực hành kỹ thuật tốt hơn.
Tôi sẽ giải thích lý do tại sao tôi tin rằng đó là những quan niệm sai lầm khi sử dụng bê tông ví dụ.
Mã được che đậy mỏng manh
Tôi sẽ bắt đầu từ Symphony của OpenAI dự án mà OpenAI coi là một ví dụ về cách tạo dự án từ một tài liệu đặc tả.
Dự án Symphony là một nhà soạn nhạc đại lý tuyên bố được tạo ra từ một "thông số kỹ thuật" (SPEC.md ), và tôi nói "thông số kỹ thuật" trong dấu ngoặc kép vì tệp này ít thông số kỹ thuật hơn và giống như mã giả ở dạng đánh dấu hơn. Nếu bạn làm xước bề mặt của tài liệu bạn sẽ thấy nó chứa những thứ như kết xuất văn xuôi của lược đồ cơ sở dữ liệu:
4.1.6 Phiên trực tiếp (Siêu dữ liệu phiên tổng đài viên)
Trạng thái được theo dõi trong khi quy trình con tác nhân mã hóa đang chạy.
Trường:
session_id(chuỗi,)- thread_id(chuỗi)turn_id(chuỗi)codex_app_server_pid(chuỗi hoặc null)last_codex_event(chuỗi/enum hoặc null)last_codex_timestamp(dấu thời gian hoặc rỗng)last_codex_message(tải trọng tóm tắt)codex_input_tokens(số nguyên)codex_output_tokens (số nguyên) codex_total_tokens(số nguyên)last_reported_input_tokens(số nguyên)last_reported_output_tokens(số nguyên)last_reported_total_tokens(số nguyên)turn_count(số nguyên)- Số lượt tác nhân mã hóa đã bắt đầu trong vòng đời của nhân viên hiện tại.
… hoặc những đoạn văn xuôi chứa mã:
8.3 Kiểm soát đồng thời
Giới hạn toàn cầu:
available_slots = max(max_concurrent_agents - Running_count, 0)
Giới hạn mỗi tiểu bang:
max_concurrent_agents_by_state[state]nếu có (khóa trạng thái được chuẩn hóa)- nếu không thì sẽ chuyển sang giới hạn chung
Thời gian chạy tính các vấn đề theo trạng thái được theo dõi hiện tại của chúng trong đang chạy bản đồ.
8.4 Thử lại và lùi
Thử tạo lại mục nhập:
- Hủy mọi đồng hồ hẹn giờ thử lại hiện có cho cùng một vấn đề.
- Lưu trữ
lần thử,mã định danh,lỗi,due_at_msvà bộ điều khiển hẹn giờ mới.
Công thức lùi:
- Thử lại việc tiếp tục bình thường sau khi thoát khỏi nhân viên sạch sẽ sử dụng độ trễ cố định ngắn là
1000ms. - Các lần thử lại do lỗi sử dụng
delay = min(10000 * 2^(attempt - 1), Agent.max_retry_backoff_ms). - Nguồn bị giới hạn bởi thời gian chờ thử lại tối đa đã được định cấu hình (
300000/ 5 phút mặc định).
Thử lại hành vi xử lý:
- Tìm nạp các vấn đề về ứng viên đang hoạt động (không phải tất cả các vấn đề).
- Tìm vấn đề cụ thể theo
issue_id. - Nếu không tìm thấy, hãy hủy bỏ xác nhận quyền sở hữu.
- Nếu được tìm thấy và vẫn đủ điều kiện làm ứng viên:
- Gửi đi nếu còn chỗ.
- Nếu không thì yêu cầu có lỗi
không có sẵn vị trí điều phối nào.
- Nếu tìm thấy nhưng không còn hoạt động, hãy hủy bỏ xác nhận quyền sở hữu.
… hoặc các phần được thêm vào một cách rõ ràng để giám sát quá trình tạo mã của mô hình, như thế này:
6.4 Tóm tắt trường cấu hình (Bảng ghi chú)
Phần này được cố tình dư thừa để tác nhân mã hóa có thể triển khai lớp cấu hình một cách nhanh chóng.
tracker.kind: chuỗi, bắt buộc, hiệntuyến tínhtracker.endpoint: chuỗi,mặc địnhhttps://api.Tuyến tính.app/graphqlkhitracker.kind=Tuyến tính- …
… hoặc mã hoàn toàn 1:
16. Thuật toán tham chiếu (Ngôn ngữ bất khả tri)
16.1 Khởi động dịch vụ
hàm start_service(): configure_logging() start_observability_outputs() start_workflow_watch(on_change=reload_and_reapply_workflow) trạng thái = { poll_interval_ms: get_config_poll_interval_ms(), max_concurrent_agents: get_config_max_concurrent_agents(), đang chạy: {}, đã xác nhận: set(), retry_attempts: {}, đã hoàn thành: set(), codex_totals: {input_tokens: 0, out_tokens: 0, Total_tokens: 0, giây_running: 0}, codex_rate_limits: null xác thực = xác thực_dispatch_config() nếu xác thực không ổn: log_validation_error(xác thực) failed_startup(xác thực) startup_terminal_workspace_cleanup() lịch_tick(delay_ms=0) sự kiện_loop(trạng thái)
Tôi cảm thấy việc những người ủng hộ mã hóa tự động tiếp thị là khá thiếu thành thật cái này thay thế cho mã khi tài liệu đặc tả có nội dung như sau code (hoặc trong một số trường hợp có nghĩa đen là code).
Đừng hiểu sai ý tôi: Tôi không nói rằng các tài liệu đặc tả không bao giờ nên bao gồm mã giả hoặc triển khai tham chiếu; cả hai đều công bằng phổ biến trong công việc đặc tả. Tuy nhiên, bạn không thể yêu cầu thông số kỹ thuật đó tài liệu thay thế cho mã khi chúng đọc giống như mã.
Tôi nêu ra điều này vì tôi tin rằng Symphony minh họa cho quan niệm sai lầm đầu tiên à:
Quan niệm sai lầm 1: tài liệu đặc tả đơn giản hơn mã tương ứng
Nếu bạn cố gắng tạo một tài liệu đặc tả đủ chính xác để tạo ra một cách đáng tin cậy triển khai có hiệu quả nhé bạn nhất thiết phải biến tài liệu thành mã hoặc thứ gì đó rất giống với mã (như có cấu trúc cao và trang trọng tiếng Anh).
Dijkstra giải thích tại sao điều này là không thể tránh khỏi:
Đồng thời, chúng tôi biết rằng việc lựa chọn giao diện không chỉ là sự phân công (một lượng lao động cố định), bởi vì công việc liên quan đến hợp tác và giao tiếp qua giao diện phải được thêm vào. Chúng tôi biết trong khi chờ đợi - từ kinh nghiệm nghiêm túc, tôi có thể nói thêm - rằng một sự thay đổi trong giao diện có thể dễ dàng tăng khối lượng công việc ở cả hai bên hàng rào phải được thực hiện (thậm chí là quyết liệt như vậy). Do đó, sự ưa thích ngày càng tăng đối với những gì bây giờ được gọi là "giao diện hẹp". Vì vậy, mặc dù chuyển sang giao tiếp giữa máy móc và con người được thực hiện bằng ngôn ngữ mẹ đẻ của nó sẽ có ý nghĩa rất lớn tăng gánh nặng cho máy, chúng ta phải thách thức giả định rằng điều này sẽ đơn giản hóa cuộc sống của con người.
Một cái nhìn ngắn gọn về lịch sử toán học sẽ cho thấy thách thức này hợp lý đến mức nào là. Toán học Hy Lạp bị mắc kẹt vì nó vẫn là ngôn từ, hình ảnh hoạt động, "đại số" Hồi giáo, sau một nỗ lực rụt rè về biểu tượng, đã chết khi nó quay trở lại với phong cách hùng biện, và thế giới văn minh hiện đại chỉ có thể nổi lên—dù tốt hơn hay tồi tệ hơn—khi Tây Âu có thể tự giải thoát khỏi xiềng xích của chủ nghĩa kinh viện thời trung cổ—một nỗ lực vô ích nhằm đạt được độ chính xác bằng lời nói!— nhờ vào hình thức trang trọng được thiết kế cẩn thận hoặc ít nhất là có ý thức biểu tượng mà chúng ta nợ những người như Vieta, Descartes, Leibniz và (sau này) Boole.
Các lập trình viên đại lý đang học hỏi một cách khó khăn để bạn không thể thoát khỏi "ngõ hẹp giao diện" (đọc: mã) mà lao động kỹ thuật yêu cầu; bạn chỉ có thể biến lao động đó thành một thứ gì đó khác biệt bề ngoài mà vẫn đòi hỏi độ chính xác như nhau.
Sự bong tróc
Ngoài ra, tạo mã từ thông số kỹ thuật thậm chí còn không hoạt động đáng tin cậy! tôi thực sự đã cố gắng làm những gì Symphony ĐỌC đề xuất:
Yêu cầu tác nhân mã hóa yêu thích của bạn xây dựng Symphony bằng ngôn ngữ lập trình mà bạn chọn:
Triển khai Symphony theo thông số kỹ thuật sau: https://github.com/openai/symphony/blob/main/SPEC.md
Tôi đã yêu cầu Claude Code xây dựng Symphony bằng ngôn ngữ lập trình mà tôi chọn
(Haskell2 , nếu bạn không thể đoán được từ tên blog của tôi) và nó không hoạt động.
Bạn có thể tìm thấy kết quả trong
kho lưu trữ Gabriella439/symphony-haskell.
Không chỉ có nhiều lỗi (mà tôi còn phải nhắc Claude sửa và bạn
có thể tìm thấy các bản sửa lỗi đó trong lịch sử cam kết), nhưng ngay cả khi mọi thứ "hoạt động"
(nghĩa là: không có thông báo lỗi) codex đại lý chỉ quay âm thầm mà không có
thực hiện bất kỳ tiến triển nào trên vé Tuyến tính mẫu sau:
Tạo kho lưu trữ trống mới
Không cần tạo dự án GitHub. Chỉ cần tạo một kho lưu trữ git trống
Nói cách khác, "nỗ lực vô ích nhằm đạt được độ chính xác bằng lời nói" của Symphony (sử dụng quan điểm của Dijkstra từ) vẫn không thể tạo ra quá trình triển khai hoạt động một cách đáng tin cậy3.
Vấn đề này cũng không chỉ giới hạn ở Symphony: chúng tôi thấy vấn đề tương tự ngay cả đối với thông số kỹ thuật nổi tiếng như YAML. các Đặc tả YAML cực kỳ chi tiết, được sử dụng rộng rãi và bao gồm một bộ kiểm tra sự phù hợp và phạm vi rộng lớn phần lớn việc triển khai YAML vẫn không tuân thủ đầy đủ thông số kỹ thuật.
Symphony có thể cố gắng khắc phục tình trạng không ổn định bằng cách mở rộng thông số kỹ thuật nhưng đã khá lâu rồi, đạt 1/6 kích thước của Elixir đi kèm triển khai! Nếu đặc điểm kỹ thuật được phát triển hơn nữa, họ sẽ tóm tắt lại truyện ngắn "Về tính chính xác trong khoa học" của Borges:
…Ở Đế chế đó, Nghệ thuật Bản đồ học đã đạt đến mức Hoàn hảo đến nỗi bản đồ của một Tỉnh chiếm toàn bộ Thành phố và bản đồ của Đế quốc, toàn bộ một tỉnh. Theo thời gian, những Bản đồ vô lương tâm đó không còn hài lòng hơn, và Hiệp hội vẽ bản đồ đã tạo ra Bản đồ của Đế chế có kích thước tương đương với Đế chế, và trùng khớp từng điểm với nó. Các thế hệ tiếp theo không mấy hứng thú với việc nghiên cứu về Bản đồ học như Tổ tiên của họ đã thấy rằng Bản đồ rộng lớn đó là vô dụng, và không phải không có sự tàn nhẫn khi họ giao nó cho Sự khắc nghiệt của mặt trời và mùa đông. Ở vùng sa mạc phía Tây, vẫn còn cho đến ngày nay, có Tàn tích rách nát của Bản đồ đó, nơi sinh sống của Động vật và Người ăn xin; trong toàn bộ đất nước không có di tích nào khác của môn Địa lý.
Dốc
Công việc đặc tả được cho là khó hơn so với viết mã. Thông thường các lý do chúng tôi viết tài liệu đặc tả trước khi thực hiện công việc là để khuyến khích xem dự án qua lăng kính chiêm nghiệm và phê phán, bởi vì một khi quá trình mã hóa bắt đầu, chúng ta chuyển số và bị thúc đẩy bởi xu hướng hành động.
Vậy tại sao tôi lại nói đây là quan niệm sai lầm:
Quan niệm sai lầm thứ 2: công việc đặc tả phải chu đáo hơn công việc viết mã làm việc
Vấn đề là sự chu đáo này không còn là điều chúng ta có thể làm được nữa. được coi là đương nhiên nhờ sự thúc đẩy của ngành nhằm giảm và giảm giá trị lao động tại các công ty công nghệ. Khi bạn bắt đầu từ tiền đề "Tôi đã nói với mọi người công việc đặc tả sẽ dễ dàng hơn việc viết mã" thì bạn hãy tự thiết lập cho mình thất bại. Không có cách nào bạn có thể làm được công việc khó khăn và không thoải mái này việc viết thông số kỹ thuật đó yêu cầu nếu bạn tối ưu hóa tốc độ phân phối. Đó là làm thế nào bạn có được thứ gì đó giống như "thông số kỹ thuật" của Symphony trông bề ngoài giống như một tài liệu đặc tả nhưng sau đó được tách ra một cách chặt chẽ hơn sự xem xét kỹ lưỡng.
Trên thực tế, thông số kỹ thuật của Symphony được hiểu là sai sót do AI viết. Mục 10.5 là một ví dụ đặc biệt nghiêm trọng về sự cẩu thả mà tôi đang nói đến, chẳng hạn như thế này đoạn trích:
Hợp đồng gia hạn tuyến tính_graphql:
-
Mục đích: thực hiện truy vấn GraphQL thô hoặc đột biến đối với Tuyến tính bằng cách sử dụng cấu hình của Symphony xác thực trình theo dõi cho phiên hiện tại.
-
Tính khả dụng: chỉ có ý nghĩa khi
tracker.kind == "tuyến tính"và xác thực tuyến tính hợp lệ được định cấu hình. -
Hình dạng đầu vào ưa thích:
{ "truy vấn": "tài liệu đột biến hoặc truy vấn GraphQL đơn lẻ", "biến": { "tùy chọn": "đối tượng biến graphql" -
truy vấnphải là một chuỗi không trống. -
truy vấnphải chứa chính xác một thao tác GraphQL. -
biếnlà tùy chọn và nếu có thì phải là đối tượng JSON. -
Việc triển khai có thể chấp nhận thêm chuỗi truy vấn GraphQL thô dưới dạng đầu vào tốc ký.
-
Thực hiện một thao tác GraphQL cho mỗi lệnh gọi công cụ.
-
Nếu tài liệu được cung cấp chứa nhiều thao tác, hãy từ chối lệnh gọi công cụ do đầu vào không hợp lệ.
-
Lựa chọn
OperationNamecố tình nằm ngoài phạm vi của tiện ích mở rộng này. -
Sử dụng lại điểm cuối tuyến tính đã định cấu hình và xác thực từ cấu hình thời gian chạy/quy trình làm việc Symphony đang hoạt động; làm không yêu cầu tác nhân mã hóa đọc mã thông báo thô từ đĩa.
-
Ngữ nghĩa kết quả của công cụ:
- truyền tải thành công + không có
lỗiGraphQL cấp cao nhất ->success=true - Có
lỗiGraphQL cấp cao nhất ->success=false, nhưng vẫn giữ nguyên nội dung phản hồi GraphQL để gỡ lỗi - đầu vào không hợp lệ, thiếu xác thực hoặc lỗi truyền tải ->
success=falsevới tải trọng bị lỗi
- truyền tải thành công + không có
-
Trả về phản hồi GraphQL hoặc tải trọng lỗi dưới dạng đầu ra của công cụ có cấu trúc mà mô hình có thể kiểm tra trong phiên.
Đó là một tập hợp các câu "có hình dạng đặc tả" đọc giống như một câu Sản phẩm công việc của đại lý: thiếu sự mạch lạc, mục đích hoặc sự hiểu biết về bức tranh lớn hơn.
Một tài liệu đặc tả như thế này nhất thiết phải cẩu thả , ngay cả khi nó được tác giả bởi con người vì họ đang tối ưu hóa thời gian giao hàng thay vì hơn là sự mạch lạc hoặc rõ ràng. Trong môi trường kỹ thuật hiện nay, chúng ta không còn có thể chấp nhận rằng các thông số kỹ thuật là sản phẩm của sự suy nghĩ cẩn thận và cân nhắc.
Kết luận
Thông số kỹ thuật không bao giờ nhằm mục đích tiết kiệm thời gian. Nếu bạn là tối ưu hóa thời gian giao hàng thì tốt hơn hết bạn nên viết mã trực tiếp thay vì thông qua tài liệu đặc tả trung gian.
Tổng quát hơn, nguyên tắc "rác vào, rác ra" được áp dụng ở đây. Ở đó không phải là thế giới nơi bạn nhập một tài liệu thiếu rõ ràng và chi tiết và nhận được một tác nhân mã hóa để điền vào sự rõ ràng và chi tiết còn thiếu một cách đáng tin cậy. Mã hóa các đặc vụ không phải là người đọc suy nghĩ và ngay cả khi có thì họ cũng không thể làm được gì nhiều nếu suy nghĩ của chính bạn đang bối rối.
Chú thích cuối trang
-
Nếu bạn thắc mắc tại sao đoạn mã không được đánh dấu thì đó là vì Tôi đang giữ lại phần đánh dấu mang hương vị GitHub chính xác như tôi đã tìm thấy trong tài liệu gốc. Tất cả các đoạn mã đều được chú thích rõ ràng dưới dạng
text(một trong nhiều dấu hiệu cho thấy tài liệu này được tạo bởi AI). Trên thực tế, đây là có thể là một ví dụ về mô hình tuân theo chữ viết hơn là tinh thần của yêu cầu. Tôi khá chắc chắn điều đã xảy ra là con người đã yêu cầu AI chuyển đổi một bản thảo ban đầu của dự án thành một bản đặc tả văn xuôi và AI đã quyết định rằng gắn nhãn các đoạn mã làvăn bảnkhiến chúng giống văn xuôi hơn là mã. ↩ -
Mọi người thường nói với tôi "bạn sẽ nhận được kết quả tốt hơn nếu bạn tạo mã bằng một ngôn ngữ chính thống hơn là Haskell" mà tôi trả lời: nếu đại lý gặp khó khăn khi tạo mã Haskell thì điều đó cho thấy các đại lý không có khả năng khái quát hóa một cách đáng tin cậy ngoài dữ liệu đào tạo của họ. ↩
-
Bạn cũng có thể tự mình thử bài tập tương tự này nếu bạn cho rằng tôi đã làm được sai. ↩
Tác giả: signa11