請確保 AI 如何改變您的程式碼庫,是經過深思熟慮的。

隨著 AI 程式碼代理編寫的程式碼越來越多,我們比以往任何時候都更需要對其編寫的程式碼有明確的意圖。

「唯一比一個程式碼代理更快地搞亂程式碼庫的東西,就是一群程式碼代理。」

本文旨在作為一份宣言和指南,為所有與 AI 程式碼代理協作的人類提供關於 AI 編寫的程式碼應如何呈現的指導。它也可作為您可以賦予 AI 代理的一項技能。

您如何將邏輯分割成函數以及塑造它們傳遞的資料,決定了程式碼庫隨著時間的推移能保持多好。

語意函數是任何程式碼庫的建構塊,一個好的語意函數應盡可能最小化,以優先考慮其正確性。語意函數應接收完成其目標所需的所有輸入,並直接返回所有必要的輸出。語意函數可以包裝其他語意函數,以描述期望的流程和用法;作為程式碼庫的建構塊,如果存在普遍使用且定義良好的複雜流程,請使用語意函數將其編碼。

副作用通常在語意函數中是不受歡迎的,除非它們是明確的目標,因為語意函數應該是安全的,可以在不了解其內部機制的情況下重複使用,只要它們聲稱的功能即可。如果邏輯很複雜,並且在一個大的流程中不清楚它做了什麼,一個好的模式是將該流程分解成一系列自我描述的語意函數,這些函數接收它們所需的內容,返回下一步所需的資料,並且不執行任何其他操作。好的語意函數的例子包括 quadratic_formula() 到 retry_with_exponential_backoff_and_run_y_in_between < Y: func, X: Func > (x: X, y: Y)。即使這些函數再也不被使用,未來的人類和代理在審查程式碼時也會欣賞資訊的索引。

語意函數不應該需要任何註解,程式碼本身應該是其功能的一個自我描述的定義。語意函數理想情況下應該極易於單元測試,因為一個好的語意函數是一個定義良好的函數。

實用函數應作為一系列語意函數和獨特邏輯的包裝器。它們是您程式碼庫中的複雜流程。在製作生產系統時,邏輯變得混亂是很自然的,實用函數就是這些的組織方式。這些函數通常不應在超過幾個地方使用,如果有的話,請考慮將顯式邏輯分解並移入語意函數。例如 provision_new_workspace_for_github_repo(repo, user) 或 handle_user_signup_webhook()。測試實用函數屬於整合測試的範疇,並且通常在測試整個應用程式功能的背景下進行。預期實用函數會隨著時間完全改變,從其內部到它們所做的事情。為此,在它們上方放置文件註解是很有幫助的。避免重述函數名稱或其明顯的特徵,而是記錄意外的事情,例如「在餘額少於 10 時提前失敗」,或反駁來自函數名稱的其他誤解。作為文件註解的讀者,請對它們持保留態度,函數內的程式設計師可能忘記更新它們,當您認為它們可能不正確時,最好進行事實核查。

您的資料結構應該使錯誤的狀態不可能發生。如果一個模型允許實際上永遠不應同時存在的欄位組合,那麼該模型就沒有盡到其職責。每一個可選欄位都是程式碼庫每次觸及該資料時必須回答的問題,而每一個鬆散類型的欄位都是呼叫者傳入看起來正確但實際上不正確內容的邀請。當模型強制執行正確性時,錯誤會在建構點浮現,而不是在某些不相關流程的深處,那裡的假設最終崩潰。模型的名稱應該足夠精確,以便您可以查看任何欄位並知道它是否屬於該模型——如果名稱沒有告訴您,那麼該模型就試圖做太多事情。當兩個概念經常一起需要但又是獨立的時,請組合它們而不是合併它們——例如 UserAndWorkspace { user: User, workspace: Workspace } 保留了兩個模型完整,而不是將工作區欄位扁平化到使用者中。像 UnverifiedEmail、PendingInvite 和 BillingAddress 這樣的好名稱可以精確地告訴您哪些欄位屬於。如果您在 BillingAddress 中看到 phone_number 欄位,您就知道出了問題。

具有相同形狀的值可以代表完全不同的領域概念:{ id: " 123 " } 在一個地方可能是 DocumentReference,在另一個地方可能是 MessagePointer,如果您的函數只接受 { id: String },程式碼將在不抱怨的情況下接受其中任何一個。品牌類型通過用一個獨特的類型包裝一個原始類型來解決這個問題,這樣編譯器就會將它們視為獨立的:DocumentId(UUID) 而不是裸 UUID。有了品牌類型,意外地交換兩個 ID 就會變成語法錯誤,而不是一個潛伏在三層之下的靜默錯誤。

當一個語意函數為了方便而變成一個實用函數,然後程式碼庫中依賴它的其他地方最終做了它們不打算做的事情時,通常會發生中斷。為了解決這個問題,在創建函數時要明確,通過命名它,不是根據它做了什麼,而是根據它在哪裡被使用。它們名稱的性質應該讓其他程式設計師在名稱中清楚地知道,它們的行為沒有被嚴格定義,不應該依賴它們的內部來執行確切的任務,並使調試它們的迴歸更容易。

模型以相同的方式但更慢地崩潰。它們一開始是專注的,然後有人添加「再多一個」可選欄位,因為這比創建一個新模型更容易,然後另一個人也這樣做,最終該模型變成一個鬆散的半相關資料集合,每個消費者都必須猜測哪些欄位被設置了以及為什麼。名稱不再描述資料是什麼,欄位不再圍繞單一概念聚集,並且每個觸及該模型的が新功能都必須導航它從未被設計來表示的狀態。當模型的欄位不再圍繞其名稱聚集時,這就是將其拆分成它一直耦合在一起的獨立事物的信號。

我相信這些模式會帶來可擴展的程式碼庫,而可擴展的程式碼庫會帶來更快的迭代週期和更好的軟體。或者,也許我已經過時了。

A human coder who cares about good code