對重寫代碼說不。
以下為譯文:
重寫代碼消耗了12個月!
我們從頭開始重寫代碼浪費的時間。
你能想象在軟件行業,12個月的時間沒有任何新產品推出,沒有任何新版本更新嗎?
真的,我不由自主地問自己這個問題:
在這個快速發展的世界里,12月的時間能讓我們做多少事情?
“2015年1月20日,星期二,下午5:10,AntiMalware軟件終于進入了第一次公測。”
經過幾十個小時的不眠不休后,第一個版本的軟件說明書終于發布到了網站上,這標志著我們的新旅程的開始。
我在一家為企業和終端用戶提供安全軟件的小型網絡安全公司工作。我們開發的軟件保護用戶免受惡意軟件的侵害。如果用戶的電腦被惡意軟件感染,我們的軟件會幫助他們清理。AntiMalware就是其中一個軟件。
第一個測試版收到的反饋令人鼓舞。我們有四個開發人員為這個產品工作,不斷地修復Bug, 改進產品功能,推出新版本。
第一個穩定版本
經過兩個月的糾錯、功能改進和編碼工作,我們發布了AntiMalware的第一個穩定版本。
看看用戶怎么說?
大多數用戶的反饋都很好,他們喜歡這個產品。這讓我們的團隊深受鼓舞,大家卯足了勁地干活,來改進這個產品的核心功能。
進入市場
2016—2017。
大風暴來臨前的黃金歲月。
AntiMalware軟件處于它的最佳期,它成為了我們的旗艦產品。用戶紛紛把它推薦給他們的朋友們。所有與安全相關的博客和論壇也都在推薦這個軟件。它成了拯救被惡意軟件感染的用戶的首選軟件。
下載、安裝、銷售,一切都向好的方向發展,用戶群在幾個月內迅速增長。 創始人很高興,團隊也是如此。大家都在想:“我們做到了! 像其他大公司一樣,我們認為我們創造了自己的成功故事。“
新機遇(至少我們這樣認為):進入企業市場
后來,公司決定進入企業市場。一個新的企業產品團隊成立了。原產品負責人離開了公司,我們的CTO接任成為新的產品負責人(這是災難的開始,稍后我會解釋)。
一些開發人員離開了公司,但沒有什么影響。我們把每件事情處理得很好,AntiMalware軟件仍然是市場上最好的選擇。
好日子結束, 麻煩開始
正如我前面所說,我們的CTO成了AntiMalware的產品負責人,他需要處理AntiMalware的方方面面。而且他還是該軟件的首席開發人員,負責不間斷地發布更新和功能提升。同時,他的職位讓他還需要處理公司的其他事務。
當然,一開始都很順利,我們的情況就像所有軟件開發一樣,我們不間斷地維護和改進我們的軟件。
正如我們應該預料到的(顯然我們沒有),不知何故,軟件開發過程開始慢下來。
新的版本更新開始延期了,這種情況持續了一陣子,很快就變成沒有版本更新了。這讓我很不安,有一天我問CTO:
“這個產品出了什么問題?為什么版本更新要花費那么多時間而且開發進展緩慢?”
他深吸一口氣,開始回答:
“我們的代碼太復雜,它的結構不好,耦合太緊。架構設計完全錯誤,用戶界面和核心邏輯代碼混雜在一起,每當修復一個Bug或作某些改變時,其他部分就會受影響。即使是小的改變也很難做好。每次更新,都會引起新的問題。
一些方法竟然有20個參數,方法體的代碼有兩頁長!你能想象嗎?有許多不應該實現的東西不知為何都實現了。
這就是為什么每次更新都要花費很長時間而我們無法推出新功能的原因。每次我們推出一個新版本,我都擔心可能會引入新的Bug,而那些現在工作得很好的核心功能則有可能因此無法工作。在這種情況下,發布新版本太冒險了,我們可能會失去我們的用戶,我們的軟件無人再愿意使用。”
他的回答中提到的一系列問題其實我們都知道。只是,我們期望從他的口中說出來。
我還問了一個問題。負責這個軟件的前任首席開發人員為這個軟件開發了一年時間,而他都在CTO的管理下,那么CTO為什么允許這樣混亂的代碼出來呢?
“我不想打擊他的積極性,我們必須盡快進入反惡意軟件市場,他很擅長這個,所以我才沒有制止他這樣做。”
CTO這樣回答。
也就是說,為了以最快的速度進入市場,我們犧牲了代碼質量,這樣做也等于破壞了這個產品的未來。
經驗教訓:
要在第一時間對不好的代碼設計說“不”,不要讓“面條式代碼”毀了你的產品的未來。要確保做出的軟件產品有可持續開發性。
那么,如何修復這個可怕的代碼?
“我們都是程序員,而程序員的心中都駐著個建筑師,當他們到達一個地方的時候,他們想做的第一件事就是把這個地方夷為平地,然后在上面建造一些宏偉的建筑。我們對那些漸進式的更新不感興趣:如小修小補、改進、種種花草等等。”
-?Joel Spolsky,Stackoverflow公司CEO
開發人員總是傾向于拋棄舊代碼然后從頭開始,他們有這樣做的理由。因為他們認為舊代碼都是無用而且凌亂的。但是這只是想當然的理由。當我們試圖找出背后的真正原因時,我們會發現:
我們可能錯了!
舊代碼對我們來說可能看起來很凌亂,必須從頭重寫的原因并不是因為代碼本身,而是因為一個重要的,基本的編程法則:
讀代碼比寫代碼難。
這解釋了代碼重用困難的原因,也解釋了為什么我們認為舊代碼象頭發一樣凌亂。因為這個原因,當我們閱讀另一個開發人員的代碼時,我們的潛意識會不斷對著我們耳語“扔掉它,重新開始”。
像所有開發人員一樣,我們也落入了這個陷阱。只是讀一遍我們的凌亂的代碼就足夠讓我們下決心考慮從頭重寫了。
在一系列的會議之后,即使CTO對重寫代碼有抵觸(他是對的),他最終還是被說服了,我們決定從頭重寫代碼。
然而,重寫代碼的決定并沒有持續太久…
那是一個周末,星期日,我邊喝早茶邊讀一些推送文章。就像我的推送知道該向我展示什么一樣,我讀到了那篇最著名的關于重寫代碼的文章,就是Joel Spolsky寫的Netscape 的代碼重寫故事(https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/)。
讀完那篇文章后,我立馬分享給了AntiMalware開發團隊,包括CTO。
然后我們開始了新的討論。
本來說服CTO作出代碼重寫的決定就已經很難了。他在讀完那篇文章后馬上改變了主意,他決定中止代碼重寫。這讓其他團隊成員生氣了,他們沖我大喊大叫:
“你為什么給他看那篇文章?我們都已經說服他了。這個產品必須從頭重寫,這是唯一的解決方案。”
我們的第一次重寫代碼的嘗試到此結束了。關于這個話題的討論也終止了。我們的CTO相信我們可以管理好這個糟糕的代碼,并有能力在它之上發布新版本,直到嚴酷的現實擊倒我們為止。
一年沒有任何更新…
真的,這不是玩笑。真的一年沒有更新了!
“為什么沒有更新?“
“自上次更新到現在已經有好幾個月過去了。”
每天,我們都得面對這些來自用戶的負面評論。作為一家小公司,我們需要管理的產品太多了,而且,我們又進入了企業市場,這些加在一起,使得我們陷入了這樣的困境。
把所有這些結合起來,你就會得出這樣的結論:我們忘記了我們的用戶。
回想一下,我們不想發布新的版本,因為我們不想失去用戶。
但事實應該是相反的:如果我們不發布新的更新,我們肯定會失去用戶,而我們已經一年半沒有給他們任何新版本了。
在被現實打了一巴掌之后,我們決定回頭。對我們來說,除了重寫代碼別無它途。我們做到了。
當前
“2018年12月17日,星期一,21:40。測試的電子郵件準備好了,即將發送給我們的內部測試組。”
經過12個月筋疲力盡的工作,代碼重寫終于完工。我們準備了第一個測試版本說明,就像上次這個產品面市的第一天一樣。
我們又回來了…
這個產品的重寫版本仍處于測試階段。測試已經快一個月了。我們正在修復錯誤,傾聽用戶的意見,審查用戶反饋……一切就像4年前一樣……
但是在這12個漫長的月中,我們錯過了什么呢?如果不是重寫,我們會做出什么新產品?!
許多問題可以在這里提出來。但我知道我們只有重寫一條路,我們看不到任何其他的解決方案。
如果你也落入了這個陷阱,開始思考“我是否應該從頭開始重寫代碼”,那么在開始代碼重寫的第一步之前,就考慮自己提問下面的問題,每個開發人員都應該問問自己:
你準備好拋棄關于舊代碼的所有知識了嗎?
這個問題很重要!請誠實地回答:你真的準備好拋棄所有的知識,所有收集到的錯誤和修復,年復一年的編碼結果嗎?拋棄舊代碼并從頭開始,真的是你所期望的嗎?當你從這個角度來審視代碼重寫的決定,你會發覺很痛苦,不是嗎?所有那些試圖修補bug的不眠之夜都會在你眼前閃過。相信我,因為我有切身體會。
你必須和很多用戶交談才能找到導致你的軟件不能正常工作的問題所在,然后你要在你的軟件中定位這個錯誤,重現這個問題,然后找到解決方法,然后……等等。
你能保證你會做的比第一次更好嗎?
這點很重要:當你從頭開始的時候,沒有人能保證你會比第一次做的更好。
因為你選擇拋棄關于這個軟件的所有知識和已經收集的錯誤和修復,所以同樣的錯誤很可能再次出現在你的新代碼里。
可能代碼重寫團隊已經不是第一個版本的開發團隊。所以你實際上沒有“更多的經驗”。你會犯下舊版本中的大部分的的錯誤,并帶來一些新錯誤,而這些新錯誤在舊版本中并不存在。
如果你沒有很好地計劃重寫工作,你可能面臨新版本比原始版本更糟的風險。然而,既然作出了重寫的決定,你就要承擔這個風險,這個風險可能導致你失去你的客戶。
你準備好將幾個月/幾年的時間優勢拱手送給你的競爭對手嗎?
你知道需要多少時間來重寫你的軟件嗎?
代碼重寫牽扯到大量的精力、計劃和準備工作。你必須把每項任務計劃好,然而一個接一個地沖刺。你必須確切地知道完成這個痛苦的過程的最后期限。沒人知道你會不會錯過這個最后期限。有很大的可能你不能準時完成這個過程。
你不得不在數月或數年時間內只能交付舊版本給用戶,這將置你于極其危險的境地。你完全無法進行任何戰略改變或對市場所需的新功能作出反應,因為你沒有任何新代碼可以交付。
你的客戶可能會拋棄你,因為你除了不斷地提供一成不變地舊版本外,無法給他們任何新的東西。
這些你都考慮到了嗎?
從代碼重寫中我們學到了什么?
從頭開始重寫一個系統,本質上就是承認作為一個設計師的失敗。它其實是在聲明,“我們未能設計一個可維護的系統,因此必須重新從頭開始。”
——摘自 Max Kanat-Alexander的Code Simplicity
像其他設計師一樣,我們承認我們未能設計好我們的軟件,我們從這個精疲力盡的過程中學到了很多東西。在這里,我分享一些我們從中獲得的經驗教訓。
代碼重寫是開發人員的一種錯覺,大多數情況下它不是解決方案。
當你的代碼遇到問題時,準確地診斷問題很重要。像每個開發人員一樣,你最初的想法不應該是代碼重寫。代碼重寫只是一種錯覺。因為你在閱讀別人的代碼的時候,你會認為如果你從頭重寫代碼,你能做得更好。在這種情況下,請始終牢記那個重要的,基本的編程法則。
在決定重寫代碼前,考慮代碼重構
有針對性的重寫對于處理代碼庫中最嚴重的錯誤很有用。如果可以限制范圍并解決大部分問題,就不要進行整體重寫。例如,軟件的加載速度非常慢。但這只影響到項目的一小部分。通過小心地移動代碼、重構和更改接口,這個問題可以一次性解決。你不必重寫所有代碼。
代碼重寫是一條比預期耗時更長、更困難、更容易失敗的路。
告訴大家一個開發人員通常在錯過最后期限后才意識到的事實:一切都比想象的要花更長的時間。代碼重寫成本的估計通常很悲觀,然而實際的成本幾乎總是比你想象的更高,花費的時間也更長。因為總是會有想不到的復雜問題要解決,這些都會使重寫過程變得更加困難和痛苦。最后,你很可能不得不接受失敗的結果。
確保重寫后的產品能夠更好地解決用戶的問題,至少相同,不能接受更差。
重寫對用戶沒有直接的影響/好處。因為用戶不關心代碼,他們只想解決自己的問題,僅此而已。在用戶看來,能夠解決他們問題的產品就是好產品。否則,他們不會用它。用戶不關心你的代碼重寫決定,所以重寫后版本必須至少和舊版本一樣有效地解決他們的問題。
保持對現有產品的維護和支持。
在我們的案例中,我們有一年的時間沒有向用戶提供任何軟件更新。這對于我們今天生活的世界來說是太長了。盡管我們的產品依然足夠優秀,但是沒有更新用戶肯定會抱怨。當程序員重寫代碼時,永遠不要停止維護當前正在使用的系統。在重寫過程中,舊的代碼仍然需要維護,小的更新和錯誤修復需要及時提供給用戶。否則,你將面臨失去用戶的風險。
讓用戶盡快參與設計過程
確保定期向用戶展示最新進展,以便他們能夠幫助你捕獲最嚴重的錯誤。盡快與用戶見面是很重要的。他們的反饋將幫助您根據他們的需求設計新產品。不要實現任何不必要的功能,這將避免你的代碼庫過于復雜化。
保持產品團隊同步步調一致
一個產品團隊不僅僅包括編程隊伍,營銷、支持、編程、設計……所有團隊需要協力工作。通過定期匯報重寫進展情況來確保整個團隊步調一致。
在我們的案例中,我們遇到了很多這樣的問題。例如,營銷團隊準備產品測試活動時,他們必須準確了解產品方面的情況,以便讓客戶為即將到來的產品改變做好準備。但是,有時我們在沒有通知他們的情況下做了一些更改。這害得他們必須從頭開始準備他們的測試活動。記住:不要浪費任何人的時間。
不要對產品作重大更改。
了解你的產品的弱項和強項,這一點很重要。切記不要改變產品的強項,也即用戶喜愛的方面。如果用戶對用戶界面滿意,不要對用戶界面作大改動。只做最小的更改和小的用戶體驗改進。當您用重寫后的版本替換現有版本時,確保你的用戶不會被新的巨大變化所困擾。有許多情況用戶放棄了新版本,因為他們找不到以前版本提供的相同的功能。不要讓同樣的事情發生在你身上。
不要讓你的產品只依賴于一個開發者。
在我們的案例中,CTO是負責開發我們軟件的首席開發人員。由于他的立場,我們的產品開發進展緩慢。即使是很小的變化也需要幾個星期,有時甚至幾個月。我想表達的關鍵點是保持一直更新,永遠不要停止。
版本遷移/更換要循序漸進。
當您確認新版本已經準備好,開始用新版本替換舊版本時。要一步一步,循序漸進。
首先,從一個小型的內部測試組開始,將您的產品發送到該組。收集他們的反饋和崩潰報告,修復錯誤,迭代新版本,然后重復這個過程,直到你確認你的產品已經準備好公開測試。
進入公開測試后,用戶的反饋是你最期待的。你的第一個目標應該是確保您的產品能夠解決用戶的問題。當你確認新版本提供的功能與舊版本相同或者更好時,就可以進行更換了。這時候開始為新用戶發布新版本,并將現有用戶遷移到新版本。
以上這些都是我從代碼重寫過程中吸取的關鍵經驗教訓。代碼重寫幾乎永遠都不應該是解決方案,重構才是更好的選擇。強烈建議采用代碼重構循序漸進解決問題。這樣做的風險更低,客戶也更滿意。
什么時候重寫代碼是合適的選擇
然而,有時候重寫代碼也是合適的解決方案。下面我我列出了重寫代碼的幾種情形:
切換到另一種語言或平臺:
當一種語言變得如此古老,導致你很難找到開發人員,或者必須花大價錢才能找到時。
現有的代碼庫變得不可維護(像我們的情形):
如何確認你的代碼變得不可維護呢?這個很難,但是如果你發現即使是很小的更改也很難實現,或者新的更新比正常需要花費的時間多得多,或者任何新的更改都會影響到軟件的其他部分并導致新的錯誤,那么你可以確認你的代碼變得不可維護了。
有足夠的資源可以同時維護現有系統和設計新系統:
重寫代碼的時候,永遠不要停止維護當前正在使用的系統。只要系統在使用中,必須始終對其提供維護。記住,你的個人注意力也是一種必須考慮的資源,如果你打算同時為新系統和舊系統做設計工作,你要考慮是否每天有足夠的時間。
開發人員變成了軟件開發的瓶頸(像我們的情形):
這不應該出現在重寫代碼的原因列表中。因為你可以隨時在團隊中調配開發人員,也可以雇傭新的開發人員來解決瓶頸問題。
然而,就像我們的情形一樣,有時你可能需要將它作為代碼重寫的一個原因。因為我們的軟件使用的是舊技術,而CTO是唯一負責開發它的人。我們很難找到一個新的開發人員,因為這個平臺年代太久。即使我們能找到一個新人,對我們來說也太昂貴。因此。我還是把它作為代碼重寫的情形之一,列在這里。
軟件的年齡太長(我說的是10-20年或更長時間):
隨著時間的推移,一個軟件的代碼會變得越來越凌亂,維護也會變得越來越昂貴。這是因為為了快速推出修復補丁,初始架構有時會被犧牲掉。而且,懂得舊技術的開發人員越來越少,人員成本也越來越高。同時,很難找到適合舊的應用程序運行的硬件、操作系統和框架。此外,隨著業務的發展,舊的系統很可能無法滿足新的業務需求。
所以,你必須在舊系統高昂的維護成本,新系統的潛在好處,以及從頭重寫的成本之間作一個權衡。
如果你的情形符合上述一點或多點,代碼重寫可能是你能接受的選項。否則,正確的做法是通過一系列簡單的步驟改進系統的設計,在不重寫代碼的情況下處理解決現有系統的復雜性。
從頭重寫代碼可能是你犯的最大錯誤,但同樣地,不重寫代碼也可能導致相同的結果。我的建議是優先考慮重構而不是重寫。
有些開發人員堅信所有系統最終都必須重寫。記住這并非總是對的。設計一個不需要拋棄的系統是可能的。總有軟件設計師會告訴你,“無論如何,總有一天我們會丟掉所有的東西”。但是,如果軟件是從一開始就設計得很好,而且一直有很好的維護,為什么它會被拋棄呢?
-
軟件
+關注
關注
69文章
4781瀏覽量
87163 -
代碼
+關注
關注
30文章
4751瀏覽量
68357 -
程序員
+關注
關注
4文章
950瀏覽量
29763
原文標題:十年程序員的告誡:千萬不要重寫代碼!
文章出處:【微信號:rgznai100,微信公眾號:rgznai100】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論