引子
9年前我入職一家公司,團隊裡都是之前公司的原同事,彼此都很熟,對各人的能力也都很瞭解。我當時負責整個公司的搜尋引擎。上班第一天,我在看之前的遺留程式碼。原同事過來問我:“你是打算用這個老系統改造還是重寫?”我笑了笑說:“我還是重寫吧。” 原同事也意會的笑了笑說:“我就知道。”當時我們都多少帶著些技術高人一籌的傲氣。而我那位同事成長的更快,我們第三次做同事的時候,他整個人更加成熟謙虛,而那時我還在路上。9年來我再也沒有接手可以毫無負擔,直接推倒重寫的程式碼。就算有,不搞清楚以前的邏輯和背景,就直接拋掉這些歷史包袱是不對的。在修改別人寫的程式碼的時候,我們需要信奉黑格爾的名言:“存在即合理”。一定要弄清楚之前這樣編寫程式碼是出於什麼樣的考慮。
專案背景
這段時間我們團隊在修改之前的一個功能。在我接觸到這個專案的時候,設計方案已經被討論了多次,已經到了詳細設計的階段。在我視角需求是這樣的:就是一個查詢介面的改造,改造前程式碼邏輯被前人做複雜了,這次一些從下游拿資料來拼接返回值的邏輯可以改成從下游(資料基礎服務)簡單取部分資料,另外一部分寫死。
聽起來是不是很簡單。這麼一件事,總有也就幾百行程式碼的開發量。有兩個團隊領導分別做專案經理和技術經理,由領導親自做的設計方案;我作為團隊架構師也被指派親自負責查詢服務模組的開發;一名一直做基礎資料服務的同學做基礎服務部分的改造;一個同學專門負責白盒測試;一個同學負責黑盒測試;還有一個對之前邏輯瞭解的同學負責方案評審和投產步驟編寫。能看得出來這個功能重要且有其特殊性。引起了高度的重視。因為這是修改之前幾年前編寫的幾經易手、十分核心且之前沒怎麼敢改動的程式碼。
詳細方案設計在別人寫的程式碼上做修改,做詳細設計時,第一步要做的是充分評估改動影響;第二步是畫流程圖梳理改動前後的呼叫鏈和資料流,列出修改點;第三步是定好測試關鍵案例,確保結果的正確性。評估影響
出現故障,第二要做的是什麼呀?是止血。那第一要做的是什麼呀?是評估影響。要開展一個新專案,第一要做的是什麼呀?是規劃目標。那第二要做的是什麼呀?是評估影響。做方案設計,第一要做的是什麼呀?是制定目標。那第二要做的是什麼呀?是評估影響。一言以蔽之,評估影響是在任何行動開始前,除了制定目標之外最重要的事。在很多方案設計中,往往沒有將這一步規劃到明確的流程中去,草率的實施,是日後出現問題的根源。
具體要怎麼做呢?舉個例子來說,之前做過很多http介面,常有需求說要在返回值裡新增欄位。很多剛剛出入程式設計這一江湖的新人,會覺得新增欄位還能有什麼影響,15年的老江湖告訴你:大錯特錯了!
新增欄位,首先對容量可能會有影響,需要額外的日誌等儲存空間,佔更多的頻寬;其次,下游有可能有校驗。所以評估影響重要的一步是要確認影響。和所有的呼叫方溝通確認,確認沒有影響再進行下一步。
邏輯梳理從這一步做的好壞,我直接可以判斷你的高考分數。在本週答辯會上,在對我的提問環節。HR小姐姐說不是單單問我,要問我們在場所有人一個問題:“程式碼都讀過了,為什麼有些人還對邏輯不清楚?”其中一個架構師回答到:“就是你上學的時候讀魯迅的書和現在讀魯迅的書的區別。”其實我想說:“治學三境界瞭解一下”,但是想想為這句話我要解釋兩分鐘詩詞,在述職評分現場,肉眼可見的在拽,豈不是在給自己減分。所以我選擇了沉默。這裡自己的地盤提一嘴。晚清國學大師王國維在其不朽之作《人間詞話》中曾用形象的比喻提出了治學的三種境界或說是三個過程:
古今之成大事業、大學問者,罔不經過三種之境界:“昨夜西風凋碧樹。獨上高樓,望盡天涯路。”此第一境界也。“衣帶漸寬終不悔,為伊消得人憔悴。”此第二境界也。“眾裡尋他千百度,驀然回首,那人卻在燈火闌珊處。”此第三境界也。
第一境界表達的本意是高瞻遠矚,立志高遠。在讀程式碼這件事上,可以理解為了解基本框架結構和程式碼基本實現的功能。第二境界是刻苦鑽研深入的過程。第三境界是頓悟,瞭解之前梳理中沒有想明白或忽略的細節或問題。
而我們動手改別人程式碼之前,至少要做到第二境界。一個可用工具就是流程圖,將每個步驟對資料做的轉換,並標識出每一步資料格式。
最後,總結一下修改點,方便形成測試案例和checklist。
制定測試案例
在評估影響和邏輯梳理時,關鍵案例其實已經出來了,這個階段是個整理階段。同時,也是從另外的視角,看看是否能達到“驀然回首”的境界,補齊之前邏輯上的疏漏。
以上三步完成之後,就是設計方案評審階段。千人千問,多視角審視方案,也增進理解。
編寫程式碼
在寫程式碼之初,自認對程式碼做了深入的分析,加上15年程式碼編寫經驗,覺得自己寫這段程式碼豈不是降維打擊。結果程式碼提交之後,真的是被打擊了。Code Review同學直接在群裡說給我找出來7個問題。開會的時候,其他同學也開玩笑的提了一嘴。就這麼被年輕同事弄沒了排面,雖說知道格局境界要高,心裡也確有不爽。關鍵是他提的7個問題,他提之前我都有認真思考過,程式碼是刻意為之。
後來我們就語音溝通了一下這些問題,雖說有些我還是不認同,但是也能明白他提的問題的道理。
有一條,是我新定了一個錯誤碼,我的思考是是這個查詢介面非常重要,希望出現問題和其他系統做區別。而這是我們內部錯誤碼,外部錯誤碼沒有變,所以不會對外部產生影響。而Code Review的同事說出了我之前沒有了解到的資訊:他之前為老錯誤碼單獨做了監控。我新定義的錯誤碼,監控就不生效了。
另外一條,說我缺少非空判斷。這個非空我是加了的,底層加了非空判斷。邏輯是沒有問題的。但是他覺得程式碼上層不加,語義上不連貫。我覺得邏輯應該內聚,自己做好的事情不應該讓上層來做。這種問題,我統歸為風格問題。每個人寫文章的思路是不同的,寫程式碼的思路也是不同的。別人覺得那樣更好理解,其實換一個人就不這麼認為。《有效的Java》這本名著,現在很多理論在被啪啪啪打臉。所以我遇到這種問題的時候都是不願意糾結的,我Review Code別人程式碼的時候也從不去糾結別人這種問題,我只說自己的考慮,別人是否接受我都不會因為這個把別人程式碼打回去。這裡Code Review的同事糾結,非要我遵從他的思路,我不同意改,也覺得沒有爭論的必要,我提出加個註釋作為妥協,結束這個爭論。
其中最重要的一條,涉及一個日誌列印。結構化日誌的列印,整個工程用了前人寫的一個“輪子”,在jar包裡不好改。改了怕影響太大。因為使用的日誌,日誌涉及其他兩個非常重要的功能。這兩個功能要藉助日誌分析,使用者來進行自動操作。所以我的處理方式是新定義了一個模板,來確保不影響原有功能。Code Review同事讓我將共用模板改一下,不要新建模板,模板多了不好維護。我的擔心是上線排期非常緊,老邏輯沒有人徹底清楚,之前的測試用例並不完善,所以求小心。而Code Review的同事說沒問題的,出了問題他承擔。真要出了問題,上面一層層的扛著擔子。我也責無旁貸的。不會落到他身上。我也不建議他這樣的保證。後來,我自己想了一下,如果用兩個模板,兩個append同時寫一個日誌檔案,之前也沒有這麼用過,也有風險,所以還是按照他說的改了。但是開會Diff程式碼的時候(上線前將上一個版本的程式碼和這個版本的程式碼做比較),我開玩笑還提了一嘴,說同事說了“出問題他承擔”。其實是隱含的勸誡一下,這句話有些慷他人之慨。其實本質上我同事的意思就是:“我和你一起保證修改的正確性”。用心是非常好的。
最終提的7條每條我們都爭論了,那是因為每一條我們兩個都真正思考過。這種氛圍我覺得是非常好的。
測試
我的程式碼CodeReview的同事有找出來問題,專業的QA白盒測試和黑盒測試都沒有發現問題。這個和我預期的一致。因為在編碼階段,不僅我自己用心了,CodeReview的同事也用心了,沒有問題才是正常的。這也應該是編寫提交後最普遍的結果。因為一旦問題讓測試發現了,那這通常只是冰山一角,底下會隱藏更多的問題。
後記
道、法、術。做任何事情的道理都是一樣的,用心是第一位。《山河令》裡體現用心的地方很多。其中一項就是留白。
比如溫週二人在龍淵閣掉落谷底,面對藥人的圍攻。周說:“得君為友不枉此生”。溫言:“幸得君心似我心。”很多人都知道:“幸得君心似我”心出自《卜運算元·我住長江頭》,下一句是:“定不負相思意”。在生死存亡之際,下面一句要是說出來,馬上共生共死的兄弟情就變質了。所以情商高的周接了一句:“聽你念詩我頭疼。”面對死亡的坦然樂觀,情緒就烘托出來了。
再比如,片尾曲《天涯客》裡,一句歌詞是“把古道西風瘦馬換小橋流水人家。”這也是個留白。《天淨沙秋思》之前教兒子寫作文的時候,我教過他:“你想把本來可以寫100字的作文寫成400字可以先羅列一堆景物描寫,最後一句才是你真正要表達的內容”。而《天淨沙秋思》最後一句是“斷腸人在天涯”正好對曲名的天涯客。
程式設計和其他事情一樣,用心是出好作品的關鍵。
推薦閱讀