1. bcachefs 編碼風格

好的開發就像園藝,程式碼庫就是我們的花園。每天照料它們;尋找那些不合時宜或需要整理的小細節。一點點修剪就能帶來很大的改善;不要等到事情失控才動手。

事情不總是需要完美——吹毛求疵往往弊大於利。但當你看到美時,要欣賞它——並讓人們知道。

你不敢觸碰的程式碼,就是最需要重構的程式碼。

零星的整理也能帶來很大的幫助。

認真思考如何組織事物。

好的程式碼是可讀的程式碼,其結構簡單,不給bug留下任何藏身之處。

斷言是我們編寫可靠程式碼最重要的工具之一。如果在編寫補丁集的過程中,你遇到一個不應該發生的條件(如果發生會導致不可預測或未定義的行為),或者你不確定它是否會發生,也不確定如何處理——就讓它成為 BUG_ON()。不要讓未定義或未指定的行為潛伏在程式碼庫中。

到你完成補丁集時,你應該更清楚哪些斷言需要處理並轉換為帶有錯誤路徑的檢查,以及哪些應該是邏輯上不可能的。對於邏輯上不可能的斷言,保留 BUG_ON()。(或者,如果它們開銷很大,可以將它們設為除錯模式斷言——但不要將所有東西都變成除錯模式斷言,這樣我們就不會在事實證明你錯了時陷入除錯未定義行為的困境)。

斷言是不會過時的文件。好的斷言是極好的。

好的斷言能顯著且大幅地減少發現bug所需的測試量。

好的斷言基於狀態而非邏輯。要編寫好的斷言,你必須思考你的狀態有哪些不變數。

好的不變數和斷言在你的程式碼庫中隨處可見。這意味著你可以在提交版本中只在少數地方執行它們,但是如果你需要除錯導致斷言失敗的問題,你可以迅速地在各處散佈它們,以找到破壞不變數的程式碼路徑。

一個好的斷言檢查的是編譯器本可以為我們檢查並消除的內容——如果我們使用的語言帶有編譯器可以檢查的嵌入式正確性證明。這種技術今天已經存在,但要將其引入系統程式語言可能還需要幾十年。但我們仍然可以在程式碼中融入這種思維,並透過執行時檢查來記錄不變數——就像使用動態型別語言的人可能會新增型別註解,逐步使其程式碼變為靜態型別一樣。

尋找使斷言更簡單、更高階的方法,往往會促使你使整個系統更簡單、更健壯。

好的程式碼是你可以深入探索並瞭解其執行情況的程式碼——即內省。如果我們看不到正在發生什麼,我們就無法除錯任何東西。

每當我們除錯時,如果解決方案不那麼顯而易見,並且問題在於我們因為看不到正在發生什麼而不知道問題出在哪裡——那首先要解決的就是這個問題。

我們擁有能在執行時高效地使一切可見的工具——其中包括 RCU 和 percpu 資料結構。不要讓事物保持隱藏。

內省最重要的工具是簡單的美化印表機——在 bcachefs 中,這意味著 *_to_text() 函式,它們將輸出到 printbufs。

美化印表機非常出色,因為它們可以組合使用,並且你可以隨處使用它們。擁有列印你正在處理的任何物件的功能,將使你的錯誤訊息更容易編寫(因此它們將實際存在)並且更具資訊量。它們可以從 sysfs/debugfs 以及跟蹤點中使用。

執行時資訊和除錯工具應該附帶清晰的描述和標籤,並具有良好的結構——我們不希望像 procfs 中那樣,檔案裡只是一堆裸整數列表。除錯工具的一部分作用是教育使用者和新開發者瞭解系統的工作原理。

錯誤訊息應儘可能地告訴你除錯問題所需的一切。值得為此付出努力。

跟蹤點不應該是你首先想到的工具。它們是一個重要的工具,但要始終尋找更直接的方式讓事物可見。當我們不得不依賴跟蹤時,我們必須知道要尋找哪個跟蹤點,然後執行有問題的負載,接著篩選日誌。當用戶遇到問題時,這需要經歷很多步驟,如果問題是間歇性的,甚至可能無法實現。

不起眼的計數器是一個極其有用的工具。它們便宜且易於使用,許多複雜的內部操作,特別是那些可能行為異常的操作(例如涉及記憶體回收的任何操作),一旦在每個不同的程式碼路徑上都有計數器,就會變得出奇地容易除錯。

持久化計數器甚至更好。

除錯時,儘量從遇到的每一個bug中獲得最大收益;不要急於修復最初的問題。尋找能讓下一次處理相關bug變得更容易的方法——例如內省、新的斷言、更好的錯誤訊息、新的除錯工具,並首先完成這些。尋找使系統表現更好的方法;通常一個bug會透過下游效應揭示出其他幾個bug。

首先修復所有這些,最後再解決最初的bug——即使這意味著讓使用者等待。從長遠來看,他們會感謝你,當他們理解你正在做什麼時,你會驚訝於他們是多麼樂意耐心等待。使用者樂於助人——否則他們一開始就不會報告bug了。

與你的使用者交流。不要孤立自己。

使用者會注意到各種有趣的事情,僅僅透過與他們交流和互動,你就能從他們的經驗中獲益。

花時間做支援和幫助臺工作。不要只寫程式碼——程式碼只有在無故障使用時才算完成。

這也會激勵你儘可能地改進你的除錯工具,甚至你的文件。就像生活中的其他事情一樣,你投入的時間越多,你就會做得越好,而作為開發人員的你,是最有能力改進工具以使除錯變得快速和輕鬆的人。

警惕你如何承擔並致力於大型專案。不要讓開發變得以產品經理為中心。很多時候一個想法是好的,但需要等待合適的時機——然而,在你開始編寫程式碼之前,你不會知道一個想法是否到了合適的時機。

要有扔掉很多東西,或者把它們半途而廢留待日後再處理的心理準備。沒有人能寫出所有完美並全部交付的程式碼,如果你能及早注意到這一點並轉向其他事情,從長遠來看你會更有效率。所獲得的經驗和教訓對你所做的所有其他工作都將是寶貴的。

但不要害怕處理那些需要對現有程式碼進行大量返工的專案。有時這些專案是最好的,因為它們可以引導我們使現有程式碼更通用、更靈活、更多功能,或許也更健壯。如果一個想法看起來會把事情搞得一團糟,就不要猶豫放棄它。

複雜的功能通常可以透過一系列重構來完成,而最終實際實現該功能的更改在最後可能只是一個很小的補丁。當這種情況發生時真是太好了,特別是當這些重構本身就能改進程式碼庫時。這樣一來,即使你想要實現的功能最終未能成功,浪費的努力也大大減少。

始終努力增量工作。始終努力將大型專案分解為可以證明自身價值的小型專案。

與其總是處理那些大型專案,不如尋找那些有用的小事情,讓大型專案變得更容易。

什麼可能有用這個問題是初級開發者最常走偏的地方——做一些看起來有用的事情往往會導致過度工程化。瞭解什麼有用來自於多年的經驗,或者與有經驗的人交流——或者僅僅是透過閱讀大量程式碼並尋找常見模式和問題。不要害怕扔掉東西,做一些更簡單的事情。

與你的同事開發者討論你的想法;很多時候,最好的想法來自於輕鬆的對話,人們在其中不害怕說“如果……會怎樣?”。

不要忽視你的工具。

最重要的工具(除了編譯器和我們的文字編輯器)是我們用於測試的工具。最短的編輯/測試/調試周期對於高效工作至關重要。我們透過執行程式碼並觀察結果來學習、積累經驗並發現思維中的錯誤。如果你的時間因為工具不好或太慢而被浪費——不要接受,去修復它。

在你的文件、提交資訊和程式碼註釋上投入精力——但不要過分。一個好的提交資訊很棒——但如果資訊重要到可以放在提交資訊中,請問問自己,它作為程式碼註釋是否會更好。

一個好的程式碼註釋很棒,但更好的是那些不需要存在的註釋,因為程式碼非常直截了當,顯而易見;組織成小巧、乾淨、整潔的模組,函式和變數名稱清晰且具有描述性,每行程式碼都有明確的目的。