成功重構了代碼是種怎樣的體驗?

成功重構了代碼是怎樣的一種體驗?


爽,暗爽。

翻來覆去的看代碼,越看越美,忍不住想和別人吹,但即使對方是程序員,如果沒參與項目,也只能出於禮貌附和一下,意興闌珊。

但沒關係,自己還是爽,走路都輕飄飄的,覺得真是人才呀。

暗暗期盼有新的需求進來,之前是戰戰兢兢,現在是一氣呵成,之前是動輒則咎,現在是浪穩風平。


每次重構完都會覺得信心爆棚,

覺得朕的代碼固若金湯,可以以不變應萬變。

想要用整棟樓都能聽到的聲音怒吼:還有誰!!

--------------------------

然而並沒有什麼卵用,

兩個月之內客戶就能讓你明白,

宇宙有多大,腦洞就有多大


我曾在2013年10月到11月完成過一項重構,效果不錯,受益至今。

13年10月,我進入現在的技術團隊已經有大半年。當時自己的狀況大致是,對團隊主要項目的技術架構有了充分清晰的了解,部分模塊已經相當熟悉,也已經開始獨立負責一些重要項目的設計開發工作。

從那時候(或者似乎更早)開始,就對系統中的一段代碼印象深刻:

首先,它是一組重要業務指標的核心統計邏輯,每天凌晨,上游百G級別數據洶湧而來,下游眾多依賴系統翹首以待。是的,它需要在所有人熟睡的時候完成所有的工作,以便在新的一個工作日的開始,讓大家能夠看到最新的數據。出問題怎麼辦?十有八九被同事、被系統報警叫起來處理問題,無奈自封「起夜家」。

其次,這段代碼從設計到實現上,都比較粗糙。就好像原先只打算給人臨時過夜的山間茅草房,被選為人民代表大會會址似的,尷尬無奈。據進一步打聽了解,這部分代碼很可能是在某個系統的前身(或許你能猜到是指啥)還在使用的時候,就已經寫下第一版了。我甚至能隱約感受到一絲創業時期的氣息……

最後,對這部分邏輯的變更、升級需求長期不斷。作為公司內的一個重要業務平台,不接下游需求是要鬧哪樣。不光要接,還要又快又好。產品經理們據理力爭,老大們義正言辭。

這段代碼具體是怎麼樣呢?

  1. 單一類文件,總行數超過1500行,同時注釋比例很低

  2. 為數不多的幾個成員方法。最主要的方法超過1100行,純正的過程化寫法
  3. 93行開始定義和初始化的局部變數,在436、664、770行等多處,使用得泰然自若
  4. 633行第一次定義和使用的某變數,在743行改頭換面,重新定義和初始化,應用到後續邏輯中
  5. 稍深入觀察,會發現有類似邏輯實現了兩次以上,原因似乎是每一次都和前面有那麼一點差別
  6. 輸入複雜,輸出更複雜,文檔那也是沒有的

就這樣一部一邊不停冒煙,一邊持續改裝和維護的上古老爺車,在勤懇的攻城獅同學們的手上,已經奔跑在路上好些個年頭了。雖說是偶然出些小毛病,大問題還真沒見犯過,很好的履行了自己的職責。可當聽到類似「哎那個小他,加個什麼什麼功能吧」的時候,我和同事那一臉吃了翔的表情和之後要死要活的開發過程,就一次又一次的讓我們認識到這處深坑的風險。

於是在一個機會下,見縫插針(真的是時間緊任務重啊)地執行了我的重構項目。具體的實現細節也沒必要細說,就說一些特別的吧。

說是重構,嚴格來說還是有些變化。為啥?倒是有人站出來告訴我,這裡面發的第三個、第五個郵件到底是幹什麼用的啊?收件人還在不在職,平時還看不看啊?前面煞有介事統計的一個量,到最後莫名消失了又是個怎麼回事?在各種找人諮詢、確認而無果後,在重構後的新版本中,我隨即做了刪除忽略。

正確性驗證。這種程度的重構,基本上算是徹底拋棄舊代碼了。測試必然不放心,更何況時間、資源有限?為了讓自己讓所有人進一步確信,又進行了超過一個月的線上並行驗證。以舊代碼為主,新代碼後續靜默執行,之後每天自動驗證結果一致性。

就這樣,最終在14年的年前,完成了重構後代碼的替換。

什麼?不就重構一下么,怎麼這麼多事兒這麼麻煩?什麼?你只想解決「Big clean problem」?呵呵,歡迎來到現實世界。

為什麼要重構,重構的價值?

成功的重構對技術人員來說,是對其綜合技術能力的可靠證明。但可惜很多時候,也僅是一種自我證明。而重構的收益,往往只會在中長期的時間內,慢慢從技術層面開始釋放。

仔細想一下,程序員自己就是重構的第一受益者,很多情況下也是唯一受益者。你之前接手的代碼,你前年、去年沒好好寫的代碼,重構了與否,對於僱主、對於用戶來說,幾乎不會有感覺。那麼誰最有感覺呢?還是程序員自己:「爽,我再一次證明了自己」。可惜很多時候,連身邊同事可能都不會有所感知。事情漸漸止步於此,你甚至會感覺落入一個孤芳自賞、懷才不遇的窘境。

那麼怎樣讓重構實現更大價值?我們先來看看好的重構能帶來什麼:更清晰的結構、更好的擴展性、更低的維護成本。你會發現,重構不應該不僅僅著眼於當下,而更應面向未來。所以你要讓項目更長期的因此受益,要讓更多的開發人員因此受益,要讓後續的更多工作因此受益。

說回前面的那次重構,後事如何?

  1. 在之後的兩年多的時間裡,相關代碼大大小小又升級過46次,涉及的直接開發人員有8人。而大體的結構依然保持不變(這裡只計算了主幹提交日誌)
  2. 另一位同事在此之上,實現了多進程並行化,大大縮短了運行時間。非常贊的工作
  3. 拆分解耦後的結構,對於測試也帶來了極大幫助
  4. 對外,對外我們可以很自信的說,系統接入時間有了顯著的減少

儘管如此,我並不贊同把重構放在較高優先順序上的做法。除非這件事已經公認的勢在必行,被認為有長期的收益。或者你是想明白了,就是用來向自己證明自己的,那我不攔著你。

但是請注意,以上的這些論述,針對的都是好的有效的成功的重構。對,我的意思就是,沒有金剛鑽別攬瓷器活。即使能力足夠,也還是請你三思而後行。

因為,在一個技術團隊里,當我們說要做一個重構時,我們在說什麼?我們在說:

首先,我對要重構的這塊功能已經有了完整和全部細節的理解,它以前、現在和近期的將來是什麼樣子,我都有清楚的了解

其次,我有完整和切實可行的方案,可以明確論證重構後會得到更好的結構和代碼;

再次,儘管已經盡努力保證對重構內容的全面把控,我清楚的認識到,客觀存在遺漏和產生問題的可能,並準備好及時跟進儘快解決問題

最後,作為重構的操刀者,最熟悉細節的人,我已準備好在較長的一段時間裡,作為這個模塊的第一負責人,直到其他大部分人也足夠了解。

以上。


  1. 代碼更少了,雖然代碼量不是重構的目的,但如果發現通過重構冗餘降低,代碼量減少,通常是正面信號
  2. 介面更清楚了,成功的重構以後,原來腦海里模糊不確定的調用關係,變得清晰確定了,如果有人問題這個東西是怎麼工作的,你通常能用幾句話就描述清楚
  3. QA通過你的重構以後,心裡踏實了,有種小小的成就感,如果你平時重構不多,這時候會感覺水平提高了一點,如果你經常重構,也沒有特別的感覺 :)


話說五個月的代碼就兩眼一碼黑,難道我也是有金魚記憶隱藏屬性?

謝天謝地,幸虧當年寫了完整的 100% 覆蓋率的單元測試,要不然死都不知道怎麼死的。

改好的代碼功能強大,性能穩定,方便靈活,超強容錯,信心爆棚。


這絕逼是世界上最完美的代碼了!!結果過幾個月回來看看感覺寫的就是一團垃圾,然後再重構.......

但是還是喜歡重構代碼,項目版本迭代了十幾個,一直在重構,就是不上線.....


媽的終於搞定了~~~

或者,

媽的居然搞定了~~~

然後,

掰響指關節,擼起袖子,站起來,去打杯水;

回來後,哇擦有bug,趁熱解決;

過兩天,哇擦這還有bug,想想,解決;

半個月後,哇擦有個以前舊版本木有的bug,沉下心來重新走一遍,解決;

然後系統就更快更高更強了~


從來沒成功過,重構的過程中又有新的想法了,然而重構已經在進行中了,新的想法暫時只能放進隊列裡面,FIFO嘛。


被錯綜複雜非同步狀態弄的死去活來的一整天,被一個簡單的重構Idea在一個小時內解決

——然後我突然意識到,high的只有我一個人,好凄涼。


好爽~

但是,怎麼跟老闆解釋這段時間做了什麼。。上次提的新需求還沒有開始做啊


過幾個月回來看又重構了一遍。

通常來說好看的代碼效率差。

另外,重構前一定要寫好測試啊


其實,很多人認為他們在做代碼「重構」,但其實是在把代碼「重寫」。

重構有一個很重要的前提,就是不改變原有代碼的邏輯。意思也就是說,如果原代碼中存在一個bug,那重構之後這個bug也應當存在。重寫代碼,就是按照現有的需求,重新實現了一遍代碼。

那bug什麼時候修?做重構之前,或者重構結束後。但不能在重構過程中。

另外,如果重構過程中缺乏相關的單元測試做保護,那同樣很難保證你正在「重構」代碼。

跑題了。

習慣性匿名。


從一開始的暗爽逐漸成為不爽,然後接著重構,如此??,這就是命運的安排吧


三個月之後又出現一堆亂七八糟的代碼!


改完以後會一臉藏不住地笑,然後欣賞很久。欣賞之餘還會把每一個空格都要改得漂漂亮亮的!!!!(?ω?)

過很長一段時間再看到代碼的時候就會嘀咕,我那時候咋那麼厲害呢!哈哈哈哈!


以前需求變動要改七八個地方,重構後只要改一個地方


重構前:怎麼又有需求了...

重構後:還有啥需求沒


我們都是那酸秀才,一個茴香的"茴"字也能沖人顯擺半天。

但願將來有一天考中了舉人不會像范進一樣失心發瘋...

體驗...現在有點說不出來。

大家先一起來體會一下,大概就是當年賈島的"推敲"典故吧:

唐朝的賈島是著名的苦吟派詩人。什麼叫苦吟派呢?就是為了一句詩或是詩中的一個詞,不惜耗費心血,花費工夫。賈島曾用幾年時間做了一首詩。詩成之後,他熱淚橫流,不僅僅是高興,也是心疼自己。當然他並不是每做一首都這麼費勁兒,如果那樣,他就成不了詩人了。

有一次,賈島騎驢闖了官道。他正琢磨著一句詩,名叫《題李凝幽居》全詩如下:

閑居少鄰並,

草徑入荒園。

鳥宿池邊樹,

月下門。

過橋分野色,

移石動雲根。

暫去還來此,

幽期不負言。

但他有一處拿不定主意,那就是覺得第二句中的「鳥宿池邊樹,僧推月下門」的「推」應換成「敲」。可他又覺著「敲」也有點不太合適,不如「推」好。不知是「敲」還是「推」好。手一邊做著「推」的姿勢,一邊做著「敲」的姿勢,反覆斟酌。不知不覺地,就騎著毛驢闖進了大官韓愈(唐宋八大家之一)的儀仗隊的第三節。

韓愈問賈島為什麼闖進自己的儀仗隊。賈島就把自己做的那首詩念給韓愈聽,但是其中一句拿不定主意是用「推」好,還是用「敲」好的事說了一遍。韓愈聽了,對賈島說:「我看還是用『敲』好,即使是在夜深人靜,拜訪友人,還敲門代表你是一個有禮貌的人!而且一個『敲』字,使夜靜更深之時,多了幾分聲響。再說,讀起來也響亮些」賈島聽了連連點頭稱讚。兩個人並排騎著自己的坐騎回到了韓愈的家,後來二人還成了很要好的朋友

推敲從此也就成為了膾炙人口的常用詞,用來比喻做文章或做事時,反覆琢磨,反覆斟酌。

我這個人說事愛打比方啊,總覺得寫代碼就好像是在寫文章,短篇或者長篇。我想,能寫出詩來的程序員...那應該大多都可以青史留名了吧。

我覺得重構大概是分兩種:重構之前本就不合理的代碼、重構之前合理的代碼。

重構之前不合理的代碼就好像是處女座或者是強迫症把格式對齊的感覺吧。比如多處重複代碼、多處重複功能,抽象出來放到一個函數里。

而重構之前合理的代碼完成之後的體驗...大概就是"爽"吧。能夠寫出更加優秀、更加合理的代碼。又或者經過系統後期的不斷拓展、對接、延伸、變更,之前本就合理的代碼就有可能變得不那麼合理。

程序員的成長過程本就是不斷地覺得自己以前寫的代碼是狗屎的過程。

重構完成之後,真想把"茴"字給所有的人都寫八遍看看。

有的人以五殺為樂,有的人以健身為樂,有的人以呼朋喚友為樂,有的人以遊山玩水為樂,有的人以吃東吃西為樂,有的人以朋友圈有人點贊為樂,有的人以段子為樂。

而我以自己能夠寫出更加優秀的代碼為樂,畢竟,斯諾克的一桿收和滿桿還是有很大差距的。

開心,就是那種吃飯都能多吃一碗的開心。

我就是這麼個酸秀才,而且還是個愛裝逼的酸秀才。

哈哈哈,寫完之後才回過味兒來,想必在此問題下的答主都是一些技術大牛或者思想大牛。我這一線基層堆碼工人難免有些捉襟見肘了。

再後來想想,寫都寫了,或許有一兩個和我一樣等級的堆碼工人看到了能和我感同身受呢。

大牛們,見笑咯~


世界再次回歸到我的掌下.....


其實就是重新重寫了一遍

因為之前寫的已經看不懂了...


推薦閱讀:

如何評價 Emacs 的配置文件 Spacemacs?
想學習 C#,案頭有兩本書(CLR via C# 和 C# in Depth),不知學習順序是怎麼樣的?
請舉幾個是上下文無關文法而非正則文法的例子?

TAG:重構 | 編程 | X是種怎樣的體驗 |