19、一個人的戰(zhàn)斗
今天早上,有個網友給我發(fā)了一條消息:他是一個老產品版本維護開發(fā)人員。他應聘到這家公司的時候,這個產品已經賣了4年了。最初的開發(fā)者已經都在這4年中不斷流失走掉了。他來了,任務就是維護這套軟件,而且就他這一個人維護這套代碼,有BUG改BUG,有需求就改需求。
雖說這套軟件賣了4年,但真不知道是怎么堅持了4年。他接手的時候仍然是BUG百出。代碼沒有文檔,沒有注釋,連表結構說明都沒有。代碼莫名其妙,經常橫插一句代碼,顯然是客戶報告了某個錯誤,為了臨時解決這個錯誤而做的針對性處理,但到底是為了修補什么錯誤,代碼也沒有說明。所以他也不敢亂動,但還要修改需求,只能硬著頭皮來。他也不知道自己修改的代碼是否還會引起其他的問題,只能憑空企求千萬不要出問題。老板還在到處吹產品很成熟,而他每天都在心驚膽戰(zhàn),害怕這套代碼不知道哪天突然崩潰,出了錯誤自己都收拾不了,那只能自己被K掉。他能想像的出老板發(fā)怒的情景:這么穩(wěn)定的產品你都搞不定。
他希望我能幫助他出出點子。
我想了想,能有機會開發(fā)新產品或新項目的程序員是很幸運的,因為沒有歷史包袱,白紙畫畫。而現在大部分的軟件公司都是拿一套已經有了型的代碼到處修改客戶化做新項目。真正做一個新項目從頭編寫這個項目的第一行代碼,這樣的機會比較少。
對于修改現有代碼適應新客戶新項目,這種情況非常多,也是大部分沒有文檔,修改定制沒有注釋。客戶打電話說了一個需求,能技術達到就答應下來修改,修改完就給客戶覆蓋,根本沒有需求管理、版本管理。而這樣的代碼,還不是一個特定客戶一套特定定制化代碼,是要給其他客戶也更新的。很可能這個客戶好使,那個客戶使用其他功能的時候就出了錯。按下葫蘆起了瓢,是很常見的現象。
我問他:現在你改的代碼有注釋嗎?
他回答:沒有。自己修改的自己都記得,即使忘了,看看自己寫的代碼也能回憶起來。所以也沒有寫。
我問他:那以后他走了,其他人怎么辦?
他回答:反正也是一個爛產品,其他人怎么辦,他就管不了了,就應該讓這套爛代碼盡快死亡,省得禍害別人。
我問他:那你找我?guī)椭哪康氖鞘裁矗?/span>
他回答:在我工作的這段期間內,它不要崩潰就可以了。
我無語了。
關于老系統(tǒng)維護這個話題,我和許多開發(fā)人員都有過深入的交流探討。
許多從事開發(fā)的網友認為,一個老系統(tǒng)要維護好,必須具備以下關鍵因素:有責任心、有文檔、設計前做好詳細的需求分析、要有需求管理、要OO編程、要有專門的測試人員。如果沒有這些,干脆推倒重來,如果不讓推倒重來,那就趕快跑路,否則就容易當了冤枉替死鬼。
但現實中,往往維護老系統(tǒng)的就一個人。這是很矛盾的事情。一個軟件的開發(fā),往往1-2月就完成,而它的銷售、實施、升級周期卻長達4-8年。但每個老板好像都認為軟件已經開發(fā)完畢,修修補補都是小功能,所以一個老系統(tǒng)維護人員就OK了。殊不知白紙好畫畫,而要在別人的畫兒再能點睛成龍就難上加難了。
我在管理運營企業(yè)的時候,發(fā)現遇到的難題也和維護老系統(tǒng)面臨的很類似。都是缺這缺那,部門之間利益沖突,人的素質怎么也提不上來,員工和老板互相做貓和老鼠的游戲,不斷博弈薪水和付出勞動力的平衡。總有些公司的歷史留下的人留下的勢力格局留下的客戶印象留下的做事方法不能改變,也無法推翻重來,但公司還要發(fā)展還要提高,就必須以目標為中心,不斷象駱駝一樣挺著風沙干渴饑餓領隊前進,有各種困難阻礙都要不斷清除,無法根除就想辦法平衡與緩解,時而讓步時而迂回時而強勢時而突然決策突然執(zhí)行,公司就這樣不斷持續(xù)經營下去。
所以,維護老系統(tǒng),也要象經營企業(yè)一樣,不斷繼承包袱,不斷細心剖析問題,剝繭抽絲理清思路,不斷改進,才能漸漸從惡性循環(huán)走向良性循環(huán),才能把一套爛代碼扭轉成可持續(xù)維護的代碼。
我給了他寫代碼的八個建議:
一、重點把控輸入數據的校驗。你看見很多橫插進來的代碼,就是由于輸入的漏洞進入,最后引起后續(xù)數據處理出錯,所以以前的程序員他不截源頭,他在最后爆發(fā)的地方堵漏洞。現在WINDOWS程序都是消息事件觸發(fā)式的,還說不準這個流程會走到哪里,他堵得了這個口,其他根本想不到的觸發(fā),他能堵住嗎?所以,把輸入數據的校驗,在保存按鈕第一步代碼寫好集中的詳細的校驗。而且,這塊代碼要寫成函數,不要大流水,省得代碼復雜性會讓程序加速崩潰。
二、以后的需求再往上加,都寫成函數。遇到比較大的IF..ELSE判斷,就把其中的代碼段再分出一個函數。
三、以后再加功能,盡量不要做成聯(lián)動觸發(fā)的。也就是說:保存,最好是單表保存。即使是主從結構的單據,如果客戶不強烈反對,也做成先保存主表后再讓錄入明細表。而且錄入明細表要單獨的窗口,這樣功能和代碼都簡化了。如查詢一張單據,也不要上邊是主摘要,下面就是明細聯(lián)動。這樣影響性能。更因為速度可能慢,用戶會連續(xù)點擊多次,觸發(fā)事件就會亂,莫名其妙的錯誤就都產生了。最好是雙擊主摘要,彈出獨立的窗口顯示明細。
四、你以后寫代碼,把特殊處理業(yè)務和正常處理業(yè)務的功能代碼分離。就好像你走路,老有人給你下絆子,你就感覺不爽。
五、現有的功能,把不常用的功能做一些隱藏處理,放到一個不起眼的位置。使用的人就會越來越少。到時候就適機真正藏掉,不讓它觸發(fā)了。
六、其實很多時候,你覺得程序很爛,索性破罐子破摔,是由于以前程序員的代碼排版可能和你不一樣。你喜歡兩個空格,人家喜歡三個空格,你就覺得不爽。人家喜歡把{放在最后,你喜歡新開一行。你可以使用代碼格式化工具重新排一次版。我看到很多關于老代碼維護人員,抱怨變量都是M、N、S、Button1之類,但其實你閱讀理解代碼,這些并不會使你理解有歧義或讀不懂,只不過你不爽而已。理解了這個不爽,你就會心平氣和一些,修改代碼會更加順利一些,你越和舊有代碼生氣,你的工作越亂。(看到這里,相信很多程序員都會會心一笑。真正的根源在于此,老系統(tǒng)無法維護只是借口而已,可能希望老板認為自己的工作很辛苦很復雜而加薪)。真正危害大的是全局變量和大流水代碼。所以寫代碼,要嚴格避免這兩個壞因素。
七、修改需求或BUG的時候,要按照模塊來集中修改,而不要挑好改的先改了,不好改的就最后改。按照模塊來集中修改,你會通盤考慮所有這些需求和BUG,而不是糊窗戶式的補窟窿。
八、我曾經和很多做維護的開發(fā)人員都做過交流。他們都覺得一個軟件沒有文檔,沒有注釋,簡直就沒法維護。但確實是很多軟件沒有任何設計文檔,連幫助說明都沒有,代碼也沒有注釋。而這些軟件又出自他們自己之手。也就是說他們一邊抱怨沒有文檔沒有注釋,一邊自己也不做文檔不寫代碼注釋,不知道在等誰來專門做。我問他們到底需要什么文檔才可以將一個軟件維護的越來越好,從一套爛代碼扭轉到一套良好漸進的代碼?他們說要要表結構說明、要詳細功能設計書。表結構還好說,可以整理出來,詳細設計說明書就不容易出了。
我曾經也維護過別人的代碼,也是什么文檔都沒有,連操作使用幫助都沒有,更別提詳細設計說明書和表結構,代碼當然沒有什么注釋。我并沒有去整理表結構說明。幸虧這個人喜歡數據庫上倒弄,寫了大量的視圖和存儲過程。視圖中有各個表之間的關系連接,也有各個表中重要字段的中文名。這樣我就不需要表結構說明了。因為表結構說明不僅需要需要描述每個表中字段的中文含義,也得描述表之間的關系,這和視圖能表達的效果是一樣的。所以,我現在也建議開發(fā)人員寫代碼,多寫視圖,多寫存儲過程。有的老代碼,SQL語句都生寫在代碼中執(zhí)行,沒有視圖。對于這樣的老系統(tǒng)維護,就是把這些SQL COPY出來,做成視圖,這樣就好維護了。
對于詳細功能設計書,其實對于程序員來說,其目的是想弄清楚業(yè)務流程的來龍去脈細節(jié)。光直接看代碼是很難弄明白意思的,又沒有什么其他文檔可以參考,所以只能猜測代碼的意思。尤其很多維護人員,很多功能細節(jié)都是為了處理某些特殊需求和異常業(yè)務的,都是以前的程序員寫的,但是以前的程序員已經走了,現在的維護人員連軟件中具體的這些細節(jié)功能都不知道。當新的實施人員或支持人員反饋回疑問,想問問程序員某個細節(jié)功能是怎么回事,程序員都發(fā)蒙,嗯,還有這個功能?我也不知道呀。
要解決這個問題,我曾經做過的事情就是組織實施人員寫功能操作說明幫助。因為實施人員要給客戶去培訓講解,沒有幫助說明,只能一張嘴叭叭叭的干說,實施人員是最需要功能操作說明幫助的。但是實施人員認為這個幫助是軟件的一部份,而且是開發(fā)部開發(fā)的軟件,開發(fā)部最了解功能,所以幫助文檔應該開發(fā)部寫。而開發(fā)部認為開發(fā)部的職責就是編寫代碼,你自己培訓你連個操作說明都沒有,你怎么培訓,所以幫助文檔應該實施部門自己編寫。于是幫助文檔誰也沒有人寫。
歸根到底,幫助說明是終究要寫的,主要是誰寫的問題。誰最有動力寫呢?實施人員最有動力,因為這和他們的工作息息相關。而程序員明顯沒有動力理由。而且實施人員熟悉第一線客戶的素質,理解客戶的具體操作思路和理解思路,寫出來的幫助客戶都能理解,幫助文件才能真正為客戶服務。很多幫助文檔的寫作都是從來沒有見過客戶沒有實施培訓過沒有客戶支持服務過,連軟件測試都沒有做過的純粹文檔人員編寫的,可想而知幫助文檔到底能對客戶有多大的幫助性。
在寫幫助說明的時候,我要求實施人員把每個按鈕都要點到,每個Grid中的每一個字段的數據來源和數據含義都要說明到,每一張報表中的字段的數據來源和數據含義,每一個明細錄入中的字段的數據來源、數據錄入要求和數據含義。這一寫不要緊,發(fā)現了很多隱藏的特殊處理功能。很多功能很多人不了解,因為很多細節(jié)功能,都是為某個客戶定制的,只有負責實施該家客戶的實施人員才知道。于是,實施人員之間互相通氣,才算補足了不少功能細節(jié)的幫助說明。實在有些功能,都不知道是哪家客戶提出來需求,也不知道為什么要這樣處理,就留下空白,轉給開發(fā)人員,讓開發(fā)人員看看代碼是怎么處理的。就這樣,一份詳細的幫助說明在壓力艱難中終于出來了。從此,開發(fā)人員理解需求快了許多,當然也就明白了那些過去自認為亂七八糟的代碼的含義,心情好了很多,修改代碼也輕松了許多。原來,一切都是自己跟自己作怪。不盼望軟件工程,不抱怨一窮二白,不幻想增加人手,從現實入手解決自己的問題,發(fā)現很多解決方法既簡單又有效,根本無須動輒就是團隊就是UML就是OO。
另外,我還給了他一些關于需求控制的建議。
需求,是很多方面的。有關于功能的(尤其是每家客戶特殊的業(yè)務需求),有關于異常錯誤的、有關于性能的、有關于兼容性的、有關于易用性的、有關于特殊權限的、有關于美觀性的。
而需求的來源也是很多方面的,有的是客戶計算機室直接打電話,有的是客戶業(yè)務部門直接打電話,有的是實施人員,有的是支持人員,有的是市場人員,有的是銷售人員,有的是老板和客戶打單或開會突然想到談到就直接給開發(fā)人員打電話。
而需求的優(yōu)先級也不一樣。有的客戶態(tài)度強硬,你必須盡快滿足他,否則他就給你老板打電話。
而正是這來自四面八方的各種層次各種看法的人的各個方面的需求電話,把程序員就煩的要命,還要去開發(fā)。而且很多都是一個電話就認為程序員能開發(fā)了。但往往程序員開發(fā)完后,客戶一看不是自己最想要的,于是再修改。
所以,需求多,其實是一個幻覺。
第一、把需求分類,做個EXCEL表格,量化解決。這個需求管理表格會有下列這些項:客戶名稱、需求提出人、提出日期、需求關閉時間、功能模塊名、客戶現在版本號、需求描述、需求分類(需求、BUG)。我在最初沒有需求管理系統(tǒng)的時候就使用過這種方法。過去沒有使用的時候,我的手下老叫忙死了煩死了。我就讓他把現在手頭的事情都整理一下給我報個郵件。但一整理,肯定不超過10件事。有些事情是等待客戶給資料,有些事情是調試跟蹤不出來錯誤,有些事情是需求模棱兩可。我給他一分析,他現在正在進行的事情就兩件,而且都是他自己能獨立做的,根本不需要別人配合參與的。他忙嗎?他瞎忙,或者故意說忙。沒有工作效果,就是這樣。帳不算不清,話不說不明,就這個道理。
有了這個表格,要定期(可能是一周,可能是一月)給老板一份。這表明你的工作量,讓他看看你確實一直很辛苦的在工作,而且干了這么多活。而且,這也能看出你工作的仔細負責態(tài)度。
有些程序員不做這個表格,也不給老板報告。很多時候是程序員并沒有干那么多活,能推則推,能混則混,能拖就拖。怕自己有一天混不下去,所以心理壓力很大,每天不干活卻總覺得很累。這種累就是自找的。想必一些程序員看到此會想起自己。
第二、需求描述不清晰是反復修改的罪魁禍首。對于BUG,要有錯誤報錯整個的屏幕截圖,千萬不要就截那個錯誤消息框那么一小塊。對于需求,是報表需求,要給出表格格式,還有每一項數據的來源,有公式關系的要給出明確的計算公式。對于輸入單據需求,要給出單據格式,每一個輸入項的要求:可選值、默認值、不可為空、唯一性、約束輸入,數字要有小數點后精確度、日期要分辨精度是到日期還是時還是到分。
對于這位網友的現狀,我還建議他開始版本管理。
、VSS之類就不必了。因為這是一個人的戰(zhàn)斗。連版本都沒有概念,一上來就是特正規(guī)的工具,大半感覺太麻煩,又退回到最初原始狀態(tài),還對版本管理產生了不好的印象,覺得繁瑣還沒什么用。所以我們有一句話:一管就死,一放就亂。其原因就是缺乏一個中間過渡解決方法。
所以,我建議先把版本意識提上來,按照版本管理的方法走,走順了就自然接受了正規(guī)的版本管理工具。版本管理工具可以分支,也可以合并,可以針對Bug進行補丁發(fā)布,而不發(fā)布還未完成的新功能,可以發(fā)布為某個客戶專門定制的版本,也可以回溯歷史版本,對比歷史差異,源代碼安全性也高。
有幾個過渡性建議特別實用:
一、有大的修改或沒有把握的修改之前,先把代碼備份到其他的機器上。備份目錄要跟上日期。
二、在大的修改前,先定一個穩(wěn)定的版本發(fā)布出去。很多程序員沒有版本這一概念,每天都在持續(xù)修改。結果,給客戶的,每個都是半成品,有半拉子沒有修改完,還自己沒有做屏蔽處理。客戶不小心用了,產生了錯誤,再告知千萬不能用這個功能,還沒有完善。但晚了,錯誤數據進入了,以后報表平帳就是問題了,又得特殊數據特殊處理了。自己的孽障自己還得解決。
三、即使是瑣碎的修改,也要每天或隔天備份一份源代碼,別怕代碼多,現在的硬盤大的很,而且備份復制一下也就是5分鐘的事情。別怕每天備份太煩。我們經常會遇到這個客戶讓改了,另外客戶不讓改。一個功能改了又改回去,但過去的源代碼沒存?zhèn)浞荩嗽趺磳懥耍@時候你就想起代碼備份的好處了。尤其現在有不少免費的文件同步或文件自動備份的軟件,都能定時做。功能還強大,有些還有些差異備份的功能
四、現在有不少文本對比軟件,如WinMerge之類。可以對比兩個文件的差異,這個功能和版本管理工具中的源代碼差異對比一樣的效果。
五、如果每次發(fā)布新版本,就把從上一版本發(fā)布之日之后的關閉的需求列表都單獨摘成一個文件,附帶到這次新發(fā)布的版本之后。這樣即使沒有人寫更新說明文檔,根據追溯也能明白這次版本解決了哪些問題和需求。很多程序員沒有需求管理表格,版本發(fā)布要求寫更新說明文檔,這才從腦海記憶中想,想的就有些遺漏,甚至錯誤。好多程序員有過這些的情景:我記得改了呀。真正一翻代碼,一點沒動。大叫:我的代碼怎么沒了,我記得我改了呀。
我這些建議,從需求描述、工作量管理、遺留系統(tǒng)代碼重構技巧、備份管理、版本管理、更新說明文檔一整套說明了一個人如何維護老系統(tǒng)的工作方法,但希望能分享給大家,給大家以幫助。
有方法,你就不是一個人在戰(zhàn)斗。
一切皆有可能。
相信自己。