
阿里妹導讀
入職一年後再讀到這本書其實有些晚了,每一章節幾乎都是在這一年的工作中所經歷和摸索過後才建立的認知,每一章外延為單獨的一本書大概才能成體系地深入學習,作者也確實每一章的末尾都做了相關閱讀推薦。
按「道法術器」的角度看,這本書講的應該更多是「法」和「術」的東西,雖然也可以被理解為是叭叭一堆“正確的廢話”,但只要是正確的就是值得輸入的,哪怕最終對自己的幫助和影響只有分毫。
此外不得不提的是,本書的翻譯實在是過於“簡單直接”,和Google機翻也大致無二了,儘可能還是閱讀英文原版吧。
書名:程式設計師的README作者:克里斯·里科米尼 德米特里·里亞博伊譯者:付裕出版社:人民郵電出版社有限公司出版時間:2023-07-01ISBN:9787115599438品牌方:人民郵電出版社有限公司

第1章 前面的旅程
入門工程師晉級需要具備的核心領域能力:
-
技術知識
-
執行力
-
溝通能力
-
領導力
新手期過程:
1. 新人入職:熟悉公司、團隊、本職工作。參加入職會議,設定開發環境,弄清楚團隊的常規流程和會議,閱讀文件並向同事請教,參加入職培訓,瞭解組織架構。建議在團隊中用文件記錄下會議的內容、入職流程和其他口口相傳的東西,重點不是要寫一份完美的文件,而是要寫得足夠多,以引發討論、充實細節。被分配到一個小任務,學習如何修改程式碼並安全釋出到生產環境,如果沒有就要主動要求承接小需求。這些改動一定要小,重點是去了解那些規範步驟。
2. 成長試煉:為團隊開始分擔真正的工作,熟悉程式碼庫,瞭解編譯、測試和部署程式碼的流程,多提問,多拉程式碼評審。持續學習,多參加技術講座、閱讀小組、導師計劃。參加迭代會議、評審會議、專案會議,瞭解團隊的產品全貌和路線規劃。與管理者建立聯絡,瞭解他們的工作風格和期望,並在一對一溝通中表達自己的目標。
3. 貢獻價值:提升自己編寫生產級程式碼的能力。主動幫助隊友,參與到程式碼評審。提升自己交付和運維的水平,包括監控、日誌、跟蹤、除錯。獨立負責一個小專案,撰寫技術設計文件並幫助團隊進行專案規劃。在系統架構、專案程式碼中看到不足,進行必要的維護和重構。參與到團隊計劃當中,與管理者一同制定目標或OKR,主動表達自己的想法和規劃,並在績效評估中獲得反饋。
坎寧安定律 (Cunningham’s Law):The best way to get the right answer on the Internet is not to ask a question, it's to post the wrong answer. 在談話中,我們也能利用“說錯話”來激發更多的對話。
腳踏車棚效應 (Bike-shed Effect) / 帕金森瑣碎定律(Parkinson's Law of Triviality):A common phenomenon in group decision-making where people give disproportionate weight to trivial issues. 一個被指派到發電廠對該發電廠的設計方案進行評審的委員會,因為發電廠的設計方案過於複雜,以至於無法討論出什麼實際的內容,所以他們花了幾分鐘就批准了這些計劃。然後,他們又花了45分鐘來討論發電廠旁邊的腳踏車棚的材料問題。
第2章 步入自覺階段
學習如何學習:
-
前置學習:入職後學習開發流程和專案知識,參與設計討論、On-Call輪換、解決運維問題和評審程式碼。 -
在實踐中學習:上手編寫併發布程式碼,適當謹慎,不要過於害怕造成問題。 -
執行程式碼:在生產環境外執行專案,多除錯,多打日誌。 -
閱讀:團隊文件、設計文件、程式碼、積壓的tickets、書籍、論文和技術網站。 -
觀看講座:觀看教程、技術講座和閱讀會議簡報。 -
適度地參加會議和聚會:學術會議、草根興趣小組聚會和科技展覽會,偶爾參加。 -
跟班學習並同有經驗的工程師結對:在另一個人執行任務時跟著他,他做筆記並提出問題;結對程式設計(pair programming),兩名工程師一起寫程式碼,輪流程式設計。 -
用副業專案實踐:貢獻導向,參與開源專案;興趣導向,找到有興趣解決的問題,用想學習的工具來解決。
提出問題:
-
嘗試自己尋找答案:不要直接請教同事,可以嘗試網際網路、文件、內部論壇、聊天記錄、原始碼等地方。 -
設定時間限制:設定好研究一個問題預期花費的時間,防止收益遞減。 -
記錄全過程:向別人提出問題時,要描述背景、自己的嘗試和發現。 -
別打擾:注意提問的時機,不要打擾別人工作的專心狀態。 -
多用“非打擾式”交流:組播(multicast)是指將訊息傳送到一個組而不是個人目標;非同步(asynchronous)是指可以稍後處理訊息,而不需要立即響應。即多使用群聊,郵件或論壇。 -
批次處理你的同步請求:面對面的交流是“高頻寬”和“低延遲”的,所以在聊天和郵件外,預約時間或安排會議集中處理問題更好,但事前要做好功課,事中要多記錄,事後要有反饋。
克服成長的障礙:
-
冒充者綜合徵:覺知(awareness),意識到是自己主動在尋找“冒充”的證據,而自己是真真切切取得了成就;重塑(awareness),把悲觀的、消極的自我暗示轉化為樂觀的、積極的自我鼓勵;反饋(feedback),與導師、敬重的同事交流,明確自己做得怎麼樣。 -
鄧寧-克魯格效應:有意識地培養好奇心;對犯錯持開放態度;向受人尊敬的工程師取經;討論設計決策並傾聽建議;培養一種權衡利弊而不是非黑即白的心態。
推薦閱讀:
《軟體開發者路線圖:從學徒到高手》(Apprenticeship Patterns: Guidance for theAspiring Software Craftsman) 《你要做的全部就是提問:如何掌握成功最重要的技能》(All You Have toDo Is Ask:How to Master the Most Important Skill forSuccess) 《解析極限程式設計——擁抱變化》(ExtremeProgramming Explained: Embrace Change) 《高能量姿勢:肢體語言打造個人影響力》(Presence: Bringing Your Boldest Self to Your BiggestChallenges)
馬丁·M.布羅德威爾在其文章《為學而教》(“Teaching forLearning”)中定義了能力的4個階段:“無意識的無能力”(unconscious incompetence)、“有意識的無能力”(consciousincompetence),“有意識的有能力”(conscious competence)和“無意識的有能力”(unconscious competence)。具體說來,無意識的無能力意味著你無法勝任某項任務,並且沒有意識到這種差距。有意識的無能力意味著你雖然無法勝任某項任務,但其實已經意識到了其中的差距。有意識的有能力意味著你有能力透過努力完成某項任務。最後,無意識的有能力意味著你可以很輕鬆地勝任某項任務。
冒充者綜合徵(冒名頂替症候群,Impostor syndrome):a behavioral health phenomenon described as self-doubt of intellect, skills, or accomplishments among high-achieving individuals.
鄧寧-克魯格效應(Dunning-Kruger effect):a cognitive bias in which people with limited competence in a particular domain overestimate their abilities.
第3章 玩轉程式碼
軟體的熵(software entropy):混亂的程式碼是變化的自然副作用,不要把程式碼的不整潔歸咎於開發者,這種走向無序的趨勢是必然的。
技術債(technical debt):
-
定義:為了修復現有的程式碼不足而欠下的未來工作,是造成軟體的熵的主要原因之一。與金融債務一樣,技術債也有“本金”和“利息”,本金是那些需要修復的原始不足,利息是隨著程式碼的發展沒有解決的潛在不足。因為實施了越來越複雜的變通方法,隨著變通辦法的複製和鞏固,利息就會增加,複雜性蔓延開來,就會造成bug。 -
技術債矩陣: -
謹慎的、有意的:在程式碼的已知不足和交付速度之間進行務實的取捨,團隊要有規劃地解決。
-
魯莽的、有意的:在團隊面臨交付壓力的情況下產生的,“就…”,“只是…”這樣的形容就是魯莽債務。
-
魯莽的、無意的:不知道自己不知道,需要透過做方案評審、程式碼評審和持續學習來最大限度減少。
-
謹慎的、無意的:成長經驗積累的自然結果,有些教訓只有在事後才會被吸取。 -
解決: -
不要等到世界都停轉一個月了才去解決問題,要邊做邊解決,著手去做小幅的重構。 -
有時增量重構還不夠,需要大型的重構。短期看,償還技術債會拖慢交付特性的速度,而承擔更多的技術債會加速交付,但長期看情況則相反,平衡點在很大程度上取決於環境。如果有大規模重構或重寫某些模組的建議,可以向團隊說明情況:按事實陳述情況,描述技術債的風險和成本,提出解決方案,討論備選方案(不採取行動也是備選之一),權衡利弊。要注意的是以書面形式提出建議,不要把呼籲建立在價值判斷上(“這段程式碼又老又難看”),要足夠具體,將重點放在技術債的成本和修復它帶來的好處上。
變更程式碼:
-
善於利用現有程式碼:定位需要改變的程式碼即變更點並閱讀,然後找到測試點即修改程式碼的入口,這也是測試用例需要呼叫和注入的區域。有時候不得不打破現有依賴結構,比如拆分方法、引入介面,這可以更方便地編寫測試用例。 -
過手的程式碼要比之前更乾淨:在不影響整個專案持續運轉的情況下要持續地重構工程,這樣重構的成本就會平攤在多次的版本更迭中。 -
做漸變式的修改:不要一次提交就“翻天覆地”,保持每次重構程式碼的體量很小,要儘量將重構程式碼的提交和新特性的提交分開。 -
對重構要務實:團隊的工作有優先事項和交付時效,重構需要花費時間,成本也可能超過其價值。正在被替換的舊的、廢棄的程式碼,低風險或很少被觸及的程式碼,都是不需要重構的。 -
善用IDE:重構時多使用IDE的功能,比如抽取方法和欄位、更新方法簽名等。 -
善用Git:儘早並頻繁地commit,但一定要規範自己的commit message,要壓縮但清晰。
避坑指南:
-
保守一些的技術選型:新技術的問題是它不太成熟,缺乏成熟的穩定性、相容性、框架、文件、社群。只有新技術的收益超過其成本時,才值得考慮。 -
不要特立獨行:不要因為個人喜好就忽視公司或行業標準,自定義的方案必然會付出代價。 -
不要只fork而不pr:沒有及時貢獻到上游程式碼庫的小變更也會隨著時間的推移而變得複雜。 -
剋制重構的衝動:把重構看作最後的手段,因為其成本和風險都巨大,但又很難評估價值。
推薦閱讀:
《修改程式碼的藝術》(Working Effectively with Legacy Code) 《處理遺留程式碼的工具箱:軟體專業人員處理遺留程式碼的專業技能》(The Legacy Code Programmer’sToolbox: Practical Skills for Software Professionals Workingwith Legacy Code) 《重構:改善既有程式碼的設計》(Refactoring: Improving the Design of Existing Code) 《人月神話》(The Mythical Man-Month)
本·霍洛維茨在他的《創業維艱:如何完成比難更難的事》(TheHard Thing About Hard Things)一書中說:任何技術創業公司必須做的主要的事情是建立一個產品,這個產品在做某件事情時至少要比目前流行的方式好十倍。兩倍或三倍的改進不足以讓人們快速或大量地轉向新事物。
丹·麥金利在他的演講“選擇保守的技術”中指出“在保守技術上出現的故障模式很好理解”。所有的技術都會發生故障,但舊的東西以可預測的方式發生故障,新東西往往會以令人驚訝的方式發生故障。
弗雷德里克·布魯克斯在他的名作《人月神話》(The Mythical Man-Month)中創造了“第二系統綜合徵”這一短語,描述了簡單系統如何被複雜系統所取代。第一個系統的範圍是有限的,因為它的創造者並不瞭解可能會出問題的地方。這個系統完成了它的工作,但它是笨拙的和有限的。現在有經驗的開發者清楚地看到了他們的問題所在,他們開始用他們的一切聰明才智來開發第二個系統。新系統是為靈活性而設計的,所有東西都是可配置和可注入的。可悲的是,第二個系統通常是一個臃腫的爛攤子。
第4章 編寫可維護的程式碼
防禦式程式設計:
-
避免空值:使用空物件模式(null object pattern)或可選型別(option type)來避免空指標異常;使用@NotNull註解替代手動空值檢查讓程式碼更乾淨。 -
保持變數不可變:將變數宣告為不可變可以防止意外修改,比如Java的final。 -
使用型別提示和靜態型別檢查器:限制變數可以被賦的值。 -
驗證輸入:永遠不要相信你的程式碼接收的輸入,一定要進行校驗。 -
善用異常:不要使用特殊的返回值來標識錯誤型別(如null、0、−1等),儘量使用異常,它可以被命名,有堆疊跟蹤和錯誤資訊。 -
異常要有精確含義:儘可能地使用內建的異常,避免建立通用的異常。使用異常處理來應對故障,而不是控制應用程式的執行邏輯。 -
早拋晚捕:“早拋”意味著在儘可能接近錯誤的地方引發異常,這樣開發人員就能迅速地定位相關的程式碼。“晚捕”意味著在呼叫的堆疊上傳播這個異常,直到你到達能夠處理異常的程式的層級。 -
智慧重試:單純的重試方法是捕捉到一個異常馬上就進行重試,但可能會影響系統性能;謹慎的做法是backoff,非線性地增加休眠時間(通常使用指數如(retry number)^2);不要盲目地重試所有失敗的呼叫,最好是讓應用程式在遇到其在設計時沒有預想到的錯誤時崩潰,即fail-fast。 -
構建冪等系統:處理重試的最好方法是構建冪等系統,一個冪等的操作是可以被進行多次並且仍然產生相同結果的操作,比如往一個Set裡新增一個值,客戶端單獨為每個請求提供一個唯一ID。
10. 及時釋放資源:故障發生後,要確保清理所有的資源,釋放你不再需要的記憶體、資料結構、Socket和檔案控制代碼。
日誌:
-
給日誌分級:透過全域性配置和對包或類級別的覆寫來控制,設定後所有處於該級別或高於該級別的日誌都會被髮出來,常見分級有TRACE、DEBUG、INFO、WARN、ERROR、FATAL。 -
日誌的原子性:原子日誌就是指在一行訊息中包含所有相關的資訊,不能原子化輸出時考慮在每一條日誌中加上Trace ID。 -
關注日誌效能:用引數化的日誌輸入及非同步附加器避免日誌對效能的損害。 -
不要記錄敏感資料:日誌資訊不應該包括任何私人資料,如密碼、安全令牌、信用卡號碼或電子郵件地址。
系統監控:
-
常見系統指標形式:計數器、儀表盤和直方圖(百分比),彙總到一個集中式視覺化系統,在聚合指標之上提供面板和監控工具,跟蹤SLO(service level objective,服務等級目標),根據指標值觸發警告,或擴容縮容。 -
使用標準的監控元件:不要推出你自己的系統指標庫,非標準庫是“維護噩夢”,而標準庫可以與其他一切“開箱即用”的東西整合。 -
測量一切:監測的效能開銷很低,要儘可能地監測各種資料結構、操作和行為,比如資源池(執行緒池、連線池)的大小、快取的命中數和失誤數、資料結構的大小、CPU密集型操作的效能開銷、I/O密集型操作的處理時間、異常和錯誤的數量、遠端請求和響應的數量和耗時。
跟蹤器:
-
堆疊跟蹤:異常。 -
分散式呼叫跟蹤:Trace ID。
配置相關注意事項:
-
配置的形式和方式:形式有JSON或YAML檔案,環境變數,命令列引數,領域特定語言(DSL),應用程式所使用的語言;方式可以是靜態配置或動態配置。 -
記錄並校驗所有的配置:在程式啟動時立即記錄所有(非秘密的)配置,並載入配置的值時立即對其進行型別和邏輯意義的校驗。 -
不要玩花活:配置方案越聰明,bug就越奇怪,儘量使用最簡單、有效的方法,理想狀態應該是單一標準格式的靜態配置檔案;如果不得不使用變數替換、條件配置或動態配置,也要日誌跟蹤配置的變化和記錄實際生效的配置值。 -
提供預設值:提供良好的預設值能讓應用開箱即用,避免使用者上手就配置大量引數。 -
給配置分組:可以使用YAML的巢狀將相關屬性分組。 -
配置即程式碼(configuration as code,CAC):配置應該受到與程式碼同樣嚴格的要求,要進行版本控制、評審、測試、構建和釋出。 -
保持配置檔案清爽:刪除不使用的配置,使用標準的格式和間距,不要盲目地從其他檔案中複製配置。 -
不要編輯已經部署的配置:避免在生產環境中手動編輯某臺特定機器上的配置檔案。
工具集:
-
多瞭解並儘量使用公司或團隊的通用運維工具。 -
如果是自己編寫的指令碼,一定要注意規範和測試,有價值的工具可以嘗試抽象為共享庫或服務。
推薦閱讀:
《程式碼大全:軟體構造之實踐指南》(CodeComplete: A Practical Handbook of Software Construction) 《程式碼整潔之道》(Clean Code: A Handbook of Agile Software Craftsmanship) 《Google系統架構解密:構建安全可靠的系統》(BuildingSecure & Reliable Systems:Best Practices for Designing, Implementing, and Maintaining Systems) 《SRE:Google運維解密》(Site Reliability Engineering: How Google Runs ProductionSystems)
第5章 依賴管理
依賴管理基礎知識:
-
相依性是指你的程式碼所依賴的程式碼,依賴關係是在軟體包管理或構建檔案中宣告的。 -
好的版本管理方案要有唯一性(版本不應該被重複使用),可比性(推斷版本的優先順序),資訊性(區分預釋出和正式釋出)。 -
語義版本管理(semantic versioning,SemVer):主版本號.次版本號.補丁版本號,還可以新增一個-來定義預釋出版本。 -
傳遞依賴:軟體包管理或構建檔案揭示了專案的直接依賴關係,直接依賴又依賴於其他類庫,形成依賴傳遞。
相依性地獄:
-
迴圈依賴:A依賴B,B依賴C,C依賴A。一個庫間接性地依賴它自己 -
鑽石依賴:A依賴B和C,B和C依賴D。一個底層庫被多個路徑依賴。 -
版本衝突:A直接或間接依賴了B的2個版本。一個專案中存在同一個庫的多個版本。
避免相依性地獄:
-
隔離依賴項:不必把依賴管理交給構建和打包系統,直接複製依賴相關的必要程式碼,Java還可以使用包路徑的區分來遮蔽依賴。 -
按需新增依賴項:將你使用的所有類庫顯式宣告為依賴項,不要使用來自橫向依賴的方法和類。 -
指定依賴項的版本:明確設定每個依賴項的版本號。 -
依賴範圍最小化:依賴範圍指在構建生命週期的編譯、執行、測試階段何時使用某個依賴,對每個依賴項指定儘可能精確的依賴範圍。 -
保護自己免受迴圈依賴的影響:使用構建工具檢測,不要引入迴圈依賴。
推薦閱讀:
SemVer的官方主頁 Python官方主頁的版本管理說明
帕累托法則(Pareto principle):也被稱為二八定律,由義大利經濟學家維爾弗雷多·帕累託在20世紀初提出,他發現義大利約有80%的土地由20%的人口所有。在程式設計領域,80%的軟體缺陷來源於程式碼中的20%部分;80%的效能提升可以透過最佳化20%的程式碼來實現;80%的開發時間被用來完成專案中20%的功能點。
第6章 測試
測試的多種用途:
-
檢查程式碼是否正常工作,驗證軟體的行為是否符合預期。 -
編寫測試迫使開發人員思考他們程式的介面和實現過程,儘早地暴露出笨拙的介面設計和混亂的實現過程。 -
測試程式碼可以是另一種形式的文件,是開始閱讀並瞭解一個新的程式碼庫的入口。
測試型別:
-
單元測試:測試某個單一的方法或行為。 -
整合測試:驗證多個元件整合在一起之後是否還能正常工作。 -
系統測試:驗證整個系統的整體執行情況。 -
效能測試:負載測試和壓力測試並監控系統性能。 -
驗收測試:驗證交付的軟體是否符合驗收標準。
測試工具:
-
模擬庫:用於單元測試,特別是面向物件的程式碼中,可以模擬外部依賴的系統、類庫或物件。 -
測試框架:管理測試的setup和teardown,管理測試執行和編排,提供工具,生成結果報告和程式碼覆蓋率。 -
程式碼質量工具:執行靜態分析並執行程式碼風格檢查,報告複雜度和測試覆蓋率等指標。
自己動手編寫測試:
-
編寫乾淨的測試:測試程式碼也要注意質量,專注於測試基本功能而不是實現細節,將測試的依賴項與常規程式碼的依賴項分開。 -
避免過度測試:編寫有意義的、對程式碼影響最大風險點的測試,不要為了覆蓋率而去寫無效測試。
測試中的確定性:
1. 確定性的程式碼對於相同的輸入總是給予相同的輸出;非確定性的程式碼對於相同的輸入可以返回不同的結果。測試中的不確定性降低了測試的價值,間歇性的測試失敗(被稱為拍打測試)是很難重現和除錯的,應該被停用或立即修復。
2. 隨機數生成器:可用一個常數作為隨機數生成器的種子,迫使每次執行拿到相同的隨機數。
3. 不要在單元測試中呼叫遠端系統:模擬依賴,規避網路的不穩定和遠端系統的不可靠,保證單元測試可移植。
4. 採用注入式時間戳:使用注入式時間戳而不是靜態時間方法now或sleep,控制程式碼在測試中獲取的時間。
5. 避免使用休眠和超時:sleep和超時都假設另一個執行執行緒會在特定時間結束,但可能會因為垃圾回收或作業系統導致意外,還會減慢測試執行過程。
6. 記得關閉socket和檔案控制代碼:避免系統資源的洩漏,可以多用try- with-resource,共享資源在setup和teardown方法中進行管理。
7. 繫結到0埠:測試不應該繫結到某個特定的網路埠,避免端口占用,要繫結到0埠允許作業系統去自動選擇。
8. 生成唯一的檔案路徑和資料庫位置:測試不應該寫入某一個已經被靜態定義好了的位置,要動態地生成唯一的檔名、目錄路徑以及資料庫或表名。
9. 隔離並清理剩餘的測試狀態:狀態存在於資料生命週期內的任何地方,全域性變數如計數器是常見的記憶體狀態,而資料庫和檔案是常見的磁碟狀態,共享資源都可能導致意外和干擾,測試結束都要重置記憶體狀態,清理磁碟狀態。
10. 不要依賴測試順序:特定的執行順序會讓測試變得難以除錯和驗證,改用setup在測試之間共享邏輯,teardown在測試結束後清理資料。
推薦閱讀:
《單元測試:原則、實踐與模式》(Unit Testing: Principles, Practices, and Patterns) 《測試驅動開發:實戰與模式解析》(Test-DrivenDevelopment: By Example) 《程式設計師修煉之道——從小工到專家》(The Pragmatic Programmer: From Journeyman to Master) 《探索吧!深入理解探索式軟體測試》(Explore It!: Reduce Risk and Increase Confidence with Exploratory Testing)
第7章 程式碼評審
為什麼需要評審程式碼:
-
捕捉bug並保持程式碼整潔。 -
團隊的教學和學習工具。 -
評審的comments也是一種文件。 -
保證安全和合規。
當你的程式碼被評審時:
-
準備工作:保持單個程式碼的小幅改動,將特性和重構工作分到不同的評審中,並寫出描述性的commit message,務必將註釋和測試包括在內。 -
用評審草案降低風險:在真正進行程式碼的編寫和評審之前,進行程式碼草案即大致設計和改造點的評審。 -
提交評審請勿觸發測試:透過提交程式碼評審來觸發持續整合的測試是一種浪費,CI環境應該只用於要釋出的程式碼。 -
預排大體量的程式碼修改:大體量程式碼修改的評審要提前做好預排,提前讓同事們瞭解設計文件和熟悉程式碼,演示時按照執行步驟來講解程式碼。 -
不要太在意:批評性的評論可能讓你很難接受,切記應該保持一些情感上的距離——這些評審意見是針對程式碼的,而不是針對你個人的,而且這甚至都不算是你的程式碼,這是整個團隊會擁有的程式碼。 -
保持同理心,但不要容忍粗魯:允許評審者懷疑,但如果他們的評論偏離了中心或粗暴無禮,要明確溝通,掃清障礙。 -
保持主動:不要羞於要求別人評審你的程式碼,收到評論時要有所回應。
評審別人的程式碼時:
1. 分流評審請求:根據程式碼的關鍵程度、變更的體量來分流評審。
2. 給評審預留時間:不要每次有評審需求時就中止你正在做的一切,最好能預留時間集中處理。
3. 理解修改的意圖:不要一上來就提交評論,先閱讀理解再提出問題。
4. 提供全面的反饋:對程式碼修改的正確性、可實施性、可維護性、可讀性和安全性提供反饋。
5. 要承認優點:評審程式碼時會很自然地集中在發現問題上,但程式碼評審不一定意味著都得是負面的評論,對好的東西也要進行讚揚。
6. 區分問題、建議和挑剔:寫comments的時候要做好分類,強制 / 推薦 / 參考。
7. 不要只做橡皮圖章:抵制用草率批准的方式快速給評審點選透過的形式主義,橡皮圖章式的評審是有害的。
8. 不要只侷限於使用網頁版的評審工具:仍然可以把程式碼拉下來到IDE中檢查。
9. 不要忘記評審測試程式碼:閱讀測試程式碼可以更好理解變更點,也要檢查測試程式碼的可維護性和清潔度。
10. 推動決斷:不要堅持要求完美,不要擴大修改範圍,清楚描述哪些評審意見是關鍵的,幫助提交者儘可能快地透過評審,不要讓質量成為效率的障礙。
推薦閱讀:
《開發者程式碼評審指南》(“Code Review Developer Guide”) 《高難度談話Ⅱ:感恩反饋》(Thanks for the Feedback:The Science and Art of Receiving Feedback Well)
第8章 軟體交付
軟體交付流程:
-
並沒有行業標準定義。 -
作者講解使用的流程:構建(build)、釋出(release)、部署(deploy)和展開(rollout)。
分支策略:
-
基於主分支的開發模式(Trunk-Based Development):主分支包含整個程式碼庫的主版本,並有修改的歷史記錄。分支是從主分支上“切”下來的,以進行程式碼修改,多個分支允許開發人員並行工作,然後將修改過的內容合併回主分支上。只有當各分支可以快速合併到主分支時,基於主分支的開發模式的效果才是最好的,即持續整合(CI)。 -
基於特性分支的開發模式(Feature Branch Workflow):開發人員同時在長期存續的特性分支上工作,每個特性分支與產品中的一個特性相關聯,特性分支存續時間都較長,開發人員需要經常從主分支中合併程式碼,使特性分支不偏離主幹太遠。釋出時特性分支會被拉入釋出分支進行測試,而特性分支可能會繼續開發,最終釋出的軟體包是建立在穩定的釋出分支上的,如果有問題就拉hotfix分支進行熱修復。
構建環節:
-
軟體包:為每個釋出版本而構建,內容和結構各不相同。 -
打包的注意事項:帶版本號,將不同的資源單獨打包。
釋出環節:
-
釋出步驟:內部發布可能只需要將軟體包發行到共享倉庫,面向使用者的釋出則需要釋出構建、文件更新、發行說明和使用者透傳。 -
不要只想著釋出:釋出不是工作的結束,你需要對釋出負責,確保適當的部署和良好的運轉,並及時解決在生產環境產生的問題。 -
將包釋出到倉庫:用於釋出的軟體包要持久化到一個儲存資源庫。 -
保持版本不變性:一旦釋出了,就永遠不要改變或覆蓋這個已釋出的包。 -
頻繁釋出:快速頻繁的釋出會讓每個版本的風險都較小,產生更穩定的軟體,當發現bug時也更容易修復。 -
對釋出計劃保持透明:釋出時間表一定要明確和公開。 -
撰寫變更日誌和發行說明:可以幫助你的使用者和支援團隊瞭解一個版本中包含的具體內容。
部署環節:
-
自動部署:使用指令碼而不是手動步驟來部署軟體。 -
部署的原子性:指令碼通常涉及多個步驟,每一步都可能失敗,要保證部署要麼全部完成要麼什麼都沒做(即原子性)。簡單方法之一是在與舊版本不同的位置上安裝軟體,不要覆蓋任何東西,讓回滾也變得容易。 -
獨立地部署應用:順序部署會降低部署效率,還可能導致衝突,最好保證每個應用可以獨立部署,做好向前和向後的相容。
展開環節:
-
系統監控:新程式碼被啟用後,監測健康指標,諸如錯誤率、響應時間和資源消耗。 -
特性開關(feature flag):程式碼被包裹在一個if語句中,該語句檢查一個flag(動態配置)以確定應該執行哪個分支的程式碼。flag可以是布林值,列表,灰度百分比等。需要注意的是資料庫通常不受開關控制,新舊程式碼會處理相同的表,因此一定要注意相容。 -
熔斷器:一種特殊的特性開關,由運維事件(效能閾值、異常日誌)控制,熔斷器的特點是二進位制的(開/關)、永久性的、自動化的。 -
並行的服務版本梯隊: -
金絲雀部署(Canary Deployment):用於處理高流量、部署到大量例項的服務。一個新的應用程式版本被首先部署到一組少量機器上,使用者的一個子集被路由到這個金絲雀版本,進行監控和評估,決定繼續擴大覆蓋範圍,或快速回滾切回老版本。 -
藍綠部署(Blue-Green Deployment):兩個相同的生產環境但部署不同的應用版本。藍色環境是當前活躍的生產環境,承載使用者流量,綠色環境初始化為藍色環境的映象,但不承載即時使用者流量,用於部署新的軟體版本,就緒並測試後,切換路由讓所有新的使用者流量打向綠色環境,此時綠色環境變為新的生產環境,舊的藍色環境空閒,新版本有問題可以迅速切回。當流量不容易劃分的時候可以考慮藍綠部署,但要注意每個環境都必須能夠處理100%的使用者流量。 -
摸黑啟動:即影子流量,將新的程式碼暴露在真實的流量中,但不使其對終端使用者可見,這樣即使程式碼有問題也沒有使用者受到影響。實現方式是將實際使用者的請求複製一份,同時傳送到舊系統和新系統,這樣新系統處理的請求是與舊系統完全相同的,但新系統的處理結果並不會返回給使用者,而是用於內部測試和分析。
推薦閱讀:
《Git團隊協作:掌握Git精髓,解決版本控制、工作流問題,實現高效開發》(Git for Teams:A User-CenteredApproach to Creating Efficient Workflows in Git) 《持續交付:釋出可靠軟體的系統方法》(Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation) 《釋出!設計與部署穩定的分散式系統(第2版)》(Release It! Design and Deploy Production-Ready Software 2nd)
第9章 On-Call
On-Call的工作方式:
-
開發人員根據時間表進行輪換。有些時間表會安排一名主要責任人和一名輔助的On-Call人員。 -
On-Call人員的大部分時間用來處理臨時性的支援,如bug報告、軟體如何執行以及使用的問題,當然也可能遇到運維事故,響應電話或訊息告警。
On-Call技能包:
-
隨時響應。
-
保持專注
-
確定工作優先順序。
-
清晰的溝通。
-
記錄跟蹤工作。
事故處理:
-
分流(triage):工程師必須找到問題,確定其嚴重性,並確定誰能修復它。 -
協同(coordination):團隊(以及潛在的使用者)必須得到這個問題的通知,如果On-Call人員自己不能解決這個問題,他們必須提醒那些能解決的人。 -
應急方案(mitigation):工程師必須儘快讓事情穩定下來。緩解並不是長期的修復,你只是在試圖“止血”,透過回滾一個版本、將故障轉移到另一個環境、關閉有問題的特性或增加硬體資源來緩解。 -
解決方案(resolution):在問題得到緩解後,工程師有一些時間來喘口氣、深入思考,繼續調查問題,以確定和解決潛在的問題。 -
後續行動(follow-up):對事故的根本原因——為什麼會發生,進行調查。如果事故很嚴重更要進行正式的事後調查或回顧性調查,建立後續任務,以防止重複的根本原因的再次出現。
提供支援:
-
On-Call工程師也得花時間處理支援類請求,來自組織內部或外部使用者,支援請求必須遵循一個相當標準的流程。 -
由於你“真正的”工作是程式設計,參與到支援工作中可能會讓你分心。把支援工作看成一次學習機會,瞭解你的應用實際的使用方式,接觸你不熟悉的程式碼,發現總結問題,幫助別人,建立良好的關係和影響力。
不要逞英雄:
-
不要“做得太多”,“救火”工程師、團隊“萬金油”都會讓團隊形成依賴,從而變成長期On-Call人員,長時間和高風險將導致倦怠,這種單點模式也是不健康的。 -
如果你覺得你正在成為“救火”工程師,與你的管理者或技術負責人討論如何找到更好的平衡,讓更多的人接受培訓並可以介入“救火”;如果你的團隊中有一名英雄,看看你是否可以向他學習,挑起一些重擔。
推薦閱讀:
《當尋呼機響起時,會發生什麼?》(“What Happens When the Pager GoesOff ?”)
第10章 技術設計流程
技術設計的V形結構:
-
軟體設計並不是一種將研究和頭腦風暴的結論落到文件上並獲得批准的線性過程。它更像是在獨立工作和相互合作之間交替進行的螺旋式上升的過程。 -
從問題空間的瞭解開始,在獨立工作和討論腦暴中進行設計文件的迭代,完成設計。
關於設計的思考:
-
定義問題:技術設計的首要任務是定義和理解你要解決的那個(或那些)問題。瞭解問題的邊界,以便知道如何解決,判斷什麼問題亟待解決,什麼問題甚至不值得解決。 -
技術選型:不要從問題定義直接就過渡到“最終”設計。考慮相關的研究、替代的解決方案,以及權衡各方案的利弊。你提出的設計不應該是你的第一個想法,而應該是你若干想法中最好的那個。 -
進行實驗:寫程式碼草稿、執行測試來對你的想法進行實驗,編寫API草案和部分實現、執行效能測試、甚至A/B使用者測試,以瞭解系統和使用者的行為。 -
給些時間:好的設計需要創造力和深入思考,不要指望坐下來一次就能完成一份設計,要給自己大塊的時間,休息一下,換換環境,耐心一點兒,然後深度工作。
撰寫設計文件:
-
判斷是否需要設計文件:並非每一項變更都需要設計文件或設計評審,按照團隊的指導方針來決定。 -
瞭解撰寫文件的目的:從表面上看,設計文件是告訴別人某個軟體或模組是如何開發、工作的。但設計文件也是一種工具,可以幫助你思考、獲得反饋,讓你的團隊瞭解情況、培養新的工程師,並推動專案規劃。 -
學會寫作:寫作作為一項技能,就像其他技能一樣,是透過實踐來進步的,充分利用寫作的機會——設計文件、電子郵件、程式碼評審意見——努力寫得清晰。 -
保證文件是最新的:設計文件是活的,必須要隨著方案的迭代和實現過程中的變更而更新,最好還要進行版本控制。
使用設計文件模板:
-
概要;
-
現狀與背景;
-
變更的目的;
-
需求;
-
潛在的解決方案;
-
建議的解決方案;
-
設計與架構;
-
系統構成圖;
-
UI/UX變更點;
-
程式碼變更點;
-
API變更點;
-
持久層變更點;
-
測試計劃;
-
釋出計劃;
-
遺留的問題;
-
附錄。
協作設計:
-
理解你的團隊的設計評審流程。 -
隨時讓團隊和技術負責人瞭解設計進展。 -
可以使用團隊內的設計討論來腦暴。 -
除了自己負責的專案,也要為團隊的技術設計貢獻力量。
推薦閱讀:
大型開源專案的文件 《吊床驅動開發》(“HammockDriven Development”) 《有效的軟體設計文件》(“Effective Software Design Documents”) 《英語寫作手冊:風格的要素》(TheElements of Style) 《寫作法寶:非虛構寫作指南》(OnWriting Well: The Classic Guide to Writing Nonfiction) 《如何有用地寫作》(“How to Write Usefully”) 《像說話一樣寫作》(“Write Like YouTalk”)
第11章 構建可演進的架構
理解複雜性:
-
高依賴性:即軟體依賴於其他的API或程式碼行為,每一次新增依賴都會導致耦合讓系統更難以修改。 -
高隱蔽性:即程式設計師很難預測某項變更的副作用、程式碼的行為方式,以及需要修改的地方。 -
高慣性:即對服務的呼叫者或軟體的使用者傾向於保持之前的使用習慣,變更成本會隨著時間推移而增加。
可演進的設計:
-
You ain’t gonna need it:不要構建你不需要的東西,避免過早最佳化,避免不必要的靈活抽象模型,以及避免最小可行產品(minimum viable product,MVP)所不需要的產品特性。 -
Principle of least astonishment:不要讓使用者感到驚訝,產品特性不要讓使用者有上揚的學習曲線和感到奇怪;也不要讓開發者感到驚訝,晦澀難懂的程式碼和隱性知識會導致維護的困難和複雜,還容易產生bug。 -
封裝專業領域知識:基於業務領域來封裝模組,考慮領域驅動設計(domain-driven design,DDD)。
可演進的API:
-
保持API小巧:只添加當下需要的API方法和入參欄位,有許多欄位的API方法應該有合理的預設值。 -
公開定義良好的服務端API:使用業界的標準模式如OpenAPI或公司定義的模式來定義服務端API,良好的服務必須宣告其模式、請求和響應方法、異常。 -
保持API變更的相容性:相容性可以讓客戶端和服務端版本獨立發展,向前相容的變更允許客戶端在呼叫舊版時使用新版的API服務,向後相容的變更讓舊的客戶端程式碼在使用新版本API時可以繼續執行而無需改造。 -
API版本化:完全向後或向前相容會讓API更難維護,最終還是需要升級版本,可以引入一個必填的版本欄位用以區分API的新老版本,並統一由API閘道器來管理和路由,注意文件也要和API保持一致的版本化。
可持續的資料管理:
-
資料庫隔離:共享的資料庫由多個應用程式共同訪問,很難演進,並且會導致喪失自主性——開發人員或團隊對系統進行獨立修改的能力。隔離的資料庫只有一個讀取者和寫入者,其他所有流量都透過遠端過程呼叫,保證了隔離性和靈活性。 -
使用schema:僵化的預定義資料欄位以及演進的重量級過程導致了無模式(schemaless)資料管理的出現,比如JSON或物件儲存,不需要預先定義結構,但會產生資料完整性和複雜性問題。強型別的面相schema的資料庫降低了應用的隱蔽性,也就降低了複雜性,因為資料大多數時候是“一次寫入,多次讀取”,schema明顯使讀取更容易。 -
schema自動化遷移:改變schema是高危操作,DDL很容易導致故障,要用schema管理工具和自動化遷移工具,並做好變更記錄的SOP。 -
保持schema的相容性:使用schema相容性檢查來探知不相容的變更,並使用資料產品來解耦內部和外部schema,比如將應用資料庫透過資料管道抽取、加工、轉換到數倉。
推薦閱讀:
《演進式架構》(Building Evolutionary Architectures) 《軟體設計的哲學》(A Philosophy of SoftwareDesign) 《Clojure的要素》(Elements of Clojure) “簡單造就易用”(“Simple MadeEasy”) 《資料網格:規模化地提供資料驅動的價值》(DataMesh: Delivering Data-Driven Value at Scale) 《資料密集型應用系統設計》(Designing Data-IntensiveApplications)
第12章 敏捷計劃
敏捷宣言:
-
個人和互動高於流程和工具 -
工作的軟體高於詳盡的文件 -
客戶合作高於合同談判 -
響應變化高於遵循計劃
敏捷計劃的框架:
-
Scrum:鼓勵短期迭代,並經常設有檢查點來調整計劃。開發工作被分成幾個衝刺階段,在衝刺開始時,每個團隊都會安排一場衝刺計劃會議來分配工作,這些工作被記錄在使用者故事或任務池中。規劃之後,開發人員便開始工作,工作進展在任務票或問題系統中被跟蹤。每天都設有一個簡短的站會,以分享最新情況並指出問題。在每個衝刺階段結束後,團隊會進行一次回顧總結, -
Kanban:看板定義了工作流程中的各個階段,所有的工作條目都要經歷這些階段(例如,待著手、計劃中、實施中、測試中、部署、展開)。看板將進行中的工作視覺化,相當於為每個工作流程階段設定了垂直列的儀表盤,透過限制每個階段的任務數量來限制正在進行中的工作,
Scrum框架:
-
使用者故事:從使用者的角度定義了特性的需求,把重點放在提供使用者價值上。格式是“作為一名<使用者>,我<想><這樣>”。 -
任務分解:單一的使用者故事需要被分解成更小的任務,以預估它需要多長時間才能完成,用來給多名開發人員分配工作,並跟蹤實施進度。 -
故事點:團隊的工作能力是以故事點(小時、天或“複雜性”)來衡量的,一次衝刺迭代的能力以開發人員的數量乘每名開發人員的故事點來計算的。例如,某個有4名工程師的團隊,每名工程師有10個故事點,其能力為40點。 -
消化積壓:積壓是指候選的使用者故事列表,分流是為了保持它的新鮮度、相關性和優先順序。 -
衝刺計劃:衝刺計劃會議由工程團隊與產品經理一起,討論高優先順序的使用者故事,決定做什麼。衝刺最重要的特點是週期短,因此一定要將大型任務分解成小型任務,才能更好理解和預估。 -
站會:每天早上安排15分鐘的會議,讓每個人都瞭解你的進展,並讓團隊有機會對任何危及衝刺目標的事情做出反應。 -
評審機制:評審發生在某兩輪衝刺之間,通常分為兩個環節:演示和專案評審。演示是團隊成員展示他們在本輪衝刺中取得的進展和結果,評審是對當前衝刺目標的完成情況的評估和總結。 -
回顧會:團隊聚在一起討論自上次回顧會以來有哪些進展,哪些不足。會議通常分為3個階段:分享、確定優先順序和解決問題。 -
路線圖:業產研的管理者需要按季度使用產品路線圖對下一個季度的工作方向,人力資源,以及更龐大的專案進行前置規劃,路線圖也可能在實施過程中不斷調整和迭代。
推薦閱讀:
〈敏捷宣言〉背後的原則》(“Principles Behind the Agile Manifesto”) Atlassian網站的文章
第13章 與管理者合作
管理者是做什麼的:
-
人:管理者構建團隊、指導和培養工程師,並進行人際關係的動態管理。 -
產品:管理者計劃和協調產品的開發,他們也可能參與產品開發的技術方面如程式碼評審和技術架構,但通常不需要親自寫程式碼。 -
流程:管理者對團隊流程進行迭代,以保持其有效性。 -
管理者們透過與高管或董事(“向上”)合作、與其他管理者(“橫向”)合作以及與他們的團隊(“向下”)合作來“管理”所有的這些事務。
溝通、目標與成長:
-
一對一面談(1∶1):用來討論關鍵問題、解決大局觀上的偏差,並建立富有成效的長期關係。 -
進展、計劃與問題(progress-plans- problems,PPP):更新工作狀態能幫助你的管理者發現問題、提供資訊、協調資源。 -
目標和關鍵結果(OKR):定義目標O,每個O附著3-5個關鍵結果KR作為衡量標準。按季度設定和評估,與你的管理者對齊。 -
績效考核:每年或每半年一次,職級和薪酬的調整一般也是在績效考核期間進行的。員工先自評,然後環評,管理者進行回應,最後管理者和員工聚在一起討論、反饋,在討論後員工需要確認績效考核結果。務必保有一份一年的工作清單方便回顧,也要試著把考核看作一次總結和計劃的機會。
向上管理:
-
接收反饋:績效考核的頻率太低,你需要定期的反饋才能迅速調整,但管理者並不總是主動提供反饋,你可能需要主動透過一對一面談的方式詢問來獲得反饋。要知道你的管理者僅僅是視角之一(誠然是很重要的視角),試著把管理者的反饋納入你的觀點,而不是直接採用管理者的反饋。此外對對方的反饋意見也要給予反饋。 -
給予反饋:反饋可以關於任何事情:團隊、公司、行為、專案、技術計劃,甚至是人力資源政策。提出問題,但不要只關注問題,可以使用情況、行為和影響(situation-behavior- impact,SBI)框架。 -
討論你的目標:不要指望你的管理者知道你對自己職業的要求。你需要清楚地闡述你的目標和願望,以便你的管理者可以幫助你實現這些目標。正式的績效考核環節是進行這種對話的好時機。 -
事情不順時要採取行動:關係和工作各有波峰、波谷,可能會發生短期的摩擦,沒必要頻繁採取激烈的行動。然而,如果你感到持續的挫折、壓力或不快樂,你應該說出來,可以使用SBI框架與你的導師、管理者、HR交流,以尋求幫助。你也可以要求變化或主動改變。
推薦閱讀:
《技術管理之路:技術領導者應對增長和變化的指南》(The Manager’s Path: A Guide for Tech Leaders Navigating Growthand Change) 《優雅謎題:工程管理的系統》(An Elegant Puzzle: Systemsof Engineering Management) 《高難度談話Ⅱ·感恩反饋》 (Thanks for the Feedback: The Science and Art of Receiving Feedback Well ) 《向上管理:如何晉升,如何在工作中獲勝以及如何與不同型別的上司一同獲取成功》(Managing Up: How to Move up, Win atWork, and Succeed with Any Type of Boss) 《格魯夫給經理人的第一課:英特爾創始人自述》(HighOutput Management)
第14章 職業生涯規劃
邁向資深之路:
-
職業發展:各家公司的職級數量不盡相同,但通常有兩個過渡表明資歷發生了重大轉變——從初級工程師到資深工程師,以及從資深工程師到主任工程師或首席工程師。 -
初級工程師:實現特性和完成任務。 -
資深工程師:處理更多的不確定性和模糊性,幫助確定工作內容、應對更大或更關鍵的專案,並且需要更少的指導。 -
主任工程師:承擔更廣泛的職責,甚至超出了團隊本身的範疇,需要對工程戰略、季度規劃、系統架構做出貢獻,並且要確保工程流程的運轉和政策的實施。職業發展分化為個人貢獻者和管理者兩條軌道。
職業生涯建議:
-
T型人才:軟體工程有許多專業領域。前端、後端、運維、資料倉庫和機器學習等,“T型”工程師既是能在大多數領域內都能有效工作的通才,並且至少是某一個領域的專家。 -
參加工程師訓練營:招聘、面試、午餐會、研討會、聚會、閱讀小組、開源專案、學徒和導師計劃都是可以參與的機會。 -
主導你自己的晉升:理想情況下,你的管理者會在完全正確和公平的時間點對你晉升。但這個世界很少是理想的,所以你可能需要主導你自己的晉升。找到公司的職業發展階梯,確定下一個職級所需的技能,瞭解晉升的流程,確保你的工作是有價值的和可見的,進行自我評估和獲得他人反饋,與你的管理者進行晉升談話,回顧和制定計劃。 -
換工作需謹慎:換工作可以拓展你的技能和人脈,但頻繁地換工作會阻礙你的成長,在HR的眼裡也很難看,沒有充分的理由就不要換工作。 -
自我調節:工作可能很忙碌,競爭很激烈,技術發展又很快,而且總是有更多的東西需要學習。你可能會覺得有太多的事情發生得太快了。新工程師的反應往往是更加努力、工作時間更長,但這是造成倦怠的關鍵因素。休息一下,短暫脫離,不要讓自己過度勞累。你的職業生涯是一場馬拉松,而不是短跑衝刺——你有幾十年的時間。給自己定下節奏,享受這段旅程吧!
雲上公網架構設計和安全管理
雲上公網的設計可以幫助企業更加統一、安全地管理自己的雲上網際網路出入口,同時可以實現統一監控運維和公網的成本最佳化。
點選閱讀原文檢視詳情。