iT邦幫忙

13

重談「佛祖流」敏捷軟體開發

這是我在傳說中的「佛祖」底下接受他的「五雷轟頂」快變成渣之後……
這幾年來匯聚的心得。
不管其中是正論、謬論……
都感謝你的閱讀及指教。
其實我要講的是,佛祖流的敏捷開法他所參考的書籍並不悖離許多程式設計原則、架構。
只不過佛祖流強調、很強調、非常的強調「單元測試」。
不管你是想要再做整合測試、系統測試、回歸測試。
就以開發的根本,他不離「單元測試」也是唯一你不可以有任何理由去背棄的做法。

「佛祖」是誰?這是我給他起的一個外號。部份IT業界的人都聽我談論過這個人。
不過因為他過去為人和說話實在太「異類」。所以反而在台灣的IT業界就顯得沒沒無名。
也許你跟他吵過。也許你跟他請教過。也許你對他不屑一顧。
然而他的的確確創造了很多軟體層面的價值。

佛祖說過一句話:你知道嗎?我開發一個軟體專案有跟客戶簽約的,只要專案delay了,
我就是賠償客戶20倍。但是我開公司這麼多年從來沒有賠過任何一毛錢。

聽起來很屌!可能很讓你覺得「Are you kidding?」
或者讓你覺得「你這人亂講話,根本就天方夜談。」

我想講的是。沒幾個人認真看過這個人的工作態度和做事態度。
我不敢說會不會有第二個人跟佛祖一樣,對自己寫的每個功能都交付一份測試code。做為他的測試報告。
至少在我看過的工程師中除了佛祖。還沒有第二個人做到。
或許你做到了。只不過是我不認識你罷了。
至少,在寫這文章的同時,我也在檢討。
佛祖教了我那麼多,那為什麼我自己還是沒有很確實的去做到這件事?

很可惜的。近幾年來佛祖因為自己身體的因素。(據他說述,第一次生大病是差不出病因
,莫名奇妙人在床上躺了二個月。那次正好是他為了收拾別人的爛攤子不得已在一個月內把
別人要做六個月的專案做完所以才住院。第二次則是腦部的病變,幸運的是現在人又健康了
。但是他選擇了退休……比我還年輕啊。)

我在佛祖那邊待過一陣子,後來離開了。
離開之後有跟他談論現在很多關於敏捷技術的問題。
似乎我們看到很多很多的敏捷都是把重心放在:這個語言帶來的特性讓你寫的code乾淨又很快。
我跟他提及現在很多年輕工程師都很喜歡很潮、很屌的設計。
佛祖只是冷冷的回我:所以測試code呢?沒有測試你跟我說他多好多棒多快的意義是?

佛祖並沒有說什麼叫做「潮」。
但是我想,對他來說。寫一個專案,交付軟體同時交付測試報告。來好好驗證他的軟體品質是高的,穩定
的。這就是他的「潮」。

我問過佛祖,有人覺得我的code跳來跳去,寫得很難看。
有一次,佛祖就叫我開遠端,讓他看看我實做code的方式和風格。
我記得他跟我說了:你寫的code我看起來很舒服啊!教你的方法都有做到。
但我跟他說有人就覺得我寫的這方法很難看,跳來跳去,不知所云。
他笑笑說:你管別人怎麼看幹嘛?別人看不懂代表他能力還沒提升上來罷了。

這我想想也是,很糟的code,很亂七八糟的code。
以前也寫過,可是從來沒聽他抱怨說他看不懂。
更有甚著有時我自己一下也有點亂了套時。
他並沒有一開始跟我一起寫,但他馬上一眼就看出我的問題點。

佛祖是對我說:你管別人怎麼看你的code幹嘛?他們覺得他們自己很厲害的話就隨他們去啊。但是沒有單元測試
。沒有測試報告,甚至連測試驅動都沒做,你別來跟我說他很厲害。那只是個假象。

最近這二、三年,我也看到一些反對做TDD的言論。
大致上都提出需求要明確,系統要事先歸劃好。
可是佛祖流告訴我的事:我不管你有沒有做好需求或是規劃好什麼事。
重點是你要寫的是一個功能,那你就是要對這個功能做測試。
你要讓這個功能,跟他一系列的功能都能確保測試無誤。
重點是我告訴過你的:測試寫完時。功能也就開發完了。
那你為什麼不先寫測試?

我跟佛祖爭論過。
我說現在有一種意見就是說,先行測試再開發軟體,會讓開發時間拉長。
佛祖問了我一句:所以你寫完功能就寫好了?
我說:怎麼可能有這種事?寫完當然要看他跑的結果啊。
佛祖說:那你看過的一般寫php程式的怎麼看?
我說:他們一般不是echo、var_dump就是print_r。
佛祖說:然後呢?想起我教過你的東西嗎?

是啊!寫code + 測試,而一般不清楚的人,就是把一個結果在程式碼中印出來。
好一點的,是直接把function拉進主程式去看結果。
可是,誰來驗證這個function是正確無誤的?
再來,寫一個功能,本來就是要去確保他的正確性。
但是一般人都是以使用上的輸出結果來認定他是正確與否的。
可是這中間的過程中誰來確保他是正確無誤的?
也許只是丟了某個特定參數值恰巧他產生的結果是對的。
但是你能確定你丟了另一個參數值他的回應還是正確的嗎?

再推論想一想,在你思考先寫測試「再寫」code會拉長開發時間。
可是你有沒有想過你先寫code也是要「測試」的不是嗎?
差別只是在於「先做還是後做。」
然後最重要的一件事情是,當你做測試先行,來讓你的功能產生出符合測試的結果後。
你倒是告訴我:你接下來是要寫code嗎?

回頭看一下吧!測試這個function的功能是不是符合我的需求。
如果符合了,就開始寫code…………???
不對啊!如果這個function的測試結果是符合我的需求了。
還要寫什麼code?這個function不就可以直接用了?

沒錯!或許我們另外寫了一個測試案例來測試我們的function或是其他功能。
但是一但這個function通過了這個測試案例,我們還需要再寫其他的code嗎?
更良善的一點。測試輸出是在測試案例中進行的而不是在你原本的功能中進行的。
所以這當中完全沒有可以去破壞你原本程式碼的任何因素。
包括很多人習慣的將值輸出後又把輸出值的那一行不是註解掉就是刪除掉。
然後就不知道有沒有人意識到那個被註解或是被刪除掉的部份。
就是你可以拿來做測試案例最佳的範本。

上面很清楚明白的指出了測試的重要性之後。

再來佛祖就會補上一擊………

這一件事,也是佛祖流敏捷開發的第二個核心價值 - 物件導向

關於這件事,我跟佛祖學到很多很多。
佛祖曾經出給我一個作業:每天寫一個class,我不管你寫什麼都好。
剛開始七天,我很耐心的一天寫了一個。
但是接下來一個月,我可能三天才寫一個。
到最後……只有要做功能需求時我才寫。

很明顯這是懶下來了。

佛祖跟我說:你知道不知道物件導向的五個基本原則?
ok!這句話我一直到最近才終於聽懂。
因為我一直都聽成:寫程式的5個基本原則。
物件導向的五個原則:SOLID!
S:單一職責原則(函式提取,單一職責,一個函式只寫一個功能,不要去過度複雜他)
O:開放封閉原則(對擴充要開放,但是對修改要封閉。)
L:立次克夫替換原則
I:介面隔離原則(佛祖把他講成了低耦合性原則,也許是相同的意思)
D:依賴倒轉原則

佛祖說:這五個原則要是你一直記不住沒關係,但是你還是給我乖乖的寫物件,寫測試。
寫久了,多寫個幾年了,你就會明白我說的。

在我接觸程式這麼多年,我也遇過很多很多不寫物件導向的各種理由。
可是為什麼我還是覺得他很美好?
最重大的依據還是……測試起來至少很美好。起碼你用物件寫,你會清楚知道你在做什
麼事情。

不過佛祖最後還是會很用力補上一擊:講那麼多,測試code寫了嗎?

老實說,測試code到底在寫什麼?
其實單元測試的核心價值,就是在驗證你的功能是不是輸出符合你所期待的結果?
最主要是用來解決你邏輯上的盲點。
更甚著……也許你解不掉的bug就正出在這種邏輯上的盲點。

我曾經在寫一個功能,不過這已經好幾年前的事,寫什麼我其實沒什麼印象。
我只知道當時快要交件了。我的邏輯問題一直存在。
誠如前述,一但一件事你沒有習慣,你就會懶的去用他。
所以我很信懶的腦袋的想法,我很確信我做的方向一定是正確的。

於是那個bug就存在了整整二天。
我二天的時間不管用什麼方式去驗證,他的輸出結果一直都是錯的。

在我不得已去向佛祖求助時,他只回了我一句:寫測試。
我記得我講了很多我認為是正確的理由。
但是佛祖到最後仍然只給了我唯一的建議:寫測試。
我說我東西都寫好了,現在寫測試還要拆開來,還要再分析各個功能很浪費時間。

是的!我在浪費時間。我浪費時間在跟佛祖爭論我的程式應該是完全無缺的。
但是……那個bug並不會因為我在和佛祖爭論而自動消失掉。

我摸摸鼻子回到坐位上,再努力了一個小時,這時已經接近下班了。
也許……我今天該加班了。
我嘆了一口氣……發揮我最快也最擅長的速度:打字!
很快的把我寫的一整個函式全拆開了各自獨立。
然後開始寫測試。讓我的每一個功能都能通過。
而我確信他們應該都是完美的通過。

但是測試出來的結果讓我很震驚!這證明了我是錯的。
當中的其中一個測試程序不管我如何測他回傳的結果就是faild。
但是,這個程序在拆解到這個階段時這個function的code其實已經剩不到四、五行。
於是我發現了一件事:正因為我最擅長打字,所以我打錯了運算方式。

重點是,在我拆解function到我寫好測試並確認問題的時間……
只有短短的20分鐘。

我不願意寫程式搞了二天的bug加上和佛祖爭論的一個小時……
就只是在我寫個測試後卻在短短的20分鐘把那個bug處理掉了。

很幸運的,我今天不用加班。佛祖沒對我說什麼………

文章到這邊。我不確定各位意識到了沒?
什麼叫做「敏捷開發」???

敏捷開發的要旨絕對不是一套語言能讓你寫code寫多快。
或是他本身多乾淨多好用。
這都只是敏捷表象。
講難聽一點:我打字打很快也可以說是敏捷開發啊。
(沒有多快啦。我中文打字無蝦米大概一分鐘120個字以上吧。)

敏捷開發的真正意涵,應該是去了解我們究竟是把時間浪費在什麼地方了?
最重要的。你有沒有打算對你的軟體未來負責?還是你只打算負責這一次?

這也就探討到軟體的未來:功能性的擴充、版本的更新、需求的變化。
今天如果有人跟你說,寫一個軟體就是要快速應付這一次,創業創新做到一次就好。
那我只能說:你就祈禱你只做這一次然後一次賺完你的人生就恭喜你了。

就一個軟體的存在來說,他幾乎不可能會是長年不變的。
你會增加功能、客戶會要求新的需求、或者是軟體已經不符現在的要求而需要改變。
於是我們打算怎麼做?重新開發?亦或是修改軟體讓他符合需求?

重新開發或許是一個好主意,因為他沒有任何舊的包袱……
但是如果你重新開發的東西仍然是屬於原本專案的功能之一,你不能免除他和原本
的程式會有一定程度的連接。

那如果不是開發新功能而是擴充原本的功能呢?

其實這往往就是工程師們會開始訐譙前人或是訐譙自己以前的code寫得很爛的時候。
為了擴充而譙、為了修改而譙。
甚至為了客戶莫名奇妙要加新功能而訐譙!譙他們當初需求不提現在才提。
譙現在要把原本的code拿出來改是多困難的一件事………

可是或許就沒有人會譙:為什麼沒有人寫測試案例?

想想看,新的功能是個擴充,這或許很好解決。
畢竟他不一定依付原本的功能。
但是如果他能直接使用原本的模組來提供功能當然更好。
可是有時會發生的問題是:原本的功能大致ok但有部份的功能需求不符。

我想,物件導向的設計原則:開放封閉原則。
是一個很好的解決方法。
這邊我不談論怎麼去實作開放封閉原則這件事。
而是你按照這個原則架構去重新實作你的新功能時。
你有沒有一個好的測試案例能確保你原本的功能和新的功能都是正常運作的。

有一個很好的案例。
以前我寫的某個功能,已經寫好放在那邊了。
結果因為要應付公司的新功能。
另一個工程師看到我寫好的東西。
他不過問我,直接在我的function加上了if…else…
然後依據他新開的參數來決定結果。

我相信了解程式設計的人會明白這是多糟糕的情況。
因為這已經改變了我的函式原本輸出的可能性結果。
事實上,也出了狀況。
他的功能對他來說是完美的執行了。
而我的功能呢?

壞掉了………

很不幸的。偏偏我就是懶的忘了要做單元測試。
而那時怎麼解決這個爭議?
我只能耐著性子,乖乖的重新寫一份測試code。
讓他能夠去對應二個不同的輸出結果。

然而事實上以當時來說。
假如他能夠不要用直接修改我的code的方式。
而是以繼承的方式來修改那個功能。
雖然不見得是一個很完全的擴充方案。
至少會是一個B不會影響到A的做法。
然而不管是那種方法,都不能脫離測試這件事。

我們不一定要擅長去理解人家的code。
但至少先學習不要破壞人家寫好的東西。
不管他寫的東西是好是壞。
那個功能基本上是正常運作的話,就讓他好好的運作。
不符需求需要改變,有更多的方式可以來處理。
假如真的覺得那個code就是要改:就應該交給開發的那個人處理。
假如他不在了,當先思考有沒有不修改他的code就可以解決的方法。
真的迫不得已非改不可……………
你就必須去寫對這個功能的測試,以確保最後的結果不會出差錯。

...

對應到所謂的測試,其實接著就是敏捷軟體中很重要的原則。
照理說他應該要放在測試前面來說。
但是因為佛祖一直測試測試測試的唸~~~~~~
變的測試超級重要而先談了測試這件事。

接著就是跟軟體開發有關的幾件事。
第一個要務就是「重構」。

最初佛祖在跟我談重構這件事情時。
他只給了我一個最基本的工作:看到重覆的程式碼你就是提取出來。
是的!這又回歸到了物件導向的單一職責。

然而在我一開始寫php的時候………
我該很慶幸那台電腦已經壞掉了所以那段見不得人的code就這樣封存在硬碟不會再打開了。

初學程式的毛病真的超級多的,而我自己的又多到不可思議。
因為我很喜歡拿一堆錯誤去撞牆來證明我的錯的。
然後佛祖後來知道大概有很長一段時間把我當笨蛋吧。

最大的問題就是佛祖當時看了我的code………
他問我一堆重覆的程式碼是怎麼回事?
我說:書上就這樣那樣寫,我就這樣那樣做………

我不會忘記當時常常聽到佛祖的口頭禪之一:你在亂搞嘛你!

那個時候我還沒在佛祖手底下工作過。
他倒是很耐心的跟我談了很多重構的事情。
事實上……重構這個議題也出過好幾本書了。
像是「重構 - 改善即有程式的設計」、「重構 - 向範式前進」。
不過因為內容都是以java語言為介紹,那個時候我很天真的就是除了php一概都說看不懂。
所以翻一翻也沒認真翻。

佛祖倒是用很簡單的話語去解釋了重構:
「在不改變程式功能或是表面產生的結果下,重新架構可以測試可以維護的程式碼」

ok!我是不完全知道重構本身談論的基礎到底是如何的改良程式碼的結構。
但是佛祖提到了二個最重要的重點:
(1)重構後的程式碼具有可測試性。
(2)重構後的程式碼具有可維護性(開放封閉原則)

只有可以測試可以維護的程式碼,他才有談論敏捷軟體的空間。
一份不知如何測試甚至難以維護的程式碼。
我們就沒有討論他能加速你軟體開發的空間。

然後話說回來~~~~~~
如果寫code是採取測試先行的話。
你基本上要寫出一個難以維護的程式碼這難度也很高了。

不過畢竟所謂的測試,只是測試你的功能性的結果。
程式碼本身是不是優良,有沒有多餘的動作或處理。
甚至是一些語言對於記憶體和效能方面的影響。
這個就不一定是測試能做到的。
(當然你也可以做效能測試來看看你的功能跑出來所消耗的時間,現行的一些軟體框架在測試
這件事也都提供了良好的效能測試。)
所以我們會需要重新架構程式碼。
這時才是朝向所謂「簡潔、明瞭、效能好」的手段。

每個程式設計師的能力都有所不同,對於重構的能力自然也會有所不同。
所以不要在一開始就要求效能會有多好。
但一開始就一定要求測試要做好。
輸出的結果必須是正確的。
效能的部份可以透過後續的重構來改進並學習。
除非是一開始就遇到較嚴重的整體效能出了問題。
那就必須立刻探討是不是要重構程式碼來獲取效能的良好改善空間。

...
最後。後我們要談什麼事情?

  • 難以達成的極致編程:因為工程師都是自私的。

佛祖流的敏捷軟體原則其實本身的核心要件也有五項:
.重構
.物件導向設計模式
.單元測試
.測試驅動開發
.極致編程

我上面的文章大致就談了前四件事。
當然我沒談物件導向設計模式。那是個必要但就算是我也還不是完全熟悉的部份。
僅管這幾年我還是極力的花時間練習學習,也告訴很多初學物件的朋友慢慢的去熟悉這件事。
也當然的部份理解到的做法對我在開發軟體上著實帶來了好處。
不過這畢竟還是軟體功力隨各人造化。自己要花時間去研究就是了。

這邊最麻煩的大概就是我寫的「難以達成的極致編程」。
有關於「極限軟體編程」這也是出過不多也不少的書籍在探討的。

極限軟體的基本原則其實是告訴我們要去順應軟體的變化。
在軟體專案中變化是常見的。我們必須去接受他,而不是安逸的認為他不會有經常性的變化。

最早其實有一本極限軟體編程,可惜他絕版了。
佛祖對於實踐這本書的做法我覺得相當有一套。
可是……卻沒有第二個工程師能跟他好好配合。
這包括我在內。

那本書我其實沒有翻得很透徹。
而當中有一件事是佛祖一直以來都很想做。
可是他從來沒有能夠成功實行的。

為什麼無法成功?因為工程師都是自私的 - 尤其當你想要霸佔他的鍵盤跟滑鼠時。

在極限軟體編程中,有一環就是搭擋編程。
書上有提到共同軟體開發。甚至要求到了:二個人,一個人拿滑鼠,一個人拿鍵盤,共同來開發一份軟體。

我相信,這個做法………
每當提到這件事時,得到的回應是:這是在浪費人事成本。把二個人力用去做一件事,那薪水是要一人一半嗎?

也就這樣,就我自己看到的朋友、或是軟體業的朋友,我沒有看過有人這樣做。
我唯一被要求過這樣的合作模式也只有在佛祖手下工作時有過。

.當你用人力來做為計算軟體開發成本時,你就要有工程師常常加班更浪費你人事支出的心理準備.

把上述所有講到的測試、重構……等等觀念結合起來後。
我們可以想像有二個工程師差不多就具備了這樣的水準。

然後你讓他們各自要做各自的功能。

他們得花時間討論功能怎麼串接,怎麼完成需求,怎麼寫測試。
甚至於得自己去看打錯字的bug再來用測試去解這個bug。

是的!我們有測試,的確加快了軟體的腳步。

但是……測試也的有測試遺漏。
那絕對是個浪費時間的事,僅管因為有了測試他能浪費的時間是有限。

但是二個人共同開發的模式其實是一個人負責於測試coding的作業。
一個人協助從旁提供意見和發現錯誤。
二相的配合有時節省的時間或許是1/5、1/4、1/3……甚至可能是倍乘。
假如二個工程師分開處理一個案子需要六個月。
因為你還要測試完後串接功能,再做整合性測試,當中有什麼錯誤可能二個人其中一個要先做修改功能。
僅管你就是因為有了單元測試所以還是能省下一些時間。
而二個人協同作業來處理一個案子反而只要花三個月的時間。
因為一開始二個人就清楚知道功能要怎麼做了。如何協力完成。
那麼同樣六個月就可以做二個案子了。

在昨天講完angularJS的專題後。
在閒聊之餘我跟老鷹、費公他們談及了軟體測試的一些事情。
於是仔細想想佛祖指導過我的一些心得或是心路歷程。
我絕對不敢說他是正確的道路。
他只是我相信而去走的道路。
即使還有太多部份我雖然記著但還沒能完全實踐。

當然佛祖的真身我想沒有他本人的意見和同意我就不提了。
但這個人絕對是我看過一個具有高度價值的軟體工程師。
可惜他已經退休不再教導後進,實屬可惜。

有朝一日如果能勸進,再看他願不願意重出江湖了。


0
raytracy
iT邦大神 1 級 ‧ 2015-05-31 22:19:49

我想, 接下來的問題應該就是: 「測試要怎麼寫?」

現在可能大部分在寫程式的人, 都沒有寫過測試程式, 所以要他們寫, 是完全沒有頭緒....

看更多先前的回應...收起先前的回應...

大神出神了………快拜!

打錯字了!
是「大神出現了」………出神就糟了!

是顯靈 ^^

januslin提到:
顯靈

這個更慘....都變成靈了....汗

raytracy提到:
都變成靈了....

背後靈驚

不要嚇我啊!

0
weiclin
iT邦高手 4 級 ‧ 2015-05-31 22:47:48

大大很幸運能夠遇到這樣一位良師
我自己也在實踐 TDD, 文章內的描述真的將測試的優點與困難都寫出來了
但我還沒想過可以將測試結果弄成測試報告, 感覺這作法值得學習

文中提到 debug 弄半天, 結果寫測試一下就抓到問題
這個情況我也親身經歷過, 那時真的有一種震撼的感覺
之後就決定要來提高測試的覆蓋率, 一直在學習怎樣撰寫可測試的程式
很可惜身邊有在研究這一塊的朋友實在太少了
而且彼此分享的內容往往都是推薦文章, 或是推薦書籍之類的
要實際拿自己工作上的東西出來跟別人分享那是有難度甚至可能違法的

而我這兩天看完 Google IO 一些內容, 其中一位講者在自己的網頁分享了可測性的的議題
他的作法是拿 Google 提供的 Android 範例, 來探討測試的困難點, 以及怎麼樣改進可測性
這給我一個想法, 我們應該可以利用類似的作法, 取一個範例當主題
然後大家各自為這個範例加上測試案例當作練習, 之後就能拿出來互相分享與討論

另外關於 pair programming, 文內主要提的是能減少錯誤節省時間
但是我認為它還能夠讓結隊的兩個人共享知識與共同成長, 是另一個值得推薦的點
可惜我還沒機會體驗, 打算將可測性研究到一個階段後, 再找人來試試看

希望大大能多分享相關的主題 讚

0
一尾
iT邦研究生 1 級 ‧ 2015-06-01 13:54:13

佛祖流

我知知道
格鬥遊戲裡還有一個叫「極限流」

亂完離場
逃跑

0
okra
iT邦研究生 3 級 ‧ 2015-06-05 14:55:33

「佛祖流」

「酒肉穿腸過,佛祖心中留,世人若學我,如同進魔道。」
疑惑偷笑

0
就是91
iT邦研究生 4 級 ‧ 2015-06-11 20:39:37

單元測試、自動測試、TDD、BDD、好的設計,其實沒這麼難的...

導入如果手法對,導入人員對了,也沒這麼不可能的...

佛祖,沒這麼遙遠的 :)

我要留言

立即登入留言